service_worker_browsertest.cc revision 46d4c2bc3267f3f028f39e7e311b0f89aba2e4fd
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.
4
5#include "base/bind.h"
6#include "base/callback.h"
7#include "base/command_line.h"
8#include "base/run_loop.h"
9#include "content/browser/fileapi/chrome_blob_storage_context.h"
10#include "content/browser/service_worker/embedded_worker_instance.h"
11#include "content/browser/service_worker/embedded_worker_registry.h"
12#include "content/browser/service_worker/service_worker_context_core.h"
13#include "content/browser/service_worker/service_worker_context_wrapper.h"
14#include "content/browser/service_worker/service_worker_registration.h"
15#include "content/browser/service_worker/service_worker_test_utils.h"
16#include "content/browser/service_worker/service_worker_version.h"
17#include "content/common/service_worker/service_worker_messages.h"
18#include "content/common/service_worker/service_worker_status_code.h"
19#include "content/common/service_worker/service_worker_types.h"
20#include "content/public/browser/browser_context.h"
21#include "content/public/browser/browser_thread.h"
22#include "content/public/browser/render_process_host.h"
23#include "content/public/browser/storage_partition.h"
24#include "content/public/browser/web_contents.h"
25#include "content/public/common/content_switches.h"
26#include "content/public/test/content_browser_test.h"
27#include "content/public/test/content_browser_test_utils.h"
28#include "content/shell/browser/shell.h"
29#include "net/test/embedded_test_server/embedded_test_server.h"
30#include "webkit/browser/blob/blob_data_handle.h"
31#include "webkit/browser/blob/blob_storage_context.h"
32#include "webkit/common/blob/blob_data.h"
33
34namespace content {
35
36namespace {
37
38struct FetchResult {
39  ServiceWorkerStatusCode status;
40  ServiceWorkerFetchEventResult result;
41  ServiceWorkerResponse response;
42};
43
44void RunAndQuit(const base::Closure& closure,
45                const base::Closure& quit,
46                base::MessageLoopProxy* original_message_loop) {
47  closure.Run();
48  original_message_loop->PostTask(FROM_HERE, quit);
49}
50
51void RunOnIOThread(const base::Closure& closure) {
52  base::RunLoop run_loop;
53  BrowserThread::PostTask(
54      BrowserThread::IO, FROM_HERE,
55      base::Bind(&RunAndQuit, closure, run_loop.QuitClosure(),
56                 base::MessageLoopProxy::current()));
57  run_loop.Run();
58}
59
60void RunOnIOThread(
61    const base::Callback<void(const base::Closure& continuation)>& closure) {
62  base::RunLoop run_loop;
63  base::Closure quit_on_original_thread =
64      base::Bind(base::IgnoreResult(&base::MessageLoopProxy::PostTask),
65                 base::MessageLoopProxy::current().get(),
66                 FROM_HERE,
67                 run_loop.QuitClosure());
68  BrowserThread::PostTask(BrowserThread::IO,
69                          FROM_HERE,
70                          base::Bind(closure, quit_on_original_thread));
71  run_loop.Run();
72}
73
74// Contrary to the style guide, the output parameter of this function comes
75// before input parameters so Bind can be used on it to create a FetchCallback
76// to pass to DispatchFetchEvent.
77void ReceiveFetchResult(BrowserThread::ID run_quit_thread,
78                        const base::Closure& quit,
79                        FetchResult* out_result,
80                        ServiceWorkerStatusCode actual_status,
81                        ServiceWorkerFetchEventResult actual_result,
82                        const ServiceWorkerResponse& actual_response) {
83  out_result->status = actual_status;
84  out_result->result = actual_result;
85  out_result->response = actual_response;
86  if (!quit.is_null())
87    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit);
88}
89
90ServiceWorkerVersion::FetchCallback CreateResponseReceiver(
91    BrowserThread::ID run_quit_thread,
92    const base::Closure& quit,
93    FetchResult* result) {
94  return base::Bind(&ReceiveFetchResult, run_quit_thread, quit, result);
95}
96
97void ReadResponseBody(std::string* body,
98                      scoped_refptr<ChromeBlobStorageContext> context,
99                      std::string blob_uuid) {
100  scoped_ptr<webkit_blob::BlobDataHandle> blob_data_handle =
101      context->context()->GetBlobDataFromUUID(blob_uuid);
102  ASSERT_EQ(1U, blob_data_handle->data()->items().size());
103  *body = std::string(blob_data_handle->data()->items()[0].bytes(),
104                      blob_data_handle->data()->items()[0].length());
105}
106
107}  // namespace
108
109class ServiceWorkerBrowserTest : public ContentBrowserTest {
110 protected:
111  typedef ServiceWorkerBrowserTest self;
112
113  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
114    command_line->AppendSwitch(switches::kEnableServiceWorker);
115  }
116
117  virtual void SetUpOnMainThread() OVERRIDE {
118    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
119    StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
120        shell()->web_contents()->GetBrowserContext());
121    wrapper_ = static_cast<ServiceWorkerContextWrapper*>(
122        partition->GetServiceWorkerContext());
123
124    // Navigate to the page to set up a renderer page (where we can embed
125    // a worker).
126    NavigateToURLBlockUntilNavigationsComplete(
127        shell(),
128        embedded_test_server()->GetURL("/service_worker/empty.html"), 1);
129
130    RunOnIOThread(base::Bind(&self::SetUpOnIOThread, this));
131  }
132
133  virtual void TearDownOnMainThread() OVERRIDE {
134    RunOnIOThread(base::Bind(&self::TearDownOnIOThread, this));
135    wrapper_ = NULL;
136  }
137
138  virtual void SetUpOnIOThread() {}
139  virtual void TearDownOnIOThread() {}
140
141  ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
142  ServiceWorkerContext* public_context() { return wrapper(); }
143
144  void AssociateRendererProcessToWorker(EmbeddedWorkerInstance* worker) {
145    worker->AddProcessReference(
146        shell()->web_contents()->GetRenderProcessHost()->GetID());
147  }
148
149 private:
150  scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
151};
152
153class EmbeddedWorkerBrowserTest : public ServiceWorkerBrowserTest,
154                                  public EmbeddedWorkerInstance::Listener {
155 public:
156  typedef EmbeddedWorkerBrowserTest self;
157
158  EmbeddedWorkerBrowserTest()
159      : last_worker_status_(EmbeddedWorkerInstance::STOPPED) {}
160  virtual ~EmbeddedWorkerBrowserTest() {}
161
162  virtual void TearDownOnIOThread() OVERRIDE {
163    if (worker_) {
164      worker_->RemoveListener(this);
165      worker_.reset();
166    }
167  }
168
169  void StartOnIOThread() {
170    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
171    worker_ = wrapper()->context()->embedded_worker_registry()->CreateWorker();
172    EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker_->status());
173    worker_->AddListener(this);
174
175    AssociateRendererProcessToWorker(worker_.get());
176
177    const int64 service_worker_version_id = 33L;
178    const GURL scope = embedded_test_server()->GetURL("/*");
179    const GURL script_url = embedded_test_server()->GetURL(
180        "/service_worker/worker.js");
181    std::vector<int> processes;
182    processes.push_back(
183        shell()->web_contents()->GetRenderProcessHost()->GetID());
184    worker_->Start(
185        service_worker_version_id,
186        scope,
187        script_url,
188        processes,
189        base::Bind(&EmbeddedWorkerBrowserTest::StartOnIOThread2, this));
190  }
191  void StartOnIOThread2(ServiceWorkerStatusCode status) {
192    last_worker_status_ = worker_->status();
193    EXPECT_EQ(SERVICE_WORKER_OK, status);
194    EXPECT_EQ(EmbeddedWorkerInstance::STARTING, last_worker_status_);
195
196    if (status != SERVICE_WORKER_OK && !done_closure_.is_null())
197      done_closure_.Run();
198  }
199
200  void StopOnIOThread() {
201    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
202    EXPECT_EQ(EmbeddedWorkerInstance::RUNNING, worker_->status());
203
204    ServiceWorkerStatusCode status = worker_->Stop();
205
206    last_worker_status_ = worker_->status();
207    EXPECT_EQ(SERVICE_WORKER_OK, status);
208    EXPECT_EQ(EmbeddedWorkerInstance::STOPPING, last_worker_status_);
209
210    if (status != SERVICE_WORKER_OK && !done_closure_.is_null())
211      done_closure_.Run();
212  }
213
214 protected:
215  // EmbeddedWorkerInstance::Observer overrides:
216  virtual void OnStarted() OVERRIDE {
217    ASSERT_TRUE(worker_ != NULL);
218    ASSERT_FALSE(done_closure_.is_null());
219    last_worker_status_ = worker_->status();
220    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_closure_);
221  }
222  virtual void OnStopped() OVERRIDE {
223    ASSERT_TRUE(worker_ != NULL);
224    ASSERT_FALSE(done_closure_.is_null());
225    last_worker_status_ = worker_->status();
226    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_closure_);
227  }
228  virtual void OnReportException(const base::string16& error_message,
229                                 int line_number,
230                                 int column_number,
231                                 const GURL& source_url) OVERRIDE {}
232  virtual void OnReportConsoleMessage(int source_identifier,
233                                      int message_level,
234                                      const base::string16& message,
235                                      int line_number,
236                                      const GURL& source_url) OVERRIDE {}
237  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
238    return false;
239  }
240
241  scoped_ptr<EmbeddedWorkerInstance> worker_;
242  EmbeddedWorkerInstance::Status last_worker_status_;
243
244  // Called by EmbeddedWorkerInstance::Observer overrides so that
245  // test code can wait for the worker status notifications.
246  base::Closure done_closure_;
247};
248
249class ServiceWorkerVersionBrowserTest : public ServiceWorkerBrowserTest {
250 public:
251  typedef ServiceWorkerVersionBrowserTest self;
252
253  virtual ~ServiceWorkerVersionBrowserTest() {}
254
255  virtual void TearDownOnIOThread() OVERRIDE {
256    registration_ = NULL;
257    version_ = NULL;
258  }
259
260  void InstallTestHelper(const std::string& worker_url,
261                         ServiceWorkerStatusCode expected_status) {
262    RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
263                             worker_url));
264
265    // Dispatch install on a worker.
266    ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
267    base::RunLoop install_run_loop;
268    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
269                            base::Bind(&self::InstallOnIOThread, this,
270                                       install_run_loop.QuitClosure(),
271                                       &status));
272    install_run_loop.Run();
273    ASSERT_EQ(expected_status, status);
274
275    // Stop the worker.
276    status = SERVICE_WORKER_ERROR_FAILED;
277    base::RunLoop stop_run_loop;
278    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
279                            base::Bind(&self::StopOnIOThread, this,
280                                       stop_run_loop.QuitClosure(),
281                                       &status));
282    stop_run_loop.Run();
283    ASSERT_EQ(SERVICE_WORKER_OK, status);
284  }
285
286  void ActivateTestHelper(
287      const std::string& worker_url,
288      ServiceWorkerStatusCode expected_status) {
289    RunOnIOThread(
290        base::Bind(&self::SetUpRegistrationOnIOThread, this, worker_url));
291    version_->SetStatus(ServiceWorkerVersion::INSTALLED);
292    ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
293    base::RunLoop run_loop;
294    BrowserThread::PostTask(
295        BrowserThread::IO,
296        FROM_HERE,
297        base::Bind(
298            &self::ActivateOnIOThread, this, run_loop.QuitClosure(), &status));
299    run_loop.Run();
300    ASSERT_EQ(expected_status, status);
301  }
302
303  void FetchOnRegisteredWorker(ServiceWorkerFetchEventResult* result,
304                               ServiceWorkerResponse* response) {
305    FetchResult fetch_result;
306    fetch_result.status = SERVICE_WORKER_ERROR_FAILED;
307    base::RunLoop fetch_run_loop;
308    BrowserThread::PostTask(BrowserThread::IO,
309                            FROM_HERE,
310                            base::Bind(&self::FetchOnIOThread,
311                                       this,
312                                       fetch_run_loop.QuitClosure(),
313                                       &fetch_result));
314    fetch_run_loop.Run();
315    *result = fetch_result.result;
316    *response = fetch_result.response;
317    ASSERT_EQ(SERVICE_WORKER_OK, fetch_result.status);
318  }
319
320  void FetchTestHelper(const std::string& worker_url,
321                       ServiceWorkerFetchEventResult* result,
322                       ServiceWorkerResponse* response) {
323    RunOnIOThread(
324        base::Bind(&self::SetUpRegistrationOnIOThread, this, worker_url));
325
326    FetchOnRegisteredWorker(result, response);
327  }
328
329  void SetUpRegistrationOnIOThread(const std::string& worker_url) {
330    registration_ = new ServiceWorkerRegistration(
331        embedded_test_server()->GetURL("/*"),
332        embedded_test_server()->GetURL(worker_url),
333        wrapper()->context()->storage()->NewRegistrationId(),
334        wrapper()->context()->AsWeakPtr());
335    version_ = new ServiceWorkerVersion(
336        registration_,
337        wrapper()->context()->storage()->NewVersionId(),
338        wrapper()->context()->AsWeakPtr());
339    AssociateRendererProcessToWorker(version_->embedded_worker());
340  }
341
342  void StartOnIOThread(const base::Closure& done,
343                       ServiceWorkerStatusCode* result) {
344    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
345    version_->StartWorker(CreateReceiver(BrowserThread::UI, done, result));
346  }
347
348  void InstallOnIOThread(const base::Closure& done,
349                         ServiceWorkerStatusCode* result) {
350    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
351    version_->DispatchInstallEvent(
352        -1, CreateReceiver(BrowserThread::UI, done, result));
353  }
354
355  void ActivateOnIOThread(const base::Closure& done,
356                          ServiceWorkerStatusCode* result) {
357    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
358    version_->SetStatus(ServiceWorkerVersion::INSTALLED);
359    version_->DispatchActivateEvent(
360        CreateReceiver(BrowserThread::UI, done, result));
361  }
362
363  void FetchOnIOThread(const base::Closure& done, FetchResult* result) {
364    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
365    ServiceWorkerFetchRequest request(
366        embedded_test_server()->GetURL("/service_worker/empty.html"),
367        "GET",
368        std::map<std::string, std::string>());
369    version_->SetStatus(ServiceWorkerVersion::ACTIVE);
370    version_->DispatchFetchEvent(
371        request, CreateResponseReceiver(BrowserThread::UI, done, result));
372  }
373
374  void StopOnIOThread(const base::Closure& done,
375                      ServiceWorkerStatusCode* result) {
376    ASSERT_TRUE(version_);
377    version_->StopWorker(CreateReceiver(BrowserThread::UI, done, result));
378  }
379
380  void SyncEventOnIOThread(const base::Closure& done,
381                           ServiceWorkerStatusCode* result) {
382    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
383    version_->SetStatus(ServiceWorkerVersion::ACTIVE);
384    version_->DispatchSyncEvent(
385        CreateReceiver(BrowserThread::UI, done, result));
386  }
387
388 protected:
389  scoped_refptr<ServiceWorkerRegistration> registration_;
390  scoped_refptr<ServiceWorkerVersion> version_;
391};
392
393IN_PROC_BROWSER_TEST_F(EmbeddedWorkerBrowserTest, StartAndStop) {
394  // Start a worker and wait until OnStarted() is called.
395  base::RunLoop start_run_loop;
396  done_closure_ = start_run_loop.QuitClosure();
397  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
398                          base::Bind(&self::StartOnIOThread, this));
399  start_run_loop.Run();
400
401  ASSERT_EQ(EmbeddedWorkerInstance::RUNNING, last_worker_status_);
402
403  // Stop a worker and wait until OnStopped() is called.
404  base::RunLoop stop_run_loop;
405  done_closure_ = stop_run_loop.QuitClosure();
406  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
407                          base::Bind(&self::StopOnIOThread, this));
408  stop_run_loop.Run();
409
410  ASSERT_EQ(EmbeddedWorkerInstance::STOPPED, last_worker_status_);
411}
412
413IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartAndStop) {
414  RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
415                           "/service_worker/worker.js"));
416
417  // Start a worker.
418  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
419  base::RunLoop start_run_loop;
420  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
421                          base::Bind(&self::StartOnIOThread, this,
422                                     start_run_loop.QuitClosure(),
423                                     &status));
424  start_run_loop.Run();
425  ASSERT_EQ(SERVICE_WORKER_OK, status);
426
427  // Stop the worker.
428  status = SERVICE_WORKER_ERROR_FAILED;
429  base::RunLoop stop_run_loop;
430  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
431                          base::Bind(&self::StopOnIOThread, this,
432                                     stop_run_loop.QuitClosure(),
433                                     &status));
434  stop_run_loop.Run();
435  ASSERT_EQ(SERVICE_WORKER_OK, status);
436}
437
438IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartNotFound) {
439  RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
440                           "/service_worker/nonexistent.js"));
441
442  // Start a worker for nonexistent URL.
443  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
444  base::RunLoop start_run_loop;
445  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
446                          base::Bind(&self::StartOnIOThread, this,
447                                     start_run_loop.QuitClosure(),
448                                     &status));
449  start_run_loop.Run();
450  ASSERT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, status);
451}
452
453IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Install) {
454  InstallTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
455}
456
457IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
458                       InstallWithWaitUntil_Fulfilled) {
459  InstallTestHelper("/service_worker/worker_install_fulfilled.js",
460                    SERVICE_WORKER_OK);
461}
462
463IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
464                       Activate_NoEventListener) {
465  ActivateTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
466  ASSERT_EQ(ServiceWorkerVersion::ACTIVE, version_->status());
467}
468
469IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Activate_Rejected) {
470  ActivateTestHelper("/service_worker/worker_activate_rejected.js",
471                     SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED);
472}
473
474IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
475                       InstallWithWaitUntil_Rejected) {
476  InstallTestHelper("/service_worker/worker_install_rejected.js",
477                    SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
478}
479
480IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchEvent_Response) {
481  ServiceWorkerFetchEventResult result;
482  ServiceWorkerResponse response;
483  FetchTestHelper("/service_worker/fetch_event.js", &result, &response);
484  ASSERT_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, result);
485  EXPECT_EQ(301, response.status_code);
486  EXPECT_EQ("Moved Permanently", response.status_text);
487  std::map<std::string, std::string> expected_headers;
488  expected_headers["Content-Language"] = "fi";
489  expected_headers["Content-Type"] = "text/html; charset=UTF-8";
490  EXPECT_EQ(expected_headers, response.headers);
491
492  scoped_refptr<ChromeBlobStorageContext> context =
493      ChromeBlobStorageContext::GetFor(
494          shell()->web_contents()->GetBrowserContext());
495  std::string body;
496  RunOnIOThread(
497      base::Bind(&ReadResponseBody, &body, context, response.blob_uuid));
498  EXPECT_EQ("This resource is gone. Gone, gone, gone.", body);
499}
500
501IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
502                       SyncAbortedWithoutFlag) {
503  RunOnIOThread(base::Bind(
504      &self::SetUpRegistrationOnIOThread, this, "/service_worker/sync.js"));
505
506  // Run the sync event.
507  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
508  base::RunLoop sync_run_loop;
509  BrowserThread::PostTask(BrowserThread::IO,
510                          FROM_HERE,
511                          base::Bind(&self::SyncEventOnIOThread,
512                                     this,
513                                     sync_run_loop.QuitClosure(),
514                                     &status));
515  sync_run_loop.Run();
516  ASSERT_EQ(SERVICE_WORKER_ERROR_ABORT, status);
517}
518
519IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, SyncEventHandled) {
520  CommandLine* command_line = CommandLine::ForCurrentProcess();
521  command_line->AppendSwitch(switches::kEnableServiceWorkerSync);
522
523  RunOnIOThread(base::Bind(
524      &self::SetUpRegistrationOnIOThread, this, "/service_worker/sync.js"));
525  ServiceWorkerFetchEventResult result;
526  ServiceWorkerResponse response;
527
528  // Should 404 before sync event.
529  FetchOnRegisteredWorker(&result, &response);
530  EXPECT_EQ(404, response.status_code);
531
532  // Run the sync event.
533  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
534  base::RunLoop sync_run_loop;
535  BrowserThread::PostTask(BrowserThread::IO,
536                          FROM_HERE,
537                          base::Bind(&self::SyncEventOnIOThread,
538                                     this,
539                                     sync_run_loop.QuitClosure(),
540                                     &status));
541  sync_run_loop.Run();
542  ASSERT_EQ(SERVICE_WORKER_OK, status);
543
544  // Should 200 after sync event.
545  FetchOnRegisteredWorker(&result, &response);
546  EXPECT_EQ(200, response.status_code);
547}
548
549class ServiceWorkerBlackBoxBrowserTest : public ServiceWorkerBrowserTest {
550 public:
551  typedef ServiceWorkerBlackBoxBrowserTest self;
552
553  static void ExpectResultAndRun(bool expected,
554                                 const base::Closure& continuation,
555                                 bool actual) {
556    EXPECT_EQ(expected, actual);
557    continuation.Run();
558  }
559
560  void FindRegistrationOnIO(const GURL& document_url,
561                            ServiceWorkerStatusCode* status,
562                            GURL* script_url,
563                            const base::Closure& continuation) {
564    wrapper()->context()->storage()->FindRegistrationForDocument(
565        document_url,
566        base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO2,
567                   this,
568                   status,
569                   script_url,
570                   continuation));
571  }
572
573  void FindRegistrationOnIO2(
574      ServiceWorkerStatusCode* out_status,
575      GURL* script_url,
576      const base::Closure& continuation,
577      ServiceWorkerStatusCode status,
578      const scoped_refptr<ServiceWorkerRegistration>& registration) {
579    *out_status = status;
580    if (registration) {
581      *script_url = registration->script_url();
582    } else {
583      EXPECT_NE(SERVICE_WORKER_OK, status);
584    }
585    continuation.Run();
586  }
587};
588
589static int CountRenderProcessHosts() {
590  int result = 0;
591  for (RenderProcessHost::iterator iter(RenderProcessHost::AllHostsIterator());
592       !iter.IsAtEnd();
593       iter.Advance()) {
594    result++;
595  }
596  return result;
597}
598
599IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, Registration) {
600  // Close the only window to be sure we're not re-using its RenderProcessHost.
601  shell()->Close();
602  EXPECT_EQ(0, CountRenderProcessHosts());
603
604  const std::string kWorkerUrl = "/service_worker/fetch_event.js";
605
606  // Unregistering nothing should return true.
607  {
608    base::RunLoop run_loop;
609    public_context()->UnregisterServiceWorker(
610        embedded_test_server()->GetURL("/*"),
611        base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
612                   true,
613                   run_loop.QuitClosure()));
614    run_loop.Run();
615  }
616
617  // If we use a worker URL that doesn't exist, registration fails.
618  {
619    base::RunLoop run_loop;
620    public_context()->RegisterServiceWorker(
621        embedded_test_server()->GetURL("/*"),
622        embedded_test_server()->GetURL("/does/not/exist"),
623        base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
624                   false,
625                   run_loop.QuitClosure()));
626    run_loop.Run();
627  }
628  EXPECT_EQ(0, CountRenderProcessHosts());
629
630  // Register returns when the promise would be resolved.
631  {
632    base::RunLoop run_loop;
633    public_context()->RegisterServiceWorker(
634        embedded_test_server()->GetURL("/*"),
635        embedded_test_server()->GetURL(kWorkerUrl),
636        base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
637                   true,
638                   run_loop.QuitClosure()));
639    run_loop.Run();
640  }
641  EXPECT_EQ(1, CountRenderProcessHosts());
642
643  // Registering again should succeed, although the algo still
644  // might not be complete.
645  {
646    base::RunLoop run_loop;
647    public_context()->RegisterServiceWorker(
648        embedded_test_server()->GetURL("/*"),
649        embedded_test_server()->GetURL(kWorkerUrl),
650        base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
651                   true,
652                   run_loop.QuitClosure()));
653    run_loop.Run();
654  }
655
656  // The registration algo might not be far enough along to have
657  // stored the registration data, so it may not be findable
658  // at this point.
659
660  // Unregistering something should return true.
661  {
662    base::RunLoop run_loop;
663    public_context()->UnregisterServiceWorker(
664        embedded_test_server()->GetURL("/*"),
665        base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
666                   true,
667                   run_loop.QuitClosure()));
668    run_loop.Run();
669  }
670  EXPECT_GE(1, CountRenderProcessHosts()) << "Unregistering doesn't stop the "
671                                             "workers eagerly, so their RPHs "
672                                             "can still be running.";
673
674  // Should not be able to find it.
675  {
676    ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
677    GURL script_url;
678    RunOnIOThread(
679        base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO,
680                   this,
681                   embedded_test_server()->GetURL("/service_worker/empty.html"),
682                   &status,
683                   &script_url));
684    EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND, status);
685  }
686}
687
688}  // namespace content
689