Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / extensions / service_worker_apitest.cc
blob29fad8fa5aa7285fc66d58454b3be8d38db67766
1 // 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.
5 #include "base/bind_helpers.h"
6 #include "base/strings/stringprintf.h"
7 #include "chrome/browser/extensions/extension_apitest.h"
8 #include "chrome/browser/extensions/extension_service.h"
9 #include "chrome/browser/ui/tabs/tab_strip_model.h"
10 #include "chrome/test/base/ui_test_utils.h"
11 #include "components/version_info/version_info.h"
12 #include "content/public/browser/navigation_controller.h"
13 #include "content/public/browser/navigation_entry.h"
14 #include "content/public/browser/web_contents.h"
15 #include "content/public/common/page_type.h"
16 #include "content/public/test/browser_test_utils.h"
17 #include "extensions/browser/extension_host.h"
18 #include "extensions/browser/process_manager.h"
19 #include "extensions/test/background_page_watcher.h"
20 #include "extensions/test/extension_test_message_listener.h"
22 namespace extensions {
24 namespace {
26 // Pass into ServiceWorkerTest::StartTestFromBackgroundPage to indicate that
27 // registration is expected to succeed.
28 std::string* const kExpectSuccess = nullptr;
30 void DoNothingWithBool(bool b) {}
32 } // namespace
34 class ServiceWorkerTest : public ExtensionApiTest {
35 public:
36 ServiceWorkerTest() : current_channel_(version_info::Channel::UNKNOWN) {}
38 ~ServiceWorkerTest() override {}
40 protected:
41 // Returns the ProcessManager for the test's profile.
42 ProcessManager* process_manager() { return ProcessManager::Get(profile()); }
44 // Starts running a test from the background page test extension.
46 // This registers a service worker with |script_name|, and fetches the
47 // registration result.
49 // If |error_or_null| is null (kExpectSuccess), success is expected and this
50 // will fail if there is an error.
51 // If |error_or_null| is not null, nothing is assumed, and the error (which
52 // may be empty) is written to it.
53 const Extension* StartTestFromBackgroundPage(const char* script_name,
54 std::string* error_or_null) {
55 const Extension* extension =
56 LoadExtension(test_data_dir_.AppendASCII("service_worker/background"));
57 CHECK(extension);
58 ExtensionHost* background_host =
59 process_manager()->GetBackgroundHostForExtension(extension->id());
60 CHECK(background_host);
61 std::string error;
62 CHECK(content::ExecuteScriptAndExtractString(
63 background_host->host_contents(),
64 base::StringPrintf("test.registerServiceWorker('%s')", script_name),
65 &error));
66 if (error_or_null)
67 *error_or_null = error;
68 else if (!error.empty())
69 ADD_FAILURE() << "Got unexpected error " << error;
70 return extension;
73 // Navigates the browser to a new tab at |url|, waits for it to load, then
74 // returns it.
75 content::WebContents* Navigate(const GURL& url) {
76 ui_test_utils::NavigateToURLWithDisposition(
77 browser(), url, NEW_FOREGROUND_TAB,
78 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
79 content::WebContents* web_contents =
80 browser()->tab_strip_model()->GetActiveWebContents();
81 content::WaitForLoadStop(web_contents);
82 return web_contents;
85 // Navigates the browser to |url| and returns the new tab's page type.
86 content::PageType NavigateAndGetPageType(const GURL& url) {
87 return Navigate(url)->GetController().GetActiveEntry()->GetPageType();
90 // Extracts the innerText from |contents|.
91 std::string ExtractInnerText(content::WebContents* contents) {
92 std::string inner_text;
93 if (!content::ExecuteScriptAndExtractString(
94 contents,
95 "window.domAutomationController.send(document.body.innerText)",
96 &inner_text)) {
97 ADD_FAILURE() << "Failed to get inner text for "
98 << contents->GetVisibleURL();
100 return inner_text;
103 // Navigates the browser to |url|, then returns the innerText of the new
104 // tab's WebContents' main frame.
105 std::string NavigateAndExtractInnerText(const GURL& url) {
106 return ExtractInnerText(Navigate(url));
109 private:
110 // Sets the channel to "trunk" since service workers are restricted to trunk.
111 ScopedCurrentChannel current_channel_;
113 DISALLOW_COPY_AND_ASSIGN(ServiceWorkerTest);
116 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, RegisterSucceedsOnTrunk) {
117 StartTestFromBackgroundPage("register.js", kExpectSuccess);
120 // This feature is restricted to trunk, so on dev it should have existing
121 // behavior - which is for it to fail.
122 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, RegisterFailsOnDev) {
123 ScopedCurrentChannel current_channel_override(
124 version_info::Channel::DEV);
125 std::string error;
126 const Extension* extension =
127 StartTestFromBackgroundPage("register.js", &error);
128 EXPECT_EQ(
129 "Failed to register a ServiceWorker: The URL protocol of the current "
130 "origin ('chrome-extension://" +
131 extension->id() + "') is not supported.",
132 error);
135 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, FetchArbitraryPaths) {
136 const Extension* extension =
137 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
139 // Open some arbirary paths. Their contents should be what the service worker
140 // responds with, which in this case is the path of the fetch.
141 EXPECT_EQ(
142 "Caught a fetch for /index.html",
143 NavigateAndExtractInnerText(extension->GetResourceURL("index.html")));
144 EXPECT_EQ("Caught a fetch for /path/to/other.html",
145 NavigateAndExtractInnerText(
146 extension->GetResourceURL("path/to/other.html")));
147 EXPECT_EQ("Caught a fetch for /some/text/file.txt",
148 NavigateAndExtractInnerText(
149 extension->GetResourceURL("some/text/file.txt")));
150 EXPECT_EQ("Caught a fetch for /no/file/extension",
151 NavigateAndExtractInnerText(
152 extension->GetResourceURL("no/file/extension")));
153 EXPECT_EQ("Caught a fetch for /",
154 NavigateAndExtractInnerText(extension->GetResourceURL("")));
157 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
158 LoadingBackgroundPageBypassesServiceWorker) {
159 const Extension* extension =
160 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
162 std::string kExpectedInnerText = "background.html contents for testing.";
164 // Sanity check that the background page has the expected content.
165 ExtensionHost* background_page =
166 process_manager()->GetBackgroundHostForExtension(extension->id());
167 ASSERT_TRUE(background_page);
168 EXPECT_EQ(kExpectedInnerText,
169 ExtractInnerText(background_page->host_contents()));
171 // Close the background page.
172 background_page->Close();
173 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
174 background_page = nullptr;
176 // Start it again.
177 process_manager()->WakeEventPage(extension->id(),
178 base::Bind(&DoNothingWithBool));
179 BackgroundPageWatcher(process_manager(), extension).WaitForOpen();
181 // Content should not have been affected by the fetch, which would otherwise
182 // be "Caught fetch for...".
183 background_page =
184 process_manager()->GetBackgroundHostForExtension(extension->id());
185 ASSERT_TRUE(background_page);
186 content::WaitForLoadStop(background_page->host_contents());
188 // TODO(kalman): Everything you've read has been a LIE! It should be:
190 // EXPECT_EQ(kExpectedInnerText,
191 // ExtractInnerText(background_page->host_contents()));
193 // but there is a bug, and we're actually *not* bypassing the service worker
194 // for background page loads! For now, let it pass (assert wrong behavior)
195 // because it's not a regression, but this must be fixed eventually.
197 // Tracked in crbug.com/532720.
198 EXPECT_EQ("Caught a fetch for /background.html",
199 ExtractInnerText(background_page->host_contents()));
202 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
203 ServiceWorkerPostsMessageToBackgroundClient) {
204 const Extension* extension = StartTestFromBackgroundPage(
205 "post_message_to_background_client.js", kExpectSuccess);
207 // The service worker in this test simply posts a message to the background
208 // client it receives from getBackgroundClient().
209 const char* kScript =
210 "var messagePromise = null;\n"
211 "if (test.lastMessageFromServiceWorker) {\n"
212 " messagePromise = Promise.resolve(test.lastMessageFromServiceWorker);\n"
213 "} else {\n"
214 " messagePromise = test.waitForMessage(navigator.serviceWorker);\n"
215 "}\n"
216 "messagePromise.then(function(message) {\n"
217 " window.domAutomationController.send(String(message == 'success'));\n"
218 "})\n";
219 EXPECT_EQ("true", ExecuteScriptInBackgroundPage(extension->id(), kScript));
222 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
223 BackgroundPagePostsMessageToServiceWorker) {
224 const Extension* extension =
225 StartTestFromBackgroundPage("post_message_to_sw.js", kExpectSuccess);
227 // The service worker in this test waits for a message, then echoes it back
228 // by posting a message to the background page via getBackgroundClient().
229 const char* kScript =
230 "var mc = new MessageChannel();\n"
231 "test.waitForMessage(mc.port1).then(function(message) {\n"
232 " window.domAutomationController.send(String(message == 'hello'));\n"
233 "});\n"
234 "test.registeredServiceWorker.postMessage(\n"
235 " {message: 'hello', port: mc.port2}, [mc.port2])\n";
236 EXPECT_EQ("true", ExecuteScriptInBackgroundPage(extension->id(), kScript));
239 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
240 ServiceWorkerSuspensionOnExtensionUnload) {
241 // For this test, only hold onto the extension's ID and URL + a function to
242 // get a resource URL, because we're going to be disabling and uninstalling
243 // it, which will invalidate the pointer.
244 std::string extension_id;
245 GURL extension_url;
247 const Extension* extension =
248 StartTestFromBackgroundPage("fetch.js", kExpectSuccess);
249 extension_id = extension->id();
250 extension_url = extension->url();
252 auto get_resource_url = [&extension_url](const std::string& path) {
253 return Extension::GetResourceURL(extension_url, path);
256 // Fetch should route to the service worker.
257 EXPECT_EQ("Caught a fetch for /index.html",
258 NavigateAndExtractInnerText(get_resource_url("index.html")));
260 // Disable the extension. Opening the page should fail.
261 extension_service()->DisableExtension(extension_id,
262 Extension::DISABLE_USER_ACTION);
263 base::RunLoop().RunUntilIdle();
265 EXPECT_EQ(content::PAGE_TYPE_ERROR,
266 NavigateAndGetPageType(get_resource_url("index.html")));
267 EXPECT_EQ(content::PAGE_TYPE_ERROR,
268 NavigateAndGetPageType(get_resource_url("other.html")));
270 // Re-enable the extension. Opening pages should immediately start to succeed
271 // again.
272 extension_service()->EnableExtension(extension_id);
273 base::RunLoop().RunUntilIdle();
275 EXPECT_EQ("Caught a fetch for /index.html",
276 NavigateAndExtractInnerText(get_resource_url("index.html")));
277 EXPECT_EQ("Caught a fetch for /other.html",
278 NavigateAndExtractInnerText(get_resource_url("other.html")));
279 EXPECT_EQ("Caught a fetch for /another.html",
280 NavigateAndExtractInnerText(get_resource_url("another.html")));
282 // Uninstall the extension. Opening pages should fail again.
283 base::string16 error;
284 extension_service()->UninstallExtension(
285 extension_id, UninstallReason::UNINSTALL_REASON_FOR_TESTING,
286 base::Bind(&base::DoNothing), &error);
287 base::RunLoop().RunUntilIdle();
289 EXPECT_EQ(content::PAGE_TYPE_ERROR,
290 NavigateAndGetPageType(get_resource_url("index.html")));
291 EXPECT_EQ(content::PAGE_TYPE_ERROR,
292 NavigateAndGetPageType(get_resource_url("other.html")));
293 EXPECT_EQ(content::PAGE_TYPE_ERROR,
294 NavigateAndGetPageType(get_resource_url("anotherother.html")));
295 EXPECT_EQ(content::PAGE_TYPE_ERROR,
296 NavigateAndGetPageType(get_resource_url("final.html")));
299 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest, BackgroundPageIsWokenIfAsleep) {
300 const Extension* extension =
301 StartTestFromBackgroundPage("wake_on_fetch.js", kExpectSuccess);
303 // Navigate to special URLs that this test's service worker recognises, each
304 // making a check then populating the response with either "true" or "false".
305 EXPECT_EQ("true", NavigateAndExtractInnerText(extension->GetResourceURL(
306 "background-client-is-awake")));
307 EXPECT_EQ("true", NavigateAndExtractInnerText(
308 extension->GetResourceURL("ping-background-client")));
309 // Ping more than once for good measure.
310 EXPECT_EQ("true", NavigateAndExtractInnerText(
311 extension->GetResourceURL("ping-background-client")));
313 // Shut down the event page. The SW should detect that it's closed, but still
314 // be able to ping it.
315 ExtensionHost* background_page =
316 process_manager()->GetBackgroundHostForExtension(extension->id());
317 ASSERT_TRUE(background_page);
318 background_page->Close();
319 BackgroundPageWatcher(process_manager(), extension).WaitForClose();
321 EXPECT_EQ("false", NavigateAndExtractInnerText(extension->GetResourceURL(
322 "background-client-is-awake")));
323 EXPECT_EQ("true", NavigateAndExtractInnerText(
324 extension->GetResourceURL("ping-background-client")));
325 EXPECT_EQ("true", NavigateAndExtractInnerText(
326 extension->GetResourceURL("ping-background-client")));
327 EXPECT_EQ("true", NavigateAndExtractInnerText(extension->GetResourceURL(
328 "background-client-is-awake")));
331 IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
332 GetBackgroundClientFailsWithNoBackgroundPage) {
333 // This extension doesn't have a background page, only a tab at page.html.
334 // The service worker it registers tries to call getBackgroundClient() and
335 // should fail.
336 // Note that this also tests that service workers can be registered from tabs.
337 EXPECT_TRUE(RunExtensionSubtest("service_worker/no_background", "page.html"));
340 } // namespace extensions