blob: 2c9e0eef1de88058f76a29a436a5670cb472c838 [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"
falkenad185092016-06-16 06:10:0228#include "content/public/common/origin_util.h"
kalman6f984ae2015-09-18 17:21:5829#include "content/public/common/page_type.h"
lazyboybd325ae2015-11-18 21:35:2630#include "content/public/test/background_sync_test_util.h"
annekao1db36fd2015-07-29 17:09:1631#include "content/public/test/browser_test_utils.h"
kalman6f984ae2015-09-18 17:21:5832#include "extensions/browser/extension_host.h"
lazyboyc3e763a2015-12-09 23:09:5833#include "extensions/browser/extension_registry.h"
kalman6f984ae2015-09-18 17:21:5834#include "extensions/browser/process_manager.h"
35#include "extensions/test/background_page_watcher.h"
annekao38685502015-07-14 17:46:3936#include "extensions/test/extension_test_message_listener.h"
falkenad185092016-06-16 06:10:0237#include "net/dns/mock_host_resolver.h"
horo1eeddde2015-11-19 05:59:2538#include "net/test/embedded_test_server/embedded_test_server.h"
annekao38685502015-07-14 17:46:3939
40namespace extensions {
41
kalman6f984ae2015-09-18 17:21:5842namespace {
43
44// Pass into ServiceWorkerTest::StartTestFromBackgroundPage to indicate that
45// registration is expected to succeed.
46std::string* const kExpectSuccess = nullptr;
47
48void DoNothingWithBool(bool b) {}
49
lazyboy22eddc712015-12-10 21:16:2650// Returns the newly added WebContents.
51content::WebContents* AddTab(Browser* browser, const GURL& url) {
52 int starting_tab_count = browser->tab_strip_model()->count();
53 ui_test_utils::NavigateToURLWithDisposition(
54 browser, url, NEW_FOREGROUND_TAB,
55 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
56 int tab_count = browser->tab_strip_model()->count();
57 EXPECT_EQ(starting_tab_count + 1, tab_count);
58 return browser->tab_strip_model()->GetActiveWebContents();
59}
60
61class WebContentsLoadStopObserver : content::WebContentsObserver {
62 public:
63 explicit WebContentsLoadStopObserver(content::WebContents* web_contents)
64 : content::WebContentsObserver(web_contents),
65 load_stop_observed_(false) {}
66
67 void WaitForLoadStop() {
68 if (load_stop_observed_)
69 return;
70 message_loop_runner_ = new content::MessageLoopRunner;
71 message_loop_runner_->Run();
72 }
73
74 private:
75 void DidStopLoading() override {
76 load_stop_observed_ = true;
77 if (message_loop_runner_)
78 message_loop_runner_->Quit();
79 }
80
81 bool load_stop_observed_;
82 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
83
84 DISALLOW_COPY_AND_ASSIGN(WebContentsLoadStopObserver);
85};
86
kalman6f984ae2015-09-18 17:21:5887} // namespace
88
annekao38685502015-07-14 17:46:3989class ServiceWorkerTest : public ExtensionApiTest {
90 public:
lazyboy20167c22016-05-18 00:59:3091 ServiceWorkerTest() : current_channel_(version_info::Channel::STABLE) {}
annekao38685502015-07-14 17:46:3992
93 ~ServiceWorkerTest() override {}
94
kalman6f984ae2015-09-18 17:21:5895 protected:
96 // Returns the ProcessManager for the test's profile.
97 ProcessManager* process_manager() { return ProcessManager::Get(profile()); }
98
99 // Starts running a test from the background page test extension.
100 //
101 // This registers a service worker with |script_name|, and fetches the
102 // registration result.
103 //
104 // If |error_or_null| is null (kExpectSuccess), success is expected and this
105 // will fail if there is an error.
106 // If |error_or_null| is not null, nothing is assumed, and the error (which
107 // may be empty) is written to it.
108 const Extension* StartTestFromBackgroundPage(const char* script_name,
109 std::string* error_or_null) {
110 const Extension* extension =
111 LoadExtension(test_data_dir_.AppendASCII("service_worker/background"));
112 CHECK(extension);
113 ExtensionHost* background_host =
114 process_manager()->GetBackgroundHostForExtension(extension->id());
115 CHECK(background_host);
116 std::string error;
117 CHECK(content::ExecuteScriptAndExtractString(
118 background_host->host_contents(),
119 base::StringPrintf("test.registerServiceWorker('%s')", script_name),
120 &error));
121 if (error_or_null)
122 *error_or_null = error;
123 else if (!error.empty())
124 ADD_FAILURE() << "Got unexpected error " << error;
125 return extension;
126 }
127
128 // Navigates the browser to a new tab at |url|, waits for it to load, then
129 // returns it.
130 content::WebContents* Navigate(const GURL& url) {
131 ui_test_utils::NavigateToURLWithDisposition(
132 browser(), url, NEW_FOREGROUND_TAB,
133 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
134 content::WebContents* web_contents =
135 browser()->tab_strip_model()->GetActiveWebContents();
136 content::WaitForLoadStop(web_contents);
137 return web_contents;
138 }
139
140 // Navigates the browser to |url| and returns the new tab's page type.
141 content::PageType NavigateAndGetPageType(const GURL& url) {
142 return Navigate(url)->GetController().GetActiveEntry()->GetPageType();
143 }
144
145 // Extracts the innerText from |contents|.
146 std::string ExtractInnerText(content::WebContents* contents) {
147 std::string inner_text;
148 if (!content::ExecuteScriptAndExtractString(
149 contents,
150 "window.domAutomationController.send(document.body.innerText)",
151 &inner_text)) {
152 ADD_FAILURE() << "Failed to get inner text for "
153 << contents->GetVisibleURL();
154 }
155 return inner_text;
156 }
157
158 // Navigates the browser to |url|, then returns the innerText of the new
159 // tab's WebContents' main frame.
160 std::string NavigateAndExtractInnerText(const GURL& url) {
161 return ExtractInnerText(Navigate(url));
162 }
163
annekao38685502015-07-14 17:46:39164 private:
lazyboy20167c22016-05-18 00:59:30165 // Sets the channel to "stable".
166 // Not useful after we've opened extension Service Workers to stable
167 // channel.
168 // TODO(lazyboy): Remove this when ExtensionServiceWorkersEnabled() is
169 // removed.
annekao38685502015-07-14 17:46:39170 ScopedCurrentChannel current_channel_;
kalman6f984ae2015-09-18 17:21:58171
annekao38685502015-07-14 17:46:39172 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerTest);
173};
174
lazyboybd325ae2015-11-18 21:35:26175class ServiceWorkerBackgroundSyncTest : public ServiceWorkerTest {
176 public:
177 ServiceWorkerBackgroundSyncTest() {}
178 ~ServiceWorkerBackgroundSyncTest() override {}
179
180 void SetUpCommandLine(base::CommandLine* command_line) override {
181 // ServiceWorkerRegistration.sync requires experimental flag.
182 command_line->AppendSwitch(
183 switches::kEnableExperimentalWebPlatformFeatures);
184 ServiceWorkerTest::SetUpCommandLine(command_line);
185 }
186
187 void SetUp() override {
188 content::background_sync_test_util::SetIgnoreNetworkChangeNotifier(true);
189 ServiceWorkerTest::SetUp();
190 }
191
192 private:
193 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerBackgroundSyncTest);
194};
195
lazyboy561b7de2015-11-19 19:27:30196class ServiceWorkerPushMessagingTest : public ServiceWorkerTest {
197 public:
198 ServiceWorkerPushMessagingTest()
199 : gcm_service_(nullptr), push_service_(nullptr) {}
200 ~ServiceWorkerPushMessagingTest() override {}
201
202 void GrantNotificationPermissionForTest(const GURL& url) {
203 GURL origin = url.GetOrigin();
204 DesktopNotificationProfileUtil::GrantPermission(profile(), origin);
lshang106c1772016-06-06 01:43:23205 ASSERT_EQ(blink::mojom::PermissionStatus::GRANTED,
206 PermissionManager::Get(profile())->GetPermissionStatus(
207 content::PermissionType::NOTIFICATIONS, origin, origin));
lazyboy561b7de2015-11-19 19:27:30208 }
209
210 PushMessagingAppIdentifier GetAppIdentifierForServiceWorkerRegistration(
avia2f4804a2015-12-24 23:11:13211 int64_t service_worker_registration_id,
lazyboy561b7de2015-11-19 19:27:30212 const GURL& origin) {
213 PushMessagingAppIdentifier app_identifier =
214 PushMessagingAppIdentifier::FindByServiceWorker(
215 profile(), origin, service_worker_registration_id);
216
217 EXPECT_FALSE(app_identifier.is_null());
218 return app_identifier;
219 }
220
221 // ExtensionApiTest overrides.
222 void SetUpCommandLine(base::CommandLine* command_line) override {
peter9de96272015-12-04 15:23:27223 command_line->AppendSwitch(
224 switches::kEnableExperimentalWebPlatformFeatures);
lazyboy561b7de2015-11-19 19:27:30225 ServiceWorkerTest::SetUpCommandLine(command_line);
226 }
227 void SetUpOnMainThread() override {
228 gcm_service_ = static_cast<gcm::FakeGCMProfileService*>(
229 gcm::GCMProfileServiceFactory::GetInstance()->SetTestingFactoryAndUse(
230 profile(), &gcm::FakeGCMProfileService::Build));
231 gcm_service_->set_collect(true);
232 push_service_ = PushMessagingServiceFactory::GetForProfile(profile());
233
234 ServiceWorkerTest::SetUpOnMainThread();
235 }
236
237 gcm::FakeGCMProfileService* gcm_service() const { return gcm_service_; }
238 PushMessagingServiceImpl* push_service() const { return push_service_; }
239
240 private:
241 gcm::FakeGCMProfileService* gcm_service_;
242 PushMessagingServiceImpl* push_service_;
243
244 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPushMessagingTest);
245};
246
lazyboy20167c22016-05-18 00:59:30247IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, RegisterSucceeds) {
kalman6f984ae2015-09-18 17:21:58248 StartTestFromBackgroundPage("register.js", kExpectSuccess);
annekao38685502015-07-14 17:46:39249}
250
lazyboyc3e763a2015-12-09 23:09:58251IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, UpdateRefreshesServiceWorker) {
252 base::ScopedTempDir scoped_temp_dir;
253 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
254 base::FilePath pem_path = test_data_dir_.AppendASCII("service_worker")
255 .AppendASCII("update")
256 .AppendASCII("service_worker.pem");
257 base::FilePath path_v1 = PackExtensionWithOptions(
258 test_data_dir_.AppendASCII("service_worker")
259 .AppendASCII("update")
260 .AppendASCII("v1"),
261 scoped_temp_dir.path().AppendASCII("v1.crx"), pem_path, base::FilePath());
262 base::FilePath path_v2 = PackExtensionWithOptions(
263 test_data_dir_.AppendASCII("service_worker")
264 .AppendASCII("update")
265 .AppendASCII("v2"),
266 scoped_temp_dir.path().AppendASCII("v2.crx"), pem_path, base::FilePath());
267 const char* kId = "hfaanndiiilofhfokeanhddpkfffchdi";
268
269 ExtensionTestMessageListener listener_v1("Pong from version 1", false);
270 listener_v1.set_failure_message("FAILURE_V1");
271 // Install version 1.0 of the extension.
272 ASSERT_TRUE(InstallExtension(path_v1, 1));
273 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
274 ->enabled_extensions()
275 .GetByID(kId));
276 EXPECT_TRUE(listener_v1.WaitUntilSatisfied());
277
278 ExtensionTestMessageListener listener_v2("Pong from version 2", false);
279 listener_v2.set_failure_message("FAILURE_V2");
280
281 // Update to version 2.0.
282 EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
283 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
284 ->enabled_extensions()
285 .GetByID(kId));
286 EXPECT_TRUE(listener_v2.WaitUntilSatisfied());
287}
288
lazyboy22eddc712015-12-10 21:16:26289IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, UpdateWithoutSkipWaiting) {
290 base::ScopedTempDir scoped_temp_dir;
291 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
292 base::FilePath pem_path = test_data_dir_.AppendASCII("service_worker")
293 .AppendASCII("update_without_skip_waiting")
294 .AppendASCII("update_without_skip_waiting.pem");
295 base::FilePath path_v1 = PackExtensionWithOptions(
296 test_data_dir_.AppendASCII("service_worker")
297 .AppendASCII("update_without_skip_waiting")
298 .AppendASCII("v1"),
299 scoped_temp_dir.path().AppendASCII("v1.crx"), pem_path, base::FilePath());
300 base::FilePath path_v2 = PackExtensionWithOptions(
301 test_data_dir_.AppendASCII("service_worker")
302 .AppendASCII("update_without_skip_waiting")
303 .AppendASCII("v2"),
304 scoped_temp_dir.path().AppendASCII("v2.crx"), pem_path, base::FilePath());
305 const char* kId = "mhnnnflgagdakldgjpfcofkiocpdmogl";
306
307 // Install version 1.0 of the extension.
308 ASSERT_TRUE(InstallExtension(path_v1, 1));
309 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
310 ->enabled_extensions()
311 .GetByID(kId));
312 const Extension* extension = extensions::ExtensionRegistry::Get(profile())
313 ->enabled_extensions()
314 .GetByID(kId);
315
316 ExtensionTestMessageListener listener1("Pong from version 1", false);
317 listener1.set_failure_message("FAILURE");
318 content::WebContents* web_contents =
319 AddTab(browser(), extension->GetResourceURL("page.html"));
320 EXPECT_TRUE(listener1.WaitUntilSatisfied());
321
322 // Update to version 2.0.
323 EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
324 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
325 ->enabled_extensions()
326 .GetByID(kId));
327 const Extension* extension_after_update =
328 extensions::ExtensionRegistry::Get(profile())
329 ->enabled_extensions()
330 .GetByID(kId);
331
332 // Service worker version 2 would be installed but it won't be controlling
333 // the extension page yet.
334 ExtensionTestMessageListener listener2("Pong from version 1", false);
335 listener2.set_failure_message("FAILURE");
336 web_contents =
337 AddTab(browser(), extension_after_update->GetResourceURL("page.html"));
338 EXPECT_TRUE(listener2.WaitUntilSatisfied());
339
340 // Navigate the tab away from the extension page so that no clients are
341 // using the service worker.
342 // Note that just closing the tab with WebContentsDestroyedWatcher doesn't
343 // seem to be enough because it returns too early.
344 WebContentsLoadStopObserver navigate_away_observer(web_contents);
345 web_contents->GetController().LoadURL(
346 GURL(url::kAboutBlankURL), content::Referrer(), ui::PAGE_TRANSITION_TYPED,
347 std::string());
348 navigate_away_observer.WaitForLoadStop();
349
350 // Now expect service worker version 2 to control the extension page.
351 ExtensionTestMessageListener listener3("Pong from version 2", false);
352 listener3.set_failure_message("FAILURE");
353 web_contents =
354 AddTab(browser(), extension_after_update->GetResourceURL("page.html"));
355 EXPECT_TRUE(listener3.WaitUntilSatisfied());
356}
357
kalman6f984ae2015-09-18 17:21:58358IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, FetchArbitraryPaths) {
359 const Extension* extension =
360 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
annekao1db36fd2015-07-29 17:09:16361
kalman6f984ae2015-09-18 17:21:58362 // Open some arbirary paths. Their contents should be what the service worker
363 // responds with, which in this case is the path of the fetch.
364 EXPECT_EQ(
365 "Caught a fetch for /index.html",
366 NavigateAndExtractInnerText(extension->GetResourceURL("index.html")));
367 EXPECT_EQ("Caught a fetch for /path/to/other.html",
368 NavigateAndExtractInnerText(
369 extension->GetResourceURL("path/to/other.html")));
370 EXPECT_EQ("Caught a fetch for /some/text/file.txt",
371 NavigateAndExtractInnerText(
372 extension->GetResourceURL("some/text/file.txt")));
373 EXPECT_EQ("Caught a fetch for /no/file/extension",
374 NavigateAndExtractInnerText(
375 extension->GetResourceURL("no/file/extension")));
376 EXPECT_EQ("Caught a fetch for /",
377 NavigateAndExtractInnerText(extension->GetResourceURL("")));
annekao1db36fd2015-07-29 17:09:16378}
379
lazyboy52c3bcf2016-01-08 00:11:29380IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, SWServedBackgroundPageReceivesEvent) {
381 const Extension* extension =
382 StartTestFromBackgroundPage("replace_background.js", kExpectSuccess);
383 ExtensionHost* background_page =
384 process_manager()->GetBackgroundHostForExtension(extension->id());
385 ASSERT_TRUE(background_page);
386
387 // Close the background page and start it again so that the service worker
388 // will start controlling pages.
389 background_page->Close();
390 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
391 background_page = nullptr;
392 process_manager()->WakeEventPage(extension->id(),
393 base::Bind(&DoNothingWithBool));
394 BackgroundPageWatcher(process_manager(), extension).WaitForOpen();
395
396 // Since the SW is now controlling the extension, the SW serves the background
397 // script. page.html sends a message to the background script and we verify
398 // that the SW served background script correctly receives the message/event.
399 ExtensionTestMessageListener listener("onMessage/SW BG.", false);
400 listener.set_failure_message("onMessage/original BG.");
401 content::WebContents* web_contents =
402 AddTab(browser(), extension->GetResourceURL("page.html"));
403 ASSERT_TRUE(web_contents);
404 EXPECT_TRUE(listener.WaitUntilSatisfied());
405}
406
kalman6f984ae2015-09-18 17:21:58407IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
408 LoadingBackgroundPageBypassesServiceWorker) {
409 const Extension* extension =
410 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
annekao49241182015-08-18 17:14:01411
kalman6f984ae2015-09-18 17:21:58412 std::string kExpectedInnerText = "background.html contents for testing.";
annekao49241182015-08-18 17:14:01413
kalman6f984ae2015-09-18 17:21:58414 // Sanity check that the background page has the expected content.
415 ExtensionHost* background_page =
416 process_manager()->GetBackgroundHostForExtension(extension->id());
417 ASSERT_TRUE(background_page);
418 EXPECT_EQ(kExpectedInnerText,
419 ExtractInnerText(background_page->host_contents()));
annekao49241182015-08-18 17:14:01420
kalman6f984ae2015-09-18 17:21:58421 // Close the background page.
422 background_page->Close();
423 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
424 background_page = nullptr;
425
426 // Start it again.
427 process_manager()->WakeEventPage(extension->id(),
428 base::Bind(&DoNothingWithBool));
429 BackgroundPageWatcher(process_manager(), extension).WaitForOpen();
430
431 // Content should not have been affected by the fetch, which would otherwise
432 // be "Caught fetch for...".
433 background_page =
434 process_manager()->GetBackgroundHostForExtension(extension->id());
435 ASSERT_TRUE(background_page);
436 content::WaitForLoadStop(background_page->host_contents());
437
438 // TODO(kalman): Everything you've read has been a LIE! It should be:
439 //
440 // EXPECT_EQ(kExpectedInnerText,
441 // ExtractInnerText(background_page->host_contents()));
442 //
443 // but there is a bug, and we're actually *not* bypassing the service worker
444 // for background page loads! For now, let it pass (assert wrong behavior)
445 // because it's not a regression, but this must be fixed eventually.
446 //
447 // Tracked in crbug.com/532720.
448 EXPECT_EQ("Caught a fetch for /background.html",
449 ExtractInnerText(background_page->host_contents()));
annekao49241182015-08-18 17:14:01450}
451
kalman6f984ae2015-09-18 17:21:58452IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
453 ServiceWorkerPostsMessageToBackgroundClient) {
454 const Extension* extension = StartTestFromBackgroundPage(
455 "post_message_to_background_client.js", kExpectSuccess);
annekao533482222015-08-21 23:23:53456
kalman6f984ae2015-09-18 17:21:58457 // The service worker in this test simply posts a message to the background
458 // client it receives from getBackgroundClient().
459 const char* kScript =
460 "var messagePromise = null;\n"
461 "if (test.lastMessageFromServiceWorker) {\n"
462 " messagePromise = Promise.resolve(test.lastMessageFromServiceWorker);\n"
463 "} else {\n"
464 " messagePromise = test.waitForMessage(navigator.serviceWorker);\n"
465 "}\n"
466 "messagePromise.then(function(message) {\n"
467 " window.domAutomationController.send(String(message == 'success'));\n"
468 "})\n";
469 EXPECT_EQ("true", ExecuteScriptInBackgroundPage(extension->id(), kScript));
annekao533482222015-08-21 23:23:53470}
471
kalman6f984ae2015-09-18 17:21:58472IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
473 BackgroundPagePostsMessageToServiceWorker) {
474 const Extension* extension =
475 StartTestFromBackgroundPage("post_message_to_sw.js", kExpectSuccess);
annekao533482222015-08-21 23:23:53476
kalman6f984ae2015-09-18 17:21:58477 // The service worker in this test waits for a message, then echoes it back
478 // by posting a message to the background page via getBackgroundClient().
479 const char* kScript =
480 "var mc = new MessageChannel();\n"
481 "test.waitForMessage(mc.port1).then(function(message) {\n"
482 " window.domAutomationController.send(String(message == 'hello'));\n"
483 "});\n"
484 "test.registeredServiceWorker.postMessage(\n"
485 " {message: 'hello', port: mc.port2}, [mc.port2])\n";
486 EXPECT_EQ("true", ExecuteScriptInBackgroundPage(extension->id(), kScript));
annekao533482222015-08-21 23:23:53487}
488
rdevlin.croninf5863da2015-09-10 19:21:45489IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
490 ServiceWorkerSuspensionOnExtensionUnload) {
kalman6f984ae2015-09-18 17:21:58491 // For this test, only hold onto the extension's ID and URL + a function to
492 // get a resource URL, because we're going to be disabling and uninstalling
493 // it, which will invalidate the pointer.
494 std::string extension_id;
495 GURL extension_url;
496 {
497 const Extension* extension =
498 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
499 extension_id = extension->id();
500 extension_url = extension->url();
501 }
502 auto get_resource_url = [&extension_url](const std::string& path) {
503 return Extension::GetResourceURL(extension_url, path);
504 };
rdevlin.croninf5863da2015-09-10 19:21:45505
kalman6f984ae2015-09-18 17:21:58506 // Fetch should route to the service worker.
507 EXPECT_EQ("Caught a fetch for /index.html",
508 NavigateAndExtractInnerText(get_resource_url("index.html")));
rdevlin.croninf5863da2015-09-10 19:21:45509
kalman6f984ae2015-09-18 17:21:58510 // Disable the extension. Opening the page should fail.
511 extension_service()->DisableExtension(extension_id,
rdevlin.croninf5863da2015-09-10 19:21:45512 Extension::DISABLE_USER_ACTION);
513 base::RunLoop().RunUntilIdle();
rdevlin.croninf5863da2015-09-10 19:21:45514
kalman6f984ae2015-09-18 17:21:58515 EXPECT_EQ(content::PAGE_TYPE_ERROR,
516 NavigateAndGetPageType(get_resource_url("index.html")));
517 EXPECT_EQ(content::PAGE_TYPE_ERROR,
518 NavigateAndGetPageType(get_resource_url("other.html")));
519
520 // Re-enable the extension. Opening pages should immediately start to succeed
521 // again.
rdevlin.croninf5863da2015-09-10 19:21:45522 extension_service()->EnableExtension(extension_id);
523 base::RunLoop().RunUntilIdle();
524
kalman6f984ae2015-09-18 17:21:58525 EXPECT_EQ("Caught a fetch for /index.html",
526 NavigateAndExtractInnerText(get_resource_url("index.html")));
527 EXPECT_EQ("Caught a fetch for /other.html",
528 NavigateAndExtractInnerText(get_resource_url("other.html")));
529 EXPECT_EQ("Caught a fetch for /another.html",
530 NavigateAndExtractInnerText(get_resource_url("another.html")));
rdevlin.croninf5863da2015-09-10 19:21:45531
kalman6f984ae2015-09-18 17:21:58532 // Uninstall the extension. Opening pages should fail again.
533 base::string16 error;
534 extension_service()->UninstallExtension(
535 extension_id, UninstallReason::UNINSTALL_REASON_FOR_TESTING,
536 base::Bind(&base::DoNothing), &error);
537 base::RunLoop().RunUntilIdle();
538
539 EXPECT_EQ(content::PAGE_TYPE_ERROR,
540 NavigateAndGetPageType(get_resource_url("index.html")));
541 EXPECT_EQ(content::PAGE_TYPE_ERROR,
542 NavigateAndGetPageType(get_resource_url("other.html")));
543 EXPECT_EQ(content::PAGE_TYPE_ERROR,
544 NavigateAndGetPageType(get_resource_url("anotherother.html")));
545 EXPECT_EQ(content::PAGE_TYPE_ERROR,
546 NavigateAndGetPageType(get_resource_url("final.html")));
547}
548
549IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, BackgroundPageIsWokenIfAsleep) {
550 const Extension* extension =
551 StartTestFromBackgroundPage("wake_on_fetch.js", kExpectSuccess);
552
553 // Navigate to special URLs that this test's service worker recognises, each
554 // making a check then populating the response with either "true" or "false".
555 EXPECT_EQ("true", NavigateAndExtractInnerText(extension->GetResourceURL(
556 "background-client-is-awake")));
557 EXPECT_EQ("true", NavigateAndExtractInnerText(
558 extension->GetResourceURL("ping-background-client")));
559 // Ping more than once for good measure.
560 EXPECT_EQ("true", NavigateAndExtractInnerText(
561 extension->GetResourceURL("ping-background-client")));
562
563 // Shut down the event page. The SW should detect that it's closed, but still
564 // be able to ping it.
565 ExtensionHost* background_page =
566 process_manager()->GetBackgroundHostForExtension(extension->id());
567 ASSERT_TRUE(background_page);
568 background_page->Close();
569 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
570
571 EXPECT_EQ("false", NavigateAndExtractInnerText(extension->GetResourceURL(
572 "background-client-is-awake")));
573 EXPECT_EQ("true", NavigateAndExtractInnerText(
574 extension->GetResourceURL("ping-background-client")));
575 EXPECT_EQ("true", NavigateAndExtractInnerText(
576 extension->GetResourceURL("ping-background-client")));
577 EXPECT_EQ("true", NavigateAndExtractInnerText(extension->GetResourceURL(
578 "background-client-is-awake")));
579}
580
581IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
582 GetBackgroundClientFailsWithNoBackgroundPage) {
583 // This extension doesn't have a background page, only a tab at page.html.
584 // The service worker it registers tries to call getBackgroundClient() and
585 // should fail.
586 // Note that this also tests that service workers can be registered from tabs.
587 EXPECT_TRUE(RunExtensionSubtest("service_worker/no_background", "page.html"));
rdevlin.croninf5863da2015-09-10 19:21:45588}
589
lazyboy6ddb7d62015-11-10 23:15:27590IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, NotificationAPI) {
591 EXPECT_TRUE(RunExtensionSubtest("service_worker/notifications/has_permission",
592 "page.html"));
593}
594
lazyboyaea32c22016-01-04 21:37:07595IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, WebAccessibleResourcesFetch) {
596 EXPECT_TRUE(RunExtensionSubtest(
597 "service_worker/web_accessible_resources/fetch/", "page.html"));
598}
599
lazyboyee4adef2016-05-24 00:55:16600IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, TabsCreate) {
601 // Extensions APIs from SW are only enabled on trunk.
602 ScopedCurrentChannel current_channel_override(version_info::Channel::UNKNOWN);
603 const Extension* extension = LoadExtensionWithFlags(
604 test_data_dir_.AppendASCII("service_worker/tabs_create"), kFlagNone);
605 ASSERT_TRUE(extension);
606 ui_test_utils::NavigateToURL(browser(),
607 extension->GetResourceURL("page.html"));
608 content::WebContents* web_contents =
609 browser()->tab_strip_model()->GetActiveWebContents();
610
611 int starting_tab_count = browser()->tab_strip_model()->count();
612 std::string result;
613 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
614 web_contents, "window.runServiceWorker()", &result));
615 ASSERT_EQ("chrome.tabs.create callback", result);
616 EXPECT_EQ(starting_tab_count + 1, browser()->tab_strip_model()->count());
617
618 // Check extension shutdown path.
619 UnloadExtension(extension->id());
620 EXPECT_EQ(starting_tab_count, browser()->tab_strip_model()->count());
621}
622
lazyboyaea32c22016-01-04 21:37:07623// This test loads a web page that has an iframe pointing to a
624// chrome-extension:// URL. The URL is listed in the extension's
625// web_accessible_resources. Initially the iframe is served from the extension's
626// resource file. After verifying that, we register a Service Worker that
627// controls the extension. Further requests to the same resource as before
628// should now be served by the Service Worker.
629// This test also verifies that if the requested resource exists in the manifest
630// but is not present in the extension directory, the Service Worker can still
631// serve the resource file.
632IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, WebAccessibleResourcesIframeSrc) {
633 const Extension* extension = LoadExtensionWithFlags(
634 test_data_dir_.AppendASCII(
635 "service_worker/web_accessible_resources/iframe_src"),
636 kFlagNone);
637 ASSERT_TRUE(extension);
638 ASSERT_TRUE(StartEmbeddedTestServer());
falkenad185092016-06-16 06:10:02639
640 // Service workers can only control secure contexts
641 // (https://w3c.github.io/webappsec-secure-contexts/). For documents, this
642 // typically means the document must have a secure origin AND all its ancestor
643 // frames must have documents with secure origins. However, extension pages
644 // are considered secure, even if they have an ancestor document that is an
645 // insecure context (see GetSchemesBypassingSecureContextCheckWhitelist). So
646 // extension service workers must be able to control an extension page
647 // embedded in an insecure context. To test this, set up an insecure
648 // (non-localhost, non-https) URL for the web page. This page will create
649 // iframes that load extension pages that must be controllable by service
650 // worker.
651 host_resolver()->AddRule("a.com", "127.0.0.1");
652 GURL page_url =
653 embedded_test_server()->GetURL("a.com",
654 "/extensions/api_test/service_worker/"
655 "web_accessible_resources/webpage.html");
656 EXPECT_FALSE(content::IsOriginSecure(page_url));
lazyboyaea32c22016-01-04 21:37:07657
658 content::WebContents* web_contents = AddTab(browser(), page_url);
659 std::string result;
660 // webpage.html will create an iframe pointing to a resource from |extension|.
661 // Expect the resource to be served by the extension.
662 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
663 web_contents, base::StringPrintf("window.testIframe('%s', 'iframe.html')",
664 extension->id().c_str()),
665 &result));
666 EXPECT_EQ("FROM_EXTENSION_RESOURCE", result);
667
668 ExtensionTestMessageListener service_worker_ready_listener("SW_READY", false);
669 EXPECT_TRUE(ExecuteScriptInBackgroundPageNoWait(
670 extension->id(), "window.registerServiceWorker()"));
671 EXPECT_TRUE(service_worker_ready_listener.WaitUntilSatisfied());
672
673 result.clear();
674 // webpage.html will create another iframe pointing to a resource from
675 // |extension| as before. But this time, the resource should be be served
676 // from the Service Worker.
677 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
678 web_contents, base::StringPrintf("window.testIframe('%s', 'iframe.html')",
679 extension->id().c_str()),
680 &result));
681 EXPECT_EQ("FROM_SW_RESOURCE", result);
682
683 result.clear();
684 // webpage.html will create yet another iframe pointing to a resource that
685 // exists in the extension manifest's web_accessible_resources, but is not
686 // present in the extension directory. Expect the resources of the iframe to
687 // be served by the Service Worker.
688 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
689 web_contents,
690 base::StringPrintf("window.testIframe('%s', 'iframe_non_existent.html')",
691 extension->id().c_str()),
692 &result));
693 EXPECT_EQ("FROM_SW_RESOURCE", result);
694}
695
lazyboybd325ae2015-11-18 21:35:26696IN_PROC_BROWSER_TEST_F(ServiceWorkerBackgroundSyncTest, Sync) {
697 const Extension* extension = LoadExtensionWithFlags(
698 test_data_dir_.AppendASCII("service_worker/sync"), kFlagNone);
699 ASSERT_TRUE(extension);
700 ui_test_utils::NavigateToURL(browser(),
701 extension->GetResourceURL("page.html"));
702 content::WebContents* web_contents =
703 browser()->tab_strip_model()->GetActiveWebContents();
704
705 // Prevent firing by going offline.
706 content::background_sync_test_util::SetOnline(web_contents, false);
707
708 ExtensionTestMessageListener sync_listener("SYNC: send-chats", false);
709 sync_listener.set_failure_message("FAIL");
710
711 std::string result;
712 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
713 web_contents, "window.runServiceWorker()", &result));
714 ASSERT_EQ("SERVICE_WORKER_READY", result);
715
716 EXPECT_FALSE(sync_listener.was_satisfied());
717 // Resume firing by going online.
718 content::background_sync_test_util::SetOnline(web_contents, true);
719 EXPECT_TRUE(sync_listener.WaitUntilSatisfied());
720}
721
horo1eeddde2015-11-19 05:59:25722IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
723 FetchFromContentScriptShouldNotGoToServiceWorkerOfPage) {
724 ASSERT_TRUE(StartEmbeddedTestServer());
725 GURL page_url = embedded_test_server()->GetURL(
726 "/extensions/api_test/service_worker/content_script_fetch/"
727 "controlled_page/index.html");
728 content::WebContents* tab =
729 browser()->tab_strip_model()->GetActiveWebContents();
730 ui_test_utils::NavigateToURL(browser(), page_url);
731 content::WaitForLoadStop(tab);
732
733 std::string value;
734 ASSERT_TRUE(
735 content::ExecuteScriptAndExtractString(tab, "register();", &value));
736 EXPECT_EQ("SW controlled", value);
737
738 ASSERT_TRUE(RunExtensionTest("service_worker/content_script_fetch"))
739 << message_;
740}
741
lazyboyd429e2582016-05-20 20:18:52742IN_PROC_BROWSER_TEST_F(ServiceWorkerPushMessagingTest, OnPush) {
lazyboy561b7de2015-11-19 19:27:30743 const Extension* extension = LoadExtensionWithFlags(
744 test_data_dir_.AppendASCII("service_worker/push_messaging"), kFlagNone);
745 ASSERT_TRUE(extension);
746 GURL extension_url = extension->url();
747
748 ASSERT_NO_FATAL_FAILURE(GrantNotificationPermissionForTest(extension_url));
749
750 GURL url = extension->GetResourceURL("page.html");
751 ui_test_utils::NavigateToURL(browser(), url);
752
753 content::WebContents* web_contents =
754 browser()->tab_strip_model()->GetActiveWebContents();
755
756 // Start the ServiceWorker.
757 ExtensionTestMessageListener ready_listener("SERVICE_WORKER_READY", false);
758 ready_listener.set_failure_message("SERVICE_WORKER_FAILURE");
759 const char* kScript = "window.runServiceWorker()";
760 EXPECT_TRUE(content::ExecuteScript(web_contents->GetMainFrame(), kScript));
761 EXPECT_TRUE(ready_listener.WaitUntilSatisfied());
762
763 PushMessagingAppIdentifier app_identifier =
764 GetAppIdentifierForServiceWorkerRegistration(0LL, extension_url);
765 ASSERT_EQ(app_identifier.app_id(), gcm_service()->last_registered_app_id());
766 EXPECT_EQ("1234567890", gcm_service()->last_registered_sender_ids()[0]);
767
lazyboyd429e2582016-05-20 20:18:52768 base::RunLoop run_loop;
lazyboy561b7de2015-11-19 19:27:30769 // Send a push message via gcm and expect the ServiceWorker to receive it.
770 ExtensionTestMessageListener push_message_listener("OK", false);
771 push_message_listener.set_failure_message("FAIL");
772 gcm::IncomingMessage message;
773 message.sender_id = "1234567890";
774 message.raw_data = "testdata";
775 message.decrypted = true;
lazyboyd429e2582016-05-20 20:18:52776 push_service()->SetMessageCallbackForTesting(run_loop.QuitClosure());
lazyboy561b7de2015-11-19 19:27:30777 push_service()->OnMessage(app_identifier.app_id(), message);
778 EXPECT_TRUE(push_message_listener.WaitUntilSatisfied());
lazyboyd429e2582016-05-20 20:18:52779 run_loop.Run(); // Wait until the message is handled by push service.
lazyboy561b7de2015-11-19 19:27:30780}
781
annekao38685502015-07-14 17:46:39782} // namespace extensions