blob: 6ca009a572db82afd1ab6de7db023752a800d345 [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"
peter9f4490a2017-01-27 00:58:3613#include "chrome/browser/gcm/fake_gcm_profile_service.h"
14#include "chrome/browser/gcm/gcm_profile_service_factory.h"
lazyboy561b7de2015-11-19 19:27:3015#include "chrome/browser/notifications/desktop_notification_profile_util.h"
lshang106c1772016-06-06 01:43:2316#include "chrome/browser/permissions/permission_manager.h"
lazyboy561b7de2015-11-19 19:27:3017#include "chrome/browser/push_messaging/push_messaging_app_identifier.h"
18#include "chrome/browser/push_messaging/push_messaging_service_factory.h"
19#include "chrome/browser/push_messaging/push_messaging_service_impl.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"
johnmea5045732016-09-08 17:23:2922#include "components/gcm_driver/instance_id/fake_gcm_driver_for_instance_id.h"
sdefresne9fb67692015-08-03 18:48:2223#include "components/version_info/version_info.h"
kalman6f984ae2015-09-18 17:21:5824#include "content/public/browser/navigation_controller.h"
rdevlin.croninf5863da2015-09-10 19:21:4525#include "content/public/browser/navigation_entry.h"
lshang106c1772016-06-06 01:43:2326#include "content/public/browser/permission_type.h"
lazyboy4c82177a2016-10-18 00:04:0927#include "content/public/browser/service_worker_context.h"
28#include "content/public/browser/storage_partition.h"
kalman6f984ae2015-09-18 17:21:5829#include "content/public/browser/web_contents.h"
lazyboybd325ae2015-11-18 21:35:2630#include "content/public/common/content_switches.h"
falkenad185092016-06-16 06:10:0231#include "content/public/common/origin_util.h"
kalman6f984ae2015-09-18 17:21:5832#include "content/public/common/page_type.h"
lazyboybd325ae2015-11-18 21:35:2633#include "content/public/test/background_sync_test_util.h"
annekao1db36fd2015-07-29 17:09:1634#include "content/public/test/browser_test_utils.h"
kalman6f984ae2015-09-18 17:21:5835#include "extensions/browser/extension_host.h"
lazyboyc3e763a2015-12-09 23:09:5836#include "extensions/browser/extension_registry.h"
kalman6f984ae2015-09-18 17:21:5837#include "extensions/browser/process_manager.h"
38#include "extensions/test/background_page_watcher.h"
annekao38685502015-07-14 17:46:3939#include "extensions/test/extension_test_message_listener.h"
falkenad185092016-06-16 06:10:0240#include "net/dns/mock_host_resolver.h"
horo1eeddde2015-11-19 05:59:2541#include "net/test/embedded_test_server/embedded_test_server.h"
annekao38685502015-07-14 17:46:3942
43namespace extensions {
44
kalman6f984ae2015-09-18 17:21:5845namespace {
46
47// Pass into ServiceWorkerTest::StartTestFromBackgroundPage to indicate that
48// registration is expected to succeed.
49std::string* const kExpectSuccess = nullptr;
50
51void DoNothingWithBool(bool b) {}
52
lazyboy22eddc712015-12-10 21:16:2653// Returns the newly added WebContents.
54content::WebContents* AddTab(Browser* browser, const GURL& url) {
55 int starting_tab_count = browser->tab_strip_model()->count();
56 ui_test_utils::NavigateToURLWithDisposition(
nick3b04f322016-08-31 19:29:1957 browser, url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
lazyboy22eddc712015-12-10 21:16:2658 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
59 int tab_count = browser->tab_strip_model()->count();
60 EXPECT_EQ(starting_tab_count + 1, tab_count);
61 return browser->tab_strip_model()->GetActiveWebContents();
62}
63
64class WebContentsLoadStopObserver : content::WebContentsObserver {
65 public:
66 explicit WebContentsLoadStopObserver(content::WebContents* web_contents)
67 : content::WebContentsObserver(web_contents),
68 load_stop_observed_(false) {}
69
70 void WaitForLoadStop() {
71 if (load_stop_observed_)
72 return;
73 message_loop_runner_ = new content::MessageLoopRunner;
74 message_loop_runner_->Run();
75 }
76
77 private:
78 void DidStopLoading() override {
79 load_stop_observed_ = true;
80 if (message_loop_runner_)
81 message_loop_runner_->Quit();
82 }
83
84 bool load_stop_observed_;
85 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
86
87 DISALLOW_COPY_AND_ASSIGN(WebContentsLoadStopObserver);
88};
89
kalman6f984ae2015-09-18 17:21:5890} // namespace
91
annekao38685502015-07-14 17:46:3992class ServiceWorkerTest : public ExtensionApiTest {
93 public:
lazyboy20167c22016-05-18 00:59:3094 ServiceWorkerTest() : current_channel_(version_info::Channel::STABLE) {}
annekao38685502015-07-14 17:46:3995
96 ~ServiceWorkerTest() override {}
97
kalman6f984ae2015-09-18 17:21:5898 protected:
99 // Returns the ProcessManager for the test's profile.
100 ProcessManager* process_manager() { return ProcessManager::Get(profile()); }
101
102 // Starts running a test from the background page test extension.
103 //
104 // This registers a service worker with |script_name|, and fetches the
105 // registration result.
106 //
107 // If |error_or_null| is null (kExpectSuccess), success is expected and this
108 // will fail if there is an error.
109 // If |error_or_null| is not null, nothing is assumed, and the error (which
110 // may be empty) is written to it.
111 const Extension* StartTestFromBackgroundPage(const char* script_name,
112 std::string* error_or_null) {
113 const Extension* extension =
114 LoadExtension(test_data_dir_.AppendASCII("service_worker/background"));
115 CHECK(extension);
116 ExtensionHost* background_host =
117 process_manager()->GetBackgroundHostForExtension(extension->id());
118 CHECK(background_host);
119 std::string error;
120 CHECK(content::ExecuteScriptAndExtractString(
121 background_host->host_contents(),
122 base::StringPrintf("test.registerServiceWorker('%s')", script_name),
123 &error));
124 if (error_or_null)
125 *error_or_null = error;
126 else if (!error.empty())
127 ADD_FAILURE() << "Got unexpected error " << error;
128 return extension;
129 }
130
131 // Navigates the browser to a new tab at |url|, waits for it to load, then
132 // returns it.
133 content::WebContents* Navigate(const GURL& url) {
134 ui_test_utils::NavigateToURLWithDisposition(
nick3b04f322016-08-31 19:29:19135 browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
kalman6f984ae2015-09-18 17:21:58136 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
137 content::WebContents* web_contents =
138 browser()->tab_strip_model()->GetActiveWebContents();
139 content::WaitForLoadStop(web_contents);
140 return web_contents;
141 }
142
143 // Navigates the browser to |url| and returns the new tab's page type.
144 content::PageType NavigateAndGetPageType(const GURL& url) {
145 return Navigate(url)->GetController().GetActiveEntry()->GetPageType();
146 }
147
148 // Extracts the innerText from |contents|.
149 std::string ExtractInnerText(content::WebContents* contents) {
150 std::string inner_text;
151 if (!content::ExecuteScriptAndExtractString(
152 contents,
153 "window.domAutomationController.send(document.body.innerText)",
154 &inner_text)) {
155 ADD_FAILURE() << "Failed to get inner text for "
156 << contents->GetVisibleURL();
157 }
158 return inner_text;
159 }
160
161 // Navigates the browser to |url|, then returns the innerText of the new
162 // tab's WebContents' main frame.
163 std::string NavigateAndExtractInnerText(const GURL& url) {
164 return ExtractInnerText(Navigate(url));
165 }
166
lazyboy4c82177a2016-10-18 00:04:09167 size_t GetWorkerRefCount(const GURL& origin) {
168 content::ServiceWorkerContext* sw_context =
169 content::BrowserContext::GetDefaultStoragePartition(
170 browser()->profile())
171 ->GetServiceWorkerContext();
172 base::RunLoop run_loop;
173 size_t ref_count = 0;
174 auto set_ref_count = [](size_t* ref_count, base::RunLoop* run_loop,
175 size_t external_request_count) {
176 *ref_count = external_request_count;
177 run_loop->Quit();
178 };
179 sw_context->CountExternalRequestsForTest(
180 origin, base::Bind(set_ref_count, &ref_count, &run_loop));
181 run_loop.Run();
182 return ref_count;
183 }
184
annekao38685502015-07-14 17:46:39185 private:
lazyboy20167c22016-05-18 00:59:30186 // Sets the channel to "stable".
187 // Not useful after we've opened extension Service Workers to stable
188 // channel.
189 // TODO(lazyboy): Remove this when ExtensionServiceWorkersEnabled() is
190 // removed.
annekao38685502015-07-14 17:46:39191 ScopedCurrentChannel current_channel_;
kalman6f984ae2015-09-18 17:21:58192
annekao38685502015-07-14 17:46:39193 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerTest);
194};
195
lazyboybd325ae2015-11-18 21:35:26196class ServiceWorkerBackgroundSyncTest : public ServiceWorkerTest {
197 public:
198 ServiceWorkerBackgroundSyncTest() {}
199 ~ServiceWorkerBackgroundSyncTest() override {}
200
201 void SetUpCommandLine(base::CommandLine* command_line) override {
202 // ServiceWorkerRegistration.sync requires experimental flag.
203 command_line->AppendSwitch(
204 switches::kEnableExperimentalWebPlatformFeatures);
205 ServiceWorkerTest::SetUpCommandLine(command_line);
206 }
207
208 void SetUp() override {
209 content::background_sync_test_util::SetIgnoreNetworkChangeNotifier(true);
210 ServiceWorkerTest::SetUp();
211 }
212
213 private:
214 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerBackgroundSyncTest);
215};
216
lazyboy561b7de2015-11-19 19:27:30217class ServiceWorkerPushMessagingTest : public ServiceWorkerTest {
218 public:
219 ServiceWorkerPushMessagingTest()
johnmea5045732016-09-08 17:23:29220 : gcm_driver_(nullptr), push_service_(nullptr) {}
lazyboy561b7de2015-11-19 19:27:30221 ~ServiceWorkerPushMessagingTest() override {}
222
223 void GrantNotificationPermissionForTest(const GURL& url) {
224 GURL origin = url.GetOrigin();
225 DesktopNotificationProfileUtil::GrantPermission(profile(), origin);
lshang106c1772016-06-06 01:43:23226 ASSERT_EQ(blink::mojom::PermissionStatus::GRANTED,
227 PermissionManager::Get(profile())->GetPermissionStatus(
228 content::PermissionType::NOTIFICATIONS, origin, origin));
lazyboy561b7de2015-11-19 19:27:30229 }
230
231 PushMessagingAppIdentifier GetAppIdentifierForServiceWorkerRegistration(
avia2f4804a2015-12-24 23:11:13232 int64_t service_worker_registration_id,
lazyboy561b7de2015-11-19 19:27:30233 const GURL& origin) {
234 PushMessagingAppIdentifier app_identifier =
235 PushMessagingAppIdentifier::FindByServiceWorker(
236 profile(), origin, service_worker_registration_id);
237
238 EXPECT_FALSE(app_identifier.is_null());
239 return app_identifier;
240 }
241
242 // ExtensionApiTest overrides.
243 void SetUpCommandLine(base::CommandLine* command_line) override {
peter9de96272015-12-04 15:23:27244 command_line->AppendSwitch(
245 switches::kEnableExperimentalWebPlatformFeatures);
lazyboy561b7de2015-11-19 19:27:30246 ServiceWorkerTest::SetUpCommandLine(command_line);
247 }
248 void SetUpOnMainThread() override {
johnmea5045732016-09-08 17:23:29249 gcm::FakeGCMProfileService* gcm_service =
250 static_cast<gcm::FakeGCMProfileService*>(
251 gcm::GCMProfileServiceFactory::GetInstance()
252 ->SetTestingFactoryAndUse(profile(),
253 &gcm::FakeGCMProfileService::Build));
254 gcm_driver_ = static_cast<instance_id::FakeGCMDriverForInstanceID*>(
255 gcm_service->driver());
lazyboy561b7de2015-11-19 19:27:30256 push_service_ = PushMessagingServiceFactory::GetForProfile(profile());
257
258 ServiceWorkerTest::SetUpOnMainThread();
259 }
260
johnmea5045732016-09-08 17:23:29261 instance_id::FakeGCMDriverForInstanceID* gcm_driver() const {
262 return gcm_driver_;
263 }
lazyboy561b7de2015-11-19 19:27:30264 PushMessagingServiceImpl* push_service() const { return push_service_; }
265
266 private:
johnmea5045732016-09-08 17:23:29267 instance_id::FakeGCMDriverForInstanceID* gcm_driver_;
lazyboy561b7de2015-11-19 19:27:30268 PushMessagingServiceImpl* push_service_;
269
270 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerPushMessagingTest);
271};
272
lazyboy20167c22016-05-18 00:59:30273IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, RegisterSucceeds) {
kalman6f984ae2015-09-18 17:21:58274 StartTestFromBackgroundPage("register.js", kExpectSuccess);
annekao38685502015-07-14 17:46:39275}
276
lazyboyc3e763a2015-12-09 23:09:58277IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, UpdateRefreshesServiceWorker) {
278 base::ScopedTempDir scoped_temp_dir;
279 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
280 base::FilePath pem_path = test_data_dir_.AppendASCII("service_worker")
281 .AppendASCII("update")
282 .AppendASCII("service_worker.pem");
vabr9142fe22016-09-08 13:19:22283 base::FilePath path_v1 =
284 PackExtensionWithOptions(test_data_dir_.AppendASCII("service_worker")
285 .AppendASCII("update")
286 .AppendASCII("v1"),
287 scoped_temp_dir.GetPath().AppendASCII("v1.crx"),
288 pem_path, base::FilePath());
289 base::FilePath path_v2 =
290 PackExtensionWithOptions(test_data_dir_.AppendASCII("service_worker")
291 .AppendASCII("update")
292 .AppendASCII("v2"),
293 scoped_temp_dir.GetPath().AppendASCII("v2.crx"),
294 pem_path, base::FilePath());
lazyboyc3e763a2015-12-09 23:09:58295 const char* kId = "hfaanndiiilofhfokeanhddpkfffchdi";
296
297 ExtensionTestMessageListener listener_v1("Pong from version 1", false);
298 listener_v1.set_failure_message("FAILURE_V1");
299 // Install version 1.0 of the extension.
300 ASSERT_TRUE(InstallExtension(path_v1, 1));
301 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
302 ->enabled_extensions()
303 .GetByID(kId));
304 EXPECT_TRUE(listener_v1.WaitUntilSatisfied());
305
306 ExtensionTestMessageListener listener_v2("Pong from version 2", false);
307 listener_v2.set_failure_message("FAILURE_V2");
308
309 // Update to version 2.0.
310 EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
311 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
312 ->enabled_extensions()
313 .GetByID(kId));
314 EXPECT_TRUE(listener_v2.WaitUntilSatisfied());
315}
316
lazyboy22eddc712015-12-10 21:16:26317IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, UpdateWithoutSkipWaiting) {
318 base::ScopedTempDir scoped_temp_dir;
319 ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
320 base::FilePath pem_path = test_data_dir_.AppendASCII("service_worker")
321 .AppendASCII("update_without_skip_waiting")
322 .AppendASCII("update_without_skip_waiting.pem");
vabr9142fe22016-09-08 13:19:22323 base::FilePath path_v1 =
324 PackExtensionWithOptions(test_data_dir_.AppendASCII("service_worker")
325 .AppendASCII("update_without_skip_waiting")
326 .AppendASCII("v1"),
327 scoped_temp_dir.GetPath().AppendASCII("v1.crx"),
328 pem_path, base::FilePath());
329 base::FilePath path_v2 =
330 PackExtensionWithOptions(test_data_dir_.AppendASCII("service_worker")
331 .AppendASCII("update_without_skip_waiting")
332 .AppendASCII("v2"),
333 scoped_temp_dir.GetPath().AppendASCII("v2.crx"),
334 pem_path, base::FilePath());
lazyboy22eddc712015-12-10 21:16:26335 const char* kId = "mhnnnflgagdakldgjpfcofkiocpdmogl";
336
337 // Install version 1.0 of the extension.
338 ASSERT_TRUE(InstallExtension(path_v1, 1));
339 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
340 ->enabled_extensions()
341 .GetByID(kId));
342 const Extension* extension = extensions::ExtensionRegistry::Get(profile())
343 ->enabled_extensions()
344 .GetByID(kId);
345
346 ExtensionTestMessageListener listener1("Pong from version 1", false);
347 listener1.set_failure_message("FAILURE");
348 content::WebContents* web_contents =
349 AddTab(browser(), extension->GetResourceURL("page.html"));
350 EXPECT_TRUE(listener1.WaitUntilSatisfied());
351
352 // Update to version 2.0.
353 EXPECT_TRUE(UpdateExtension(kId, path_v2, 0));
354 EXPECT_TRUE(extensions::ExtensionRegistry::Get(profile())
355 ->enabled_extensions()
356 .GetByID(kId));
357 const Extension* extension_after_update =
358 extensions::ExtensionRegistry::Get(profile())
359 ->enabled_extensions()
360 .GetByID(kId);
361
362 // Service worker version 2 would be installed but it won't be controlling
363 // the extension page yet.
364 ExtensionTestMessageListener listener2("Pong from version 1", false);
365 listener2.set_failure_message("FAILURE");
366 web_contents =
367 AddTab(browser(), extension_after_update->GetResourceURL("page.html"));
368 EXPECT_TRUE(listener2.WaitUntilSatisfied());
369
370 // Navigate the tab away from the extension page so that no clients are
371 // using the service worker.
372 // Note that just closing the tab with WebContentsDestroyedWatcher doesn't
373 // seem to be enough because it returns too early.
374 WebContentsLoadStopObserver navigate_away_observer(web_contents);
375 web_contents->GetController().LoadURL(
376 GURL(url::kAboutBlankURL), content::Referrer(), ui::PAGE_TRANSITION_TYPED,
377 std::string());
378 navigate_away_observer.WaitForLoadStop();
379
380 // Now expect service worker version 2 to control the extension page.
381 ExtensionTestMessageListener listener3("Pong from version 2", false);
382 listener3.set_failure_message("FAILURE");
383 web_contents =
384 AddTab(browser(), extension_after_update->GetResourceURL("page.html"));
385 EXPECT_TRUE(listener3.WaitUntilSatisfied());
386}
387
kalman6f984ae2015-09-18 17:21:58388IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, FetchArbitraryPaths) {
389 const Extension* extension =
390 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
annekao1db36fd2015-07-29 17:09:16391
kalman6f984ae2015-09-18 17:21:58392 // Open some arbirary paths. Their contents should be what the service worker
393 // responds with, which in this case is the path of the fetch.
394 EXPECT_EQ(
395 "Caught a fetch for /index.html",
396 NavigateAndExtractInnerText(extension->GetResourceURL("index.html")));
397 EXPECT_EQ("Caught a fetch for /path/to/other.html",
398 NavigateAndExtractInnerText(
399 extension->GetResourceURL("path/to/other.html")));
400 EXPECT_EQ("Caught a fetch for /some/text/file.txt",
401 NavigateAndExtractInnerText(
402 extension->GetResourceURL("some/text/file.txt")));
403 EXPECT_EQ("Caught a fetch for /no/file/extension",
404 NavigateAndExtractInnerText(
405 extension->GetResourceURL("no/file/extension")));
406 EXPECT_EQ("Caught a fetch for /",
407 NavigateAndExtractInnerText(extension->GetResourceURL("")));
annekao1db36fd2015-07-29 17:09:16408}
409
lazyboy52c3bcf2016-01-08 00:11:29410IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, SWServedBackgroundPageReceivesEvent) {
411 const Extension* extension =
412 StartTestFromBackgroundPage("replace_background.js", kExpectSuccess);
413 ExtensionHost* background_page =
414 process_manager()->GetBackgroundHostForExtension(extension->id());
415 ASSERT_TRUE(background_page);
416
417 // Close the background page and start it again so that the service worker
418 // will start controlling pages.
419 background_page->Close();
420 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
421 background_page = nullptr;
422 process_manager()->WakeEventPage(extension->id(),
423 base::Bind(&DoNothingWithBool));
424 BackgroundPageWatcher(process_manager(), extension).WaitForOpen();
425
426 // Since the SW is now controlling the extension, the SW serves the background
427 // script. page.html sends a message to the background script and we verify
428 // that the SW served background script correctly receives the message/event.
429 ExtensionTestMessageListener listener("onMessage/SW BG.", false);
430 listener.set_failure_message("onMessage/original BG.");
431 content::WebContents* web_contents =
432 AddTab(browser(), extension->GetResourceURL("page.html"));
433 ASSERT_TRUE(web_contents);
434 EXPECT_TRUE(listener.WaitUntilSatisfied());
435}
436
kalman6f984ae2015-09-18 17:21:58437IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
438 LoadingBackgroundPageBypassesServiceWorker) {
439 const Extension* extension =
440 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
annekao49241182015-08-18 17:14:01441
kalman6f984ae2015-09-18 17:21:58442 std::string kExpectedInnerText = "background.html contents for testing.";
annekao49241182015-08-18 17:14:01443
kalman6f984ae2015-09-18 17:21:58444 // Sanity check that the background page has the expected content.
445 ExtensionHost* background_page =
446 process_manager()->GetBackgroundHostForExtension(extension->id());
447 ASSERT_TRUE(background_page);
448 EXPECT_EQ(kExpectedInnerText,
449 ExtractInnerText(background_page->host_contents()));
annekao49241182015-08-18 17:14:01450
kalman6f984ae2015-09-18 17:21:58451 // Close the background page.
452 background_page->Close();
453 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
454 background_page = nullptr;
455
456 // Start it again.
457 process_manager()->WakeEventPage(extension->id(),
458 base::Bind(&DoNothingWithBool));
459 BackgroundPageWatcher(process_manager(), extension).WaitForOpen();
460
461 // Content should not have been affected by the fetch, which would otherwise
462 // be "Caught fetch for...".
463 background_page =
464 process_manager()->GetBackgroundHostForExtension(extension->id());
465 ASSERT_TRUE(background_page);
466 content::WaitForLoadStop(background_page->host_contents());
467
468 // TODO(kalman): Everything you've read has been a LIE! It should be:
469 //
470 // EXPECT_EQ(kExpectedInnerText,
471 // ExtractInnerText(background_page->host_contents()));
472 //
473 // but there is a bug, and we're actually *not* bypassing the service worker
474 // for background page loads! For now, let it pass (assert wrong behavior)
475 // because it's not a regression, but this must be fixed eventually.
476 //
477 // Tracked in crbug.com/532720.
478 EXPECT_EQ("Caught a fetch for /background.html",
479 ExtractInnerText(background_page->host_contents()));
annekao49241182015-08-18 17:14:01480}
481
kalman6f984ae2015-09-18 17:21:58482IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
483 ServiceWorkerPostsMessageToBackgroundClient) {
484 const Extension* extension = StartTestFromBackgroundPage(
485 "post_message_to_background_client.js", kExpectSuccess);
annekao533482222015-08-21 23:23:53486
kalman6f984ae2015-09-18 17:21:58487 // The service worker in this test simply posts a message to the background
488 // client it receives from getBackgroundClient().
489 const char* kScript =
490 "var messagePromise = null;\n"
491 "if (test.lastMessageFromServiceWorker) {\n"
492 " messagePromise = Promise.resolve(test.lastMessageFromServiceWorker);\n"
493 "} else {\n"
494 " messagePromise = test.waitForMessage(navigator.serviceWorker);\n"
495 "}\n"
496 "messagePromise.then(function(message) {\n"
497 " window.domAutomationController.send(String(message == 'success'));\n"
498 "})\n";
499 EXPECT_EQ("true", ExecuteScriptInBackgroundPage(extension->id(), kScript));
annekao533482222015-08-21 23:23:53500}
501
kalman6f984ae2015-09-18 17:21:58502IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
503 BackgroundPagePostsMessageToServiceWorker) {
504 const Extension* extension =
505 StartTestFromBackgroundPage("post_message_to_sw.js", kExpectSuccess);
annekao533482222015-08-21 23:23:53506
kalman6f984ae2015-09-18 17:21:58507 // The service worker in this test waits for a message, then echoes it back
508 // by posting a message to the background page via getBackgroundClient().
509 const char* kScript =
510 "var mc = new MessageChannel();\n"
511 "test.waitForMessage(mc.port1).then(function(message) {\n"
512 " window.domAutomationController.send(String(message == 'hello'));\n"
513 "});\n"
514 "test.registeredServiceWorker.postMessage(\n"
515 " {message: 'hello', port: mc.port2}, [mc.port2])\n";
516 EXPECT_EQ("true", ExecuteScriptInBackgroundPage(extension->id(), kScript));
annekao533482222015-08-21 23:23:53517}
518
rdevlin.croninf5863da2015-09-10 19:21:45519IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
520 ServiceWorkerSuspensionOnExtensionUnload) {
kalman6f984ae2015-09-18 17:21:58521 // For this test, only hold onto the extension's ID and URL + a function to
522 // get a resource URL, because we're going to be disabling and uninstalling
523 // it, which will invalidate the pointer.
524 std::string extension_id;
525 GURL extension_url;
526 {
527 const Extension* extension =
528 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
529 extension_id = extension->id();
530 extension_url = extension->url();
531 }
532 auto get_resource_url = [&extension_url](const std::string& path) {
533 return Extension::GetResourceURL(extension_url, path);
534 };
rdevlin.croninf5863da2015-09-10 19:21:45535
kalman6f984ae2015-09-18 17:21:58536 // Fetch should route to the service worker.
537 EXPECT_EQ("Caught a fetch for /index.html",
538 NavigateAndExtractInnerText(get_resource_url("index.html")));
rdevlin.croninf5863da2015-09-10 19:21:45539
kalman6f984ae2015-09-18 17:21:58540 // Disable the extension. Opening the page should fail.
541 extension_service()->DisableExtension(extension_id,
rdevlin.croninf5863da2015-09-10 19:21:45542 Extension::DISABLE_USER_ACTION);
543 base::RunLoop().RunUntilIdle();
rdevlin.croninf5863da2015-09-10 19:21:45544
kalman6f984ae2015-09-18 17:21:58545 EXPECT_EQ(content::PAGE_TYPE_ERROR,
546 NavigateAndGetPageType(get_resource_url("index.html")));
547 EXPECT_EQ(content::PAGE_TYPE_ERROR,
548 NavigateAndGetPageType(get_resource_url("other.html")));
549
550 // Re-enable the extension. Opening pages should immediately start to succeed
551 // again.
rdevlin.croninf5863da2015-09-10 19:21:45552 extension_service()->EnableExtension(extension_id);
553 base::RunLoop().RunUntilIdle();
554
kalman6f984ae2015-09-18 17:21:58555 EXPECT_EQ("Caught a fetch for /index.html",
556 NavigateAndExtractInnerText(get_resource_url("index.html")));
557 EXPECT_EQ("Caught a fetch for /other.html",
558 NavigateAndExtractInnerText(get_resource_url("other.html")));
559 EXPECT_EQ("Caught a fetch for /another.html",
560 NavigateAndExtractInnerText(get_resource_url("another.html")));
rdevlin.croninf5863da2015-09-10 19:21:45561
kalman6f984ae2015-09-18 17:21:58562 // Uninstall the extension. Opening pages should fail again.
563 base::string16 error;
564 extension_service()->UninstallExtension(
565 extension_id, UninstallReason::UNINSTALL_REASON_FOR_TESTING,
566 base::Bind(&base::DoNothing), &error);
567 base::RunLoop().RunUntilIdle();
568
569 EXPECT_EQ(content::PAGE_TYPE_ERROR,
570 NavigateAndGetPageType(get_resource_url("index.html")));
571 EXPECT_EQ(content::PAGE_TYPE_ERROR,
572 NavigateAndGetPageType(get_resource_url("other.html")));
573 EXPECT_EQ(content::PAGE_TYPE_ERROR,
574 NavigateAndGetPageType(get_resource_url("anotherother.html")));
575 EXPECT_EQ(content::PAGE_TYPE_ERROR,
576 NavigateAndGetPageType(get_resource_url("final.html")));
577}
578
579IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, BackgroundPageIsWokenIfAsleep) {
580 const Extension* extension =
581 StartTestFromBackgroundPage("wake_on_fetch.js", kExpectSuccess);
582
583 // Navigate to special URLs that this test's service worker recognises, each
584 // making a check then populating the response with either "true" or "false".
585 EXPECT_EQ("true", NavigateAndExtractInnerText(extension->GetResourceURL(
586 "background-client-is-awake")));
587 EXPECT_EQ("true", NavigateAndExtractInnerText(
588 extension->GetResourceURL("ping-background-client")));
589 // Ping more than once for good measure.
590 EXPECT_EQ("true", NavigateAndExtractInnerText(
591 extension->GetResourceURL("ping-background-client")));
592
593 // Shut down the event page. The SW should detect that it's closed, but still
594 // be able to ping it.
595 ExtensionHost* background_page =
596 process_manager()->GetBackgroundHostForExtension(extension->id());
597 ASSERT_TRUE(background_page);
598 background_page->Close();
599 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
600
601 EXPECT_EQ("false", NavigateAndExtractInnerText(extension->GetResourceURL(
602 "background-client-is-awake")));
603 EXPECT_EQ("true", NavigateAndExtractInnerText(
604 extension->GetResourceURL("ping-background-client")));
605 EXPECT_EQ("true", NavigateAndExtractInnerText(
606 extension->GetResourceURL("ping-background-client")));
607 EXPECT_EQ("true", NavigateAndExtractInnerText(extension->GetResourceURL(
608 "background-client-is-awake")));
609}
610
611IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
612 GetBackgroundClientFailsWithNoBackgroundPage) {
613 // This extension doesn't have a background page, only a tab at page.html.
614 // The service worker it registers tries to call getBackgroundClient() and
615 // should fail.
616 // Note that this also tests that service workers can be registered from tabs.
617 EXPECT_TRUE(RunExtensionSubtest("service_worker/no_background", "page.html"));
rdevlin.croninf5863da2015-09-10 19:21:45618}
619
lazyboy6ddb7d62015-11-10 23:15:27620IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, NotificationAPI) {
621 EXPECT_TRUE(RunExtensionSubtest("service_worker/notifications/has_permission",
622 "page.html"));
623}
624
lazyboyaea32c22016-01-04 21:37:07625IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, WebAccessibleResourcesFetch) {
626 EXPECT_TRUE(RunExtensionSubtest(
627 "service_worker/web_accessible_resources/fetch/", "page.html"));
628}
629
lazyboyee4adef2016-05-24 00:55:16630IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, TabsCreate) {
631 // Extensions APIs from SW are only enabled on trunk.
632 ScopedCurrentChannel current_channel_override(version_info::Channel::UNKNOWN);
633 const Extension* extension = LoadExtensionWithFlags(
634 test_data_dir_.AppendASCII("service_worker/tabs_create"), kFlagNone);
635 ASSERT_TRUE(extension);
636 ui_test_utils::NavigateToURL(browser(),
637 extension->GetResourceURL("page.html"));
638 content::WebContents* web_contents =
639 browser()->tab_strip_model()->GetActiveWebContents();
640
641 int starting_tab_count = browser()->tab_strip_model()->count();
642 std::string result;
643 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
644 web_contents, "window.runServiceWorker()", &result));
645 ASSERT_EQ("chrome.tabs.create callback", result);
646 EXPECT_EQ(starting_tab_count + 1, browser()->tab_strip_model()->count());
647
648 // Check extension shutdown path.
649 UnloadExtension(extension->id());
650 EXPECT_EQ(starting_tab_count, browser()->tab_strip_model()->count());
651}
652
lazyboy4c82177a2016-10-18 00:04:09653// Tests that worker ref count increments while extension API function is
654// active.
655IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, WorkerRefCount) {
656 // Extensions APIs from SW are only enabled on trunk.
657 ScopedCurrentChannel current_channel_override(version_info::Channel::UNKNOWN);
658 const Extension* extension = LoadExtensionWithFlags(
659 test_data_dir_.AppendASCII("service_worker/api_worker_ref_count"),
660 kFlagNone);
661 ASSERT_TRUE(extension);
662 ui_test_utils::NavigateToURL(browser(),
663 extension->GetResourceURL("page.html"));
664 content::WebContents* web_contents =
665 browser()->tab_strip_model()->GetActiveWebContents();
666
667 ExtensionTestMessageListener worker_start_listener("WORKER STARTED", false);
668 worker_start_listener.set_failure_message("FAILURE");
669 ASSERT_TRUE(
670 content::ExecuteScript(web_contents, "window.runServiceWorker()"));
671 ASSERT_TRUE(worker_start_listener.WaitUntilSatisfied());
672
673 // Service worker should have no pending requests because it hasn't peformed
674 // any extension API request yet.
675 EXPECT_EQ(0u, GetWorkerRefCount(extension->url()));
676
677 ExtensionTestMessageListener worker_listener("CHECK_REF_COUNT", true);
678 worker_listener.set_failure_message("FAILURE");
679 ASSERT_TRUE(content::ExecuteScript(web_contents, "window.testSendMessage()"));
680 ASSERT_TRUE(worker_listener.WaitUntilSatisfied());
681
682 // Service worker should have exactly one pending request because
683 // chrome.test.sendMessage() API call is in-flight.
684 EXPECT_EQ(1u, GetWorkerRefCount(extension->url()));
685
686 // Peform another extension API request while one is ongoing.
687 {
688 ExtensionTestMessageListener listener("CHECK_REF_COUNT", true);
689 listener.set_failure_message("FAILURE");
690 ASSERT_TRUE(
691 content::ExecuteScript(web_contents, "window.testSendMessage()"));
692 ASSERT_TRUE(listener.WaitUntilSatisfied());
693
694 // Service worker currently has two extension API requests in-flight.
695 EXPECT_EQ(2u, GetWorkerRefCount(extension->url()));
696 // Finish executing the nested chrome.test.sendMessage() first.
697 listener.Reply("Hello world");
698 }
699
700 ExtensionTestMessageListener extension_listener("SUCCESS", false);
701 extension_listener.set_failure_message("FAILURE");
702 // Finish executing chrome.test.sendMessage().
703 worker_listener.Reply("Hello world");
704 ASSERT_TRUE(extension_listener.WaitUntilSatisfied());
705
706 // The ref count should drop to 0.
707 EXPECT_EQ(0u, GetWorkerRefCount(extension->url()));
708}
709
lazyboyaea32c22016-01-04 21:37:07710// This test loads a web page that has an iframe pointing to a
711// chrome-extension:// URL. The URL is listed in the extension's
712// web_accessible_resources. Initially the iframe is served from the extension's
713// resource file. After verifying that, we register a Service Worker that
714// controls the extension. Further requests to the same resource as before
715// should now be served by the Service Worker.
716// This test also verifies that if the requested resource exists in the manifest
717// but is not present in the extension directory, the Service Worker can still
718// serve the resource file.
719IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, WebAccessibleResourcesIframeSrc) {
720 const Extension* extension = LoadExtensionWithFlags(
721 test_data_dir_.AppendASCII(
722 "service_worker/web_accessible_resources/iframe_src"),
723 kFlagNone);
724 ASSERT_TRUE(extension);
725 ASSERT_TRUE(StartEmbeddedTestServer());
falkenad185092016-06-16 06:10:02726
727 // Service workers can only control secure contexts
728 // (https://w3c.github.io/webappsec-secure-contexts/). For documents, this
729 // typically means the document must have a secure origin AND all its ancestor
730 // frames must have documents with secure origins. However, extension pages
731 // are considered secure, even if they have an ancestor document that is an
732 // insecure context (see GetSchemesBypassingSecureContextCheckWhitelist). So
733 // extension service workers must be able to control an extension page
734 // embedded in an insecure context. To test this, set up an insecure
735 // (non-localhost, non-https) URL for the web page. This page will create
736 // iframes that load extension pages that must be controllable by service
737 // worker.
738 host_resolver()->AddRule("a.com", "127.0.0.1");
739 GURL page_url =
740 embedded_test_server()->GetURL("a.com",
741 "/extensions/api_test/service_worker/"
742 "web_accessible_resources/webpage.html");
743 EXPECT_FALSE(content::IsOriginSecure(page_url));
lazyboyaea32c22016-01-04 21:37:07744
745 content::WebContents* web_contents = AddTab(browser(), page_url);
746 std::string result;
747 // webpage.html will create an iframe pointing to a resource from |extension|.
748 // Expect the resource to be served by the extension.
749 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
750 web_contents, base::StringPrintf("window.testIframe('%s', 'iframe.html')",
751 extension->id().c_str()),
752 &result));
753 EXPECT_EQ("FROM_EXTENSION_RESOURCE", result);
754
755 ExtensionTestMessageListener service_worker_ready_listener("SW_READY", false);
756 EXPECT_TRUE(ExecuteScriptInBackgroundPageNoWait(
757 extension->id(), "window.registerServiceWorker()"));
758 EXPECT_TRUE(service_worker_ready_listener.WaitUntilSatisfied());
759
760 result.clear();
761 // webpage.html will create another iframe pointing to a resource from
762 // |extension| as before. But this time, the resource should be be served
763 // from the Service Worker.
764 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
765 web_contents, base::StringPrintf("window.testIframe('%s', 'iframe.html')",
766 extension->id().c_str()),
767 &result));
768 EXPECT_EQ("FROM_SW_RESOURCE", result);
769
770 result.clear();
771 // webpage.html will create yet another iframe pointing to a resource that
772 // exists in the extension manifest's web_accessible_resources, but is not
773 // present in the extension directory. Expect the resources of the iframe to
774 // be served by the Service Worker.
775 EXPECT_TRUE(content::ExecuteScriptAndExtractString(
776 web_contents,
777 base::StringPrintf("window.testIframe('%s', 'iframe_non_existent.html')",
778 extension->id().c_str()),
779 &result));
780 EXPECT_EQ("FROM_SW_RESOURCE", result);
781}
782
lazyboybd325ae2015-11-18 21:35:26783IN_PROC_BROWSER_TEST_F(ServiceWorkerBackgroundSyncTest, Sync) {
784 const Extension* extension = LoadExtensionWithFlags(
785 test_data_dir_.AppendASCII("service_worker/sync"), kFlagNone);
786 ASSERT_TRUE(extension);
787 ui_test_utils::NavigateToURL(browser(),
788 extension->GetResourceURL("page.html"));
789 content::WebContents* web_contents =
790 browser()->tab_strip_model()->GetActiveWebContents();
791
792 // Prevent firing by going offline.
793 content::background_sync_test_util::SetOnline(web_contents, false);
794
795 ExtensionTestMessageListener sync_listener("SYNC: send-chats", false);
796 sync_listener.set_failure_message("FAIL");
797
798 std::string result;
799 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
800 web_contents, "window.runServiceWorker()", &result));
801 ASSERT_EQ("SERVICE_WORKER_READY", result);
802
803 EXPECT_FALSE(sync_listener.was_satisfied());
804 // Resume firing by going online.
805 content::background_sync_test_util::SetOnline(web_contents, true);
806 EXPECT_TRUE(sync_listener.WaitUntilSatisfied());
807}
808
horo1eeddde2015-11-19 05:59:25809IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
810 FetchFromContentScriptShouldNotGoToServiceWorkerOfPage) {
811 ASSERT_TRUE(StartEmbeddedTestServer());
812 GURL page_url = embedded_test_server()->GetURL(
813 "/extensions/api_test/service_worker/content_script_fetch/"
814 "controlled_page/index.html");
815 content::WebContents* tab =
816 browser()->tab_strip_model()->GetActiveWebContents();
817 ui_test_utils::NavigateToURL(browser(), page_url);
818 content::WaitForLoadStop(tab);
819
820 std::string value;
821 ASSERT_TRUE(
822 content::ExecuteScriptAndExtractString(tab, "register();", &value));
823 EXPECT_EQ("SW controlled", value);
824
825 ASSERT_TRUE(RunExtensionTest("service_worker/content_script_fetch"))
826 << message_;
827}
828
lazyboyd429e2582016-05-20 20:18:52829IN_PROC_BROWSER_TEST_F(ServiceWorkerPushMessagingTest, OnPush) {
lazyboy561b7de2015-11-19 19:27:30830 const Extension* extension = LoadExtensionWithFlags(
831 test_data_dir_.AppendASCII("service_worker/push_messaging"), kFlagNone);
832 ASSERT_TRUE(extension);
833 GURL extension_url = extension->url();
834
835 ASSERT_NO_FATAL_FAILURE(GrantNotificationPermissionForTest(extension_url));
836
837 GURL url = extension->GetResourceURL("page.html");
838 ui_test_utils::NavigateToURL(browser(), url);
839
840 content::WebContents* web_contents =
841 browser()->tab_strip_model()->GetActiveWebContents();
842
843 // Start the ServiceWorker.
844 ExtensionTestMessageListener ready_listener("SERVICE_WORKER_READY", false);
845 ready_listener.set_failure_message("SERVICE_WORKER_FAILURE");
846 const char* kScript = "window.runServiceWorker()";
847 EXPECT_TRUE(content::ExecuteScript(web_contents->GetMainFrame(), kScript));
848 EXPECT_TRUE(ready_listener.WaitUntilSatisfied());
849
850 PushMessagingAppIdentifier app_identifier =
851 GetAppIdentifierForServiceWorkerRegistration(0LL, extension_url);
johnmea5045732016-09-08 17:23:29852 ASSERT_EQ(app_identifier.app_id(), gcm_driver()->last_gettoken_app_id());
853 EXPECT_EQ("1234567890", gcm_driver()->last_gettoken_authorized_entity());
lazyboy561b7de2015-11-19 19:27:30854
lazyboyd429e2582016-05-20 20:18:52855 base::RunLoop run_loop;
lazyboy561b7de2015-11-19 19:27:30856 // Send a push message via gcm and expect the ServiceWorker to receive it.
857 ExtensionTestMessageListener push_message_listener("OK", false);
858 push_message_listener.set_failure_message("FAIL");
859 gcm::IncomingMessage message;
860 message.sender_id = "1234567890";
861 message.raw_data = "testdata";
862 message.decrypted = true;
lazyboyd429e2582016-05-20 20:18:52863 push_service()->SetMessageCallbackForTesting(run_loop.QuitClosure());
lazyboy561b7de2015-11-19 19:27:30864 push_service()->OnMessage(app_identifier.app_id(), message);
865 EXPECT_TRUE(push_message_listener.WaitUntilSatisfied());
lazyboyd429e2582016-05-20 20:18:52866 run_loop.Run(); // Wait until the message is handled by push service.
lazyboy561b7de2015-11-19 19:27:30867}
868
annekao38685502015-07-14 17:46:39869} // namespace extensions