1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 #include "base/callback.h"
7 #include "base/command_line.h"
8 #include "base/run_loop.h"
9 #include "content/browser/service_worker/embedded_worker_instance.h"
10 #include "content/browser/service_worker/embedded_worker_registry.h"
11 #include "content/browser/service_worker/service_worker_context_core.h"
12 #include "content/browser/service_worker/service_worker_context_wrapper.h"
13 #include "content/browser/service_worker/service_worker_registration.h"
14 #include "content/browser/service_worker/service_worker_test_utils.h"
15 #include "content/browser/service_worker/service_worker_version.h"
16 #include "content/common/service_worker/service_worker_messages.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/render_process_host.h"
20 #include "content/public/browser/storage_partition.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/common/content_switches.h"
23 #include "content/shell/browser/shell.h"
24 #include "content/test/content_browser_test.h"
25 #include "content/test/content_browser_test_utils.h"
26 #include "net/test/embedded_test_server/embedded_test_server.h"
32 void RunAndQuit(const base::Closure
& closure
,
33 const base::Closure
& quit
,
34 base::MessageLoopProxy
* original_message_loop
) {
36 original_message_loop
->PostTask(FROM_HERE
, quit
);
39 void RunOnIOThread(const base::Closure
& closure
) {
40 base::RunLoop run_loop
;
41 BrowserThread::PostTask(
42 BrowserThread::IO
, FROM_HERE
,
43 base::Bind(&RunAndQuit
, closure
, run_loop
.QuitClosure(),
44 base::MessageLoopProxy::current()));
48 void ReceiveFetchResult(BrowserThread::ID run_quit_thread
,
49 const base::Closure
& quit
,
50 ServiceWorkerStatusCode
* out_status
,
51 ServiceWorkerFetchResponse
* out_response
,
52 ServiceWorkerStatusCode actual_status
,
53 const ServiceWorkerFetchResponse
& actual_response
) {
54 *out_status
= actual_status
;
55 *out_response
= actual_response
;
57 BrowserThread::PostTask(run_quit_thread
, FROM_HERE
, quit
);
60 ServiceWorkerVersion::FetchCallback
CreateFetchResponseReceiver(
61 BrowserThread::ID run_quit_thread
,
62 const base::Closure
& quit
,
63 ServiceWorkerStatusCode
* out_status
,
64 ServiceWorkerFetchResponse
* out_response
) {
66 &ReceiveFetchResult
, run_quit_thread
, quit
, out_status
, out_response
);
71 class ServiceWorkerBrowserTest
: public ContentBrowserTest
{
73 typedef ServiceWorkerBrowserTest self
;
75 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
76 command_line
->AppendSwitch(switches::kEnableServiceWorker
);
79 virtual void SetUpOnMainThread() OVERRIDE
{
80 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
81 StoragePartition
* partition
= BrowserContext::GetDefaultStoragePartition(
82 shell()->web_contents()->GetBrowserContext());
83 wrapper_
= partition
->GetServiceWorkerContext();
85 // Navigate to the page to set up a renderer page (where we can embed
87 NavigateToURLBlockUntilNavigationsComplete(
89 embedded_test_server()->GetURL("/service_worker/empty.html"), 1);
91 RunOnIOThread(base::Bind(&self::SetUpOnIOThread
, this));
94 virtual void TearDownOnMainThread() OVERRIDE
{
95 RunOnIOThread(base::Bind(&self::TearDownOnIOThread
, this));
99 virtual void SetUpOnIOThread() {}
100 virtual void TearDownOnIOThread() {}
102 ServiceWorkerContextWrapper
* wrapper() { return wrapper_
.get(); }
104 void AssociateRendererProcessToWorker(EmbeddedWorkerInstance
* worker
) {
105 worker
->AddProcessReference(
106 shell()->web_contents()->GetRenderProcessHost()->GetID());
110 scoped_refptr
<ServiceWorkerContextWrapper
> wrapper_
;
113 class EmbeddedWorkerBrowserTest
: public ServiceWorkerBrowserTest
,
114 public EmbeddedWorkerInstance::Observer
{
116 typedef EmbeddedWorkerBrowserTest self
;
118 EmbeddedWorkerBrowserTest()
119 : last_worker_status_(EmbeddedWorkerInstance::STOPPED
) {}
120 virtual ~EmbeddedWorkerBrowserTest() {}
122 virtual void TearDownOnIOThread() OVERRIDE
{
124 worker_
->RemoveObserver(this);
129 void StartOnIOThread() {
130 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
131 worker_
= wrapper()->context()->embedded_worker_registry()->CreateWorker();
132 EXPECT_EQ(EmbeddedWorkerInstance::STOPPED
, worker_
->status());
133 worker_
->AddObserver(this);
135 AssociateRendererProcessToWorker(worker_
.get());
137 const int64 service_worker_version_id
= 33L;
138 const GURL script_url
= embedded_test_server()->GetURL(
139 "/service_worker/worker.js");
140 ServiceWorkerStatusCode status
= worker_
->Start(
141 service_worker_version_id
, script_url
);
143 last_worker_status_
= worker_
->status();
144 EXPECT_EQ(SERVICE_WORKER_OK
, status
);
145 EXPECT_EQ(EmbeddedWorkerInstance::STARTING
, last_worker_status_
);
147 if (status
!= SERVICE_WORKER_OK
&& !done_closure_
.is_null())
151 void StopOnIOThread() {
152 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
153 EXPECT_EQ(EmbeddedWorkerInstance::RUNNING
, worker_
->status());
155 ServiceWorkerStatusCode status
= worker_
->Stop();
157 last_worker_status_
= worker_
->status();
158 EXPECT_EQ(SERVICE_WORKER_OK
, status
);
159 EXPECT_EQ(EmbeddedWorkerInstance::STOPPING
, last_worker_status_
);
161 if (status
!= SERVICE_WORKER_OK
&& !done_closure_
.is_null())
166 // EmbeddedWorkerInstance::Observer overrides:
167 virtual void OnStarted() OVERRIDE
{
168 ASSERT_TRUE(worker_
!= NULL
);
169 ASSERT_FALSE(done_closure_
.is_null());
170 last_worker_status_
= worker_
->status();
171 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, done_closure_
);
173 virtual void OnStopped() OVERRIDE
{
174 ASSERT_TRUE(worker_
!= NULL
);
175 ASSERT_FALSE(done_closure_
.is_null());
176 last_worker_status_
= worker_
->status();
177 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, done_closure_
);
179 virtual void OnMessageReceived(
180 int request_id
, const IPC::Message
& message
) OVERRIDE
{
184 scoped_ptr
<EmbeddedWorkerInstance
> worker_
;
185 EmbeddedWorkerInstance::Status last_worker_status_
;
187 // Called by EmbeddedWorkerInstance::Observer overrides so that
188 // test code can wait for the worker status notifications.
189 base::Closure done_closure_
;
192 class ServiceWorkerVersionBrowserTest
: public ServiceWorkerBrowserTest
{
194 typedef ServiceWorkerVersionBrowserTest self
;
196 ServiceWorkerVersionBrowserTest() : next_registration_id_(1) {}
197 virtual ~ServiceWorkerVersionBrowserTest() {}
199 virtual void TearDownOnIOThread() OVERRIDE
{
201 registration_
->Shutdown();
202 registration_
= NULL
;
205 version_
->Shutdown();
210 void InstallTestHelper(const std::string
& worker_url
) {
211 RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread
, this,
214 // Dispatch install on a worker.
215 ServiceWorkerStatusCode status
= SERVICE_WORKER_ERROR_FAILED
;
216 base::RunLoop install_run_loop
;
217 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
218 base::Bind(&self::InstallOnIOThread
, this,
219 install_run_loop
.QuitClosure(),
221 install_run_loop
.Run();
222 ASSERT_EQ(SERVICE_WORKER_OK
, status
);
225 status
= SERVICE_WORKER_ERROR_FAILED
;
226 base::RunLoop stop_run_loop
;
227 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
228 base::Bind(&self::StopOnIOThread
, this,
229 stop_run_loop
.QuitClosure(),
232 ASSERT_EQ(SERVICE_WORKER_OK
, status
);
235 void SetUpRegistrationOnIOThread(const std::string
& worker_url
) {
236 const int64 version_id
= 1L;
237 registration_
= new ServiceWorkerRegistration(
238 embedded_test_server()->GetURL("/*"),
239 embedded_test_server()->GetURL(worker_url
),
240 next_registration_id_
++);
241 version_
= new ServiceWorkerVersion(
243 wrapper()->context()->embedded_worker_registry(),
245 AssociateRendererProcessToWorker(version_
->embedded_worker());
248 void StartOnIOThread(const base::Closure
& done
,
249 ServiceWorkerStatusCode
* result
) {
250 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
251 version_
->StartWorker(CreateReceiver(BrowserThread::UI
, done
, result
));
254 void InstallOnIOThread(const base::Closure
& done
,
255 ServiceWorkerStatusCode
* result
) {
256 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
257 version_
->DispatchInstallEvent(
258 -1, CreateReceiver(BrowserThread::UI
, done
, result
));
261 void FetchOnIOThread(const base::Closure
& done
,
262 ServiceWorkerStatusCode
* result
,
263 ServiceWorkerFetchResponse
* message
) {
264 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO
));
265 ServiceWorkerFetchRequest
request(
266 embedded_test_server()->GetURL("/service_worker/empty.html"),
268 std::map
<std::string
, std::string
>());
269 version_
->DispatchFetchEvent(
271 CreateFetchResponseReceiver(BrowserThread::UI
, done
, result
, message
));
275 void StopOnIOThread(const base::Closure
& done
,
276 ServiceWorkerStatusCode
* result
) {
277 ASSERT_TRUE(version_
);
278 version_
->StopWorker(CreateReceiver(BrowserThread::UI
, done
, result
));
282 int64 next_registration_id_
;
283 scoped_refptr
<ServiceWorkerRegistration
> registration_
;
284 scoped_refptr
<ServiceWorkerVersion
> version_
;
287 IN_PROC_BROWSER_TEST_F(EmbeddedWorkerBrowserTest
, StartAndStop
) {
288 // Start a worker and wait until OnStarted() is called.
289 base::RunLoop start_run_loop
;
290 done_closure_
= start_run_loop
.QuitClosure();
291 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
292 base::Bind(&self::StartOnIOThread
, this));
293 start_run_loop
.Run();
295 ASSERT_EQ(EmbeddedWorkerInstance::RUNNING
, last_worker_status_
);
297 // Stop a worker and wait until OnStopped() is called.
298 base::RunLoop stop_run_loop
;
299 done_closure_
= stop_run_loop
.QuitClosure();
300 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
301 base::Bind(&self::StopOnIOThread
, this));
304 ASSERT_EQ(EmbeddedWorkerInstance::STOPPED
, last_worker_status_
);
307 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest
, StartAndStop
) {
308 RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread
, this,
309 "/service_worker/worker.js"));
312 ServiceWorkerStatusCode status
= SERVICE_WORKER_ERROR_FAILED
;
313 base::RunLoop start_run_loop
;
314 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
315 base::Bind(&self::StartOnIOThread
, this,
316 start_run_loop
.QuitClosure(),
318 start_run_loop
.Run();
319 ASSERT_EQ(SERVICE_WORKER_OK
, status
);
322 status
= SERVICE_WORKER_ERROR_FAILED
;
323 base::RunLoop stop_run_loop
;
324 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
325 base::Bind(&self::StopOnIOThread
, this,
326 stop_run_loop
.QuitClosure(),
329 ASSERT_EQ(SERVICE_WORKER_OK
, status
);
332 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest
, StartNotFound
) {
333 RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread
, this,
334 "/service_worker/nonexistent.js"));
336 // Start a worker for nonexistent URL.
337 ServiceWorkerStatusCode status
= SERVICE_WORKER_ERROR_FAILED
;
338 base::RunLoop start_run_loop
;
339 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
340 base::Bind(&self::StartOnIOThread
, this,
341 start_run_loop
.QuitClosure(),
343 start_run_loop
.Run();
344 ASSERT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED
, status
);
347 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest
, Install
) {
348 InstallTestHelper("/service_worker/worker.js");
351 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest
,
352 InstallWithWaitUntil_Fulfilled
) {
353 InstallTestHelper("/service_worker/worker_install_fulfilled.js");
356 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest
,
357 InstallWithWaitUntil_Rejected
) {
358 // TODO(kinuko): This should also report back an error, but we
359 // don't have plumbing for it yet.
360 InstallTestHelper("/service_worker/worker_install_rejected.js");
363 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest
, Fetch
) {
364 RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread
, this,
365 "/service_worker/worker.js"));
367 ServiceWorkerStatusCode status
= SERVICE_WORKER_ERROR_FAILED
;
368 ServiceWorkerFetchResponse response
;
369 base::RunLoop fetch_run_loop
;
370 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
371 base::Bind(&self::FetchOnIOThread
, this,
372 fetch_run_loop
.QuitClosure(),
373 &status
, &response
));
374 fetch_run_loop
.Run();
375 ASSERT_EQ(SERVICE_WORKER_OK
, status
);
376 ASSERT_EQ(200, response
.status_code
);
377 ASSERT_EQ("OK", response
.status_text
);
378 ASSERT_EQ("GET", response
.method
);
379 std::map
<std::string
, std::string
> expected_headers
;
380 ASSERT_EQ(expected_headers
, response
.headers
);
383 } // namespace content