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 "base/strings/utf_string_conversions.h"
10#include "content/browser/fileapi/chrome_blob_storage_context.h"
11#include "content/browser/service_worker/embedded_worker_instance.h"
12#include "content/browser/service_worker/embedded_worker_registry.h"
13#include "content/browser/service_worker/service_worker_context_core.h"
14#include "content/browser/service_worker/service_worker_context_observer.h"
15#include "content/browser/service_worker/service_worker_context_wrapper.h"
16#include "content/browser/service_worker/service_worker_registration.h"
17#include "content/browser/service_worker/service_worker_test_utils.h"
18#include "content/browser/service_worker/service_worker_version.h"
19#include "content/common/service_worker/service_worker_messages.h"
20#include "content/common/service_worker/service_worker_status_code.h"
21#include "content/common/service_worker/service_worker_types.h"
22#include "content/public/browser/browser_context.h"
23#include "content/public/browser/browser_thread.h"
24#include "content/public/browser/render_process_host.h"
25#include "content/public/browser/storage_partition.h"
26#include "content/public/browser/web_contents.h"
27#include "content/public/common/content_switches.h"
28#include "content/public/test/browser_test_utils.h"
29#include "content/public/test/content_browser_test.h"
30#include "content/public/test/content_browser_test_utils.h"
31#include "content/shell/browser/shell.h"
32#include "net/test/embedded_test_server/embedded_test_server.h"
33#include "net/test/embedded_test_server/http_request.h"
34#include "net/test/embedded_test_server/http_response.h"
35#include "net/url_request/url_request_filter.h"
36#include "net/url_request/url_request_interceptor.h"
37#include "net/url_request/url_request_test_job.h"
38#include "storage/browser/blob/blob_data_handle.h"
39#include "storage/browser/blob/blob_storage_context.h"
40#include "storage/common/blob/blob_data.h"
41
42namespace content {
43
44namespace {
45
46struct FetchResult {
47  ServiceWorkerStatusCode status;
48  ServiceWorkerFetchEventResult result;
49  ServiceWorkerResponse response;
50  scoped_ptr<storage::BlobDataHandle> blob_data_handle;
51};
52
53void RunAndQuit(const base::Closure& closure,
54                const base::Closure& quit,
55                base::MessageLoopProxy* original_message_loop) {
56  closure.Run();
57  original_message_loop->PostTask(FROM_HERE, quit);
58}
59
60void RunOnIOThread(const base::Closure& closure) {
61  base::RunLoop run_loop;
62  BrowserThread::PostTask(
63      BrowserThread::IO, FROM_HERE,
64      base::Bind(&RunAndQuit, closure, run_loop.QuitClosure(),
65                 base::MessageLoopProxy::current()));
66  run_loop.Run();
67}
68
69void RunOnIOThread(
70    const base::Callback<void(const base::Closure& continuation)>& closure) {
71  base::RunLoop run_loop;
72  base::Closure quit_on_original_thread =
73      base::Bind(base::IgnoreResult(&base::MessageLoopProxy::PostTask),
74                 base::MessageLoopProxy::current().get(),
75                 FROM_HERE,
76                 run_loop.QuitClosure());
77  BrowserThread::PostTask(BrowserThread::IO,
78                          FROM_HERE,
79                          base::Bind(closure, quit_on_original_thread));
80  run_loop.Run();
81}
82
83void ReceivePrepareResult(bool* is_prepared) {
84  *is_prepared = true;
85}
86
87base::Closure CreatePrepareReceiver(bool* is_prepared) {
88  return base::Bind(&ReceivePrepareResult, is_prepared);
89}
90
91// Contrary to the style guide, the output parameter of this function comes
92// before input parameters so Bind can be used on it to create a FetchCallback
93// to pass to DispatchFetchEvent.
94void ReceiveFetchResult(BrowserThread::ID run_quit_thread,
95                        const base::Closure& quit,
96                        ChromeBlobStorageContext* blob_context,
97                        FetchResult* out_result,
98                        ServiceWorkerStatusCode actual_status,
99                        ServiceWorkerFetchEventResult actual_result,
100                        const ServiceWorkerResponse& actual_response) {
101  out_result->status = actual_status;
102  out_result->result = actual_result;
103  out_result->response = actual_response;
104  if (!actual_response.blob_uuid.empty()) {
105    out_result->blob_data_handle =
106        blob_context->context()->GetBlobDataFromUUID(
107            actual_response.blob_uuid);
108  }
109  if (!quit.is_null())
110    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, quit);
111}
112
113ServiceWorkerVersion::FetchCallback CreateResponseReceiver(
114    BrowserThread::ID run_quit_thread,
115    const base::Closure& quit,
116    ChromeBlobStorageContext* blob_context,
117    FetchResult* result) {
118  return base::Bind(&ReceiveFetchResult, run_quit_thread, quit,
119                    make_scoped_refptr<ChromeBlobStorageContext>(blob_context),
120                    result);
121}
122
123void ReadResponseBody(std::string* body,
124                      storage::BlobDataHandle* blob_data_handle) {
125  ASSERT_TRUE(blob_data_handle);
126  ASSERT_EQ(1U, blob_data_handle->data()->items().size());
127  *body = std::string(blob_data_handle->data()->items()[0].bytes(),
128                      blob_data_handle->data()->items()[0].length());
129}
130
131void ExpectResultAndRun(bool expected,
132                        const base::Closure& continuation,
133                        bool actual) {
134  EXPECT_EQ(expected, actual);
135  continuation.Run();
136}
137
138class WorkerActivatedObserver
139    : public ServiceWorkerContextObserver,
140      public base::RefCountedThreadSafe<WorkerActivatedObserver> {
141 public:
142  explicit WorkerActivatedObserver(ServiceWorkerContextWrapper* context)
143      : context_(context) {}
144  void Init() {
145    RunOnIOThread(base::Bind(&WorkerActivatedObserver::InitOnIOThread, this));
146  }
147  // ServiceWorkerContextObserver overrides.
148  virtual void OnVersionStateChanged(int64 version_id) OVERRIDE {
149    DCHECK_CURRENTLY_ON(BrowserThread::IO);
150    const ServiceWorkerVersion* version =
151        context_->context()->GetLiveVersion(version_id);
152    if (version->status() == ServiceWorkerVersion::ACTIVATED) {
153      context_->RemoveObserver(this);
154      BrowserThread::PostTask(BrowserThread::UI,
155                              FROM_HERE,
156                              base::Bind(&WorkerActivatedObserver::Quit, this));
157    }
158  }
159  void Wait() { run_loop_.Run(); }
160
161 private:
162  friend class base::RefCountedThreadSafe<WorkerActivatedObserver>;
163  virtual ~WorkerActivatedObserver() {}
164  void InitOnIOThread() { context_->AddObserver(this); }
165  void Quit() { run_loop_.Quit(); }
166
167  base::RunLoop run_loop_;
168  ServiceWorkerContextWrapper* context_;
169  DISALLOW_COPY_AND_ASSIGN(WorkerActivatedObserver);
170};
171
172scoped_ptr<net::test_server::HttpResponse> VerifyServiceWorkerHeaderInRequest(
173    const net::test_server::HttpRequest& request) {
174  EXPECT_EQ(request.relative_url, "/service_worker/generated_sw.js");
175  std::map<std::string, std::string>::const_iterator it =
176      request.headers.find("Service-Worker");
177  EXPECT_TRUE(it != request.headers.end());
178  EXPECT_EQ("script", it->second);
179
180  scoped_ptr<net::test_server::BasicHttpResponse> http_response(
181      new net::test_server::BasicHttpResponse());
182  http_response->set_content_type("text/javascript");
183  return http_response.PassAs<net::test_server::HttpResponse>();
184}
185
186// The ImportsBustMemcache test requires that the imported script
187// would naturally be cached in blink's memcache, but the embedded
188// test server doesn't produce headers that allow the blink's memcache
189// to do that. This interceptor injects headers that give the import
190// an experiration far in the future.
191class LongLivedResourceInterceptor : public net::URLRequestInterceptor {
192 public:
193  LongLivedResourceInterceptor(const std::string& body)
194      : body_(body) {}
195  virtual ~LongLivedResourceInterceptor() {}
196
197  // net::URLRequestInterceptor implementation
198  virtual net::URLRequestJob* MaybeInterceptRequest(
199      net::URLRequest* request,
200      net::NetworkDelegate* network_delegate) const OVERRIDE {
201    const char kHeaders[] =
202        "HTTP/1.1 200 OK\0"
203        "Content-Type: text/javascript\0"
204        "Expires: Thu, 1 Jan 2100 20:00:00 GMT\0"
205        "\0";
206    std::string headers(kHeaders, arraysize(kHeaders));
207    return new net::URLRequestTestJob(
208        request, network_delegate, headers, body_, true);
209  }
210
211 private:
212  std::string body_;
213  DISALLOW_COPY_AND_ASSIGN(LongLivedResourceInterceptor);
214};
215
216void CreateLongLivedResourceInterceptors(
217    const GURL& worker_url, const GURL& import_url) {
218  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
219  scoped_ptr<net::URLRequestInterceptor> interceptor;
220
221  interceptor.reset(new LongLivedResourceInterceptor(
222      "importScripts('long_lived_import.js');"));
223  net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
224      worker_url, interceptor.Pass());
225
226  interceptor.reset(new LongLivedResourceInterceptor(
227      "// the imported script does nothing"));
228  net::URLRequestFilter::GetInstance()->AddUrlInterceptor(
229      import_url, interceptor.Pass());
230}
231
232void CountScriptResources(
233    ServiceWorkerContextWrapper* wrapper,
234    const GURL& scope,
235    int* num_resources) {
236  *num_resources = -1;
237
238  std::vector<ServiceWorkerRegistrationInfo> infos =
239     wrapper->context()->GetAllLiveRegistrationInfo();
240  if (infos.empty())
241    return;
242
243  int version_id;
244  size_t index = infos.size() - 1;
245  if (!infos[index].installing_version.is_null)
246    version_id = infos[index].installing_version.version_id;
247  else if (!infos[index].waiting_version.is_null)
248    version_id = infos[1].waiting_version.version_id;
249  else if (!infos[index].active_version.is_null)
250    version_id = infos[index].active_version.version_id;
251  else
252    return;
253
254  ServiceWorkerVersion* version =
255      wrapper->context()->GetLiveVersion(version_id);
256  *num_resources = static_cast<int>(version->script_cache_map()->size());
257}
258
259}  // namespace
260
261class ServiceWorkerBrowserTest : public ContentBrowserTest {
262 protected:
263  typedef ServiceWorkerBrowserTest self;
264
265  virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE {
266    command_line->AppendSwitch(
267        switches::kEnableExperimentalWebPlatformFeatures);
268  }
269
270  virtual void SetUpOnMainThread() OVERRIDE {
271    ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
272    StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
273        shell()->web_contents()->GetBrowserContext());
274    wrapper_ = static_cast<ServiceWorkerContextWrapper*>(
275        partition->GetServiceWorkerContext());
276
277    // Navigate to the page to set up a renderer page (where we can embed
278    // a worker).
279    NavigateToURLBlockUntilNavigationsComplete(
280        shell(),
281        embedded_test_server()->GetURL("/service_worker/empty.html"), 1);
282
283    RunOnIOThread(base::Bind(&self::SetUpOnIOThread, this));
284  }
285
286  virtual void TearDownOnMainThread() OVERRIDE {
287    RunOnIOThread(base::Bind(&self::TearDownOnIOThread, this));
288    wrapper_ = NULL;
289  }
290
291  virtual void SetUpOnIOThread() {}
292  virtual void TearDownOnIOThread() {}
293
294  ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
295  ServiceWorkerContext* public_context() { return wrapper(); }
296
297  void AssociateRendererProcessToPattern(const GURL& pattern) {
298    wrapper_->process_manager()->AddProcessReferenceToPattern(
299        pattern, shell()->web_contents()->GetRenderProcessHost()->GetID());
300  }
301
302 private:
303  scoped_refptr<ServiceWorkerContextWrapper> wrapper_;
304};
305
306class EmbeddedWorkerBrowserTest : public ServiceWorkerBrowserTest,
307                                  public EmbeddedWorkerInstance::Listener {
308 public:
309  typedef EmbeddedWorkerBrowserTest self;
310
311  EmbeddedWorkerBrowserTest()
312      : last_worker_status_(EmbeddedWorkerInstance::STOPPED),
313        pause_mode_(DONT_PAUSE) {}
314  virtual ~EmbeddedWorkerBrowserTest() {}
315
316  virtual void TearDownOnIOThread() OVERRIDE {
317    if (worker_) {
318      worker_->RemoveListener(this);
319      worker_.reset();
320    }
321  }
322
323  void StartOnIOThread() {
324    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
325    worker_ = wrapper()->context()->embedded_worker_registry()->CreateWorker();
326    EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker_->status());
327    worker_->AddListener(this);
328
329
330    const int64 service_worker_version_id = 33L;
331    const GURL pattern = embedded_test_server()->GetURL("/");
332    const GURL script_url = embedded_test_server()->GetURL(
333        "/service_worker/worker.js");
334    AssociateRendererProcessToPattern(pattern);
335    int process_id = shell()->web_contents()->GetRenderProcessHost()->GetID();
336    wrapper()->process_manager()->AddProcessReferenceToPattern(
337        pattern, process_id);
338    worker_->Start(
339        service_worker_version_id,
340        pattern,
341        script_url,
342        pause_mode_ != DONT_PAUSE,
343        base::Bind(&EmbeddedWorkerBrowserTest::StartOnIOThread2, this));
344  }
345  void StartOnIOThread2(ServiceWorkerStatusCode status) {
346    last_worker_status_ = worker_->status();
347    EXPECT_EQ(SERVICE_WORKER_OK, status);
348    EXPECT_EQ(EmbeddedWorkerInstance::STARTING, last_worker_status_);
349
350    if (status != SERVICE_WORKER_OK && !done_closure_.is_null())
351      done_closure_.Run();
352  }
353
354  void StopOnIOThread() {
355    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
356    EXPECT_EQ(EmbeddedWorkerInstance::RUNNING, worker_->status());
357
358    ServiceWorkerStatusCode status = worker_->Stop();
359
360    last_worker_status_ = worker_->status();
361    EXPECT_EQ(SERVICE_WORKER_OK, status);
362    EXPECT_EQ(EmbeddedWorkerInstance::STOPPING, last_worker_status_);
363
364    if (status != SERVICE_WORKER_OK && !done_closure_.is_null())
365      done_closure_.Run();
366  }
367
368 protected:
369  // EmbeddedWorkerInstance::Observer overrides:
370  virtual void OnStarted() OVERRIDE {
371    ASSERT_TRUE(worker_ != NULL);
372    ASSERT_FALSE(done_closure_.is_null());
373    last_worker_status_ = worker_->status();
374    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_closure_);
375  }
376  virtual void OnStopped() OVERRIDE {
377    ASSERT_TRUE(worker_ != NULL);
378    ASSERT_FALSE(done_closure_.is_null());
379    last_worker_status_ = worker_->status();
380    BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, done_closure_);
381  }
382  virtual void OnPausedAfterDownload() OVERRIDE {
383    if (pause_mode_ == PAUSE_THEN_RESUME)
384      worker_->ResumeAfterDownload();
385    else if (pause_mode_ == PAUSE_THEN_STOP)
386      worker_->Stop();
387    else
388      ASSERT_TRUE(false);
389  }
390  virtual void OnReportException(const base::string16& error_message,
391                                 int line_number,
392                                 int column_number,
393                                 const GURL& source_url) OVERRIDE {}
394  virtual void OnReportConsoleMessage(int source_identifier,
395                                      int message_level,
396                                      const base::string16& message,
397                                      int line_number,
398                                      const GURL& source_url) OVERRIDE {}
399  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
400    return false;
401  }
402
403  scoped_ptr<EmbeddedWorkerInstance> worker_;
404  EmbeddedWorkerInstance::Status last_worker_status_;
405
406  enum {
407    DONT_PAUSE,
408    PAUSE_THEN_RESUME,
409    PAUSE_THEN_STOP,
410  } pause_mode_;
411
412  // Called by EmbeddedWorkerInstance::Observer overrides so that
413  // test code can wait for the worker status notifications.
414  base::Closure done_closure_;
415};
416
417class ServiceWorkerVersionBrowserTest : public ServiceWorkerBrowserTest {
418 public:
419  typedef ServiceWorkerVersionBrowserTest self;
420
421  virtual ~ServiceWorkerVersionBrowserTest() {}
422
423  virtual void TearDownOnIOThread() OVERRIDE {
424    registration_ = NULL;
425    version_ = NULL;
426  }
427
428  void InstallTestHelper(const std::string& worker_url,
429                         ServiceWorkerStatusCode expected_status) {
430    RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
431                             worker_url));
432
433    // Dispatch install on a worker.
434    ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
435    base::RunLoop install_run_loop;
436    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
437                            base::Bind(&self::InstallOnIOThread, this,
438                                       install_run_loop.QuitClosure(),
439                                       &status));
440    install_run_loop.Run();
441    ASSERT_EQ(expected_status, status);
442
443    // Stop the worker.
444    status = SERVICE_WORKER_ERROR_FAILED;
445    base::RunLoop stop_run_loop;
446    BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
447                            base::Bind(&self::StopOnIOThread, this,
448                                       stop_run_loop.QuitClosure(),
449                                       &status));
450    stop_run_loop.Run();
451    ASSERT_EQ(SERVICE_WORKER_OK, status);
452  }
453
454  void ActivateTestHelper(
455      const std::string& worker_url,
456      ServiceWorkerStatusCode expected_status) {
457    RunOnIOThread(
458        base::Bind(&self::SetUpRegistrationOnIOThread, this, worker_url));
459    ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
460    base::RunLoop run_loop;
461    BrowserThread::PostTask(
462        BrowserThread::IO,
463        FROM_HERE,
464        base::Bind(
465            &self::ActivateOnIOThread, this, run_loop.QuitClosure(), &status));
466    run_loop.Run();
467    ASSERT_EQ(expected_status, status);
468  }
469
470  void FetchOnRegisteredWorker(
471      ServiceWorkerFetchEventResult* result,
472      ServiceWorkerResponse* response,
473      scoped_ptr<storage::BlobDataHandle>* blob_data_handle) {
474    blob_context_ = ChromeBlobStorageContext::GetFor(
475        shell()->web_contents()->GetBrowserContext());
476    bool prepare_result = false;
477    FetchResult fetch_result;
478    fetch_result.status = SERVICE_WORKER_ERROR_FAILED;
479    base::RunLoop fetch_run_loop;
480    BrowserThread::PostTask(BrowserThread::IO,
481                            FROM_HERE,
482                            base::Bind(&self::FetchOnIOThread,
483                                       this,
484                                       fetch_run_loop.QuitClosure(),
485                                       &prepare_result,
486                                       &fetch_result));
487    fetch_run_loop.Run();
488    ASSERT_TRUE(prepare_result);
489    *result = fetch_result.result;
490    *response = fetch_result.response;
491    *blob_data_handle = fetch_result.blob_data_handle.Pass();
492    ASSERT_EQ(SERVICE_WORKER_OK, fetch_result.status);
493  }
494
495  void FetchTestHelper(const std::string& worker_url,
496                       ServiceWorkerFetchEventResult* result,
497                       ServiceWorkerResponse* response,
498                       scoped_ptr<storage::BlobDataHandle>* blob_data_handle) {
499    RunOnIOThread(
500        base::Bind(&self::SetUpRegistrationOnIOThread, this, worker_url));
501    FetchOnRegisteredWorker(result, response, blob_data_handle);
502  }
503
504  void SetUpRegistrationOnIOThread(const std::string& worker_url) {
505    const GURL pattern = embedded_test_server()->GetURL("/");
506    registration_ = new ServiceWorkerRegistration(
507        pattern,
508        wrapper()->context()->storage()->NewRegistrationId(),
509        wrapper()->context()->AsWeakPtr());
510    version_ = new ServiceWorkerVersion(
511        registration_.get(),
512        embedded_test_server()->GetURL(worker_url),
513        wrapper()->context()->storage()->NewVersionId(),
514        wrapper()->context()->AsWeakPtr());
515    AssociateRendererProcessToPattern(pattern);
516  }
517
518  void StartOnIOThread(const base::Closure& done,
519                       ServiceWorkerStatusCode* result) {
520    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
521    version_->StartWorker(CreateReceiver(BrowserThread::UI, done, result));
522  }
523
524  void InstallOnIOThread(const base::Closure& done,
525                         ServiceWorkerStatusCode* result) {
526    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
527    version_->SetStatus(ServiceWorkerVersion::INSTALLING);
528    version_->DispatchInstallEvent(
529        -1, CreateReceiver(BrowserThread::UI, done, result));
530  }
531
532  void ActivateOnIOThread(const base::Closure& done,
533                          ServiceWorkerStatusCode* result) {
534    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
535    version_->SetStatus(ServiceWorkerVersion::ACTIVATING);
536    version_->DispatchActivateEvent(
537        CreateReceiver(BrowserThread::UI, done, result));
538  }
539
540  void FetchOnIOThread(const base::Closure& done,
541                       bool* prepare_result,
542                       FetchResult* result) {
543    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
544    ServiceWorkerFetchRequest request(
545        embedded_test_server()->GetURL("/service_worker/empty.html"),
546        "GET",
547        ServiceWorkerHeaderMap(),
548        GURL(""),
549        false);
550    version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
551    version_->DispatchFetchEvent(
552        request,
553        CreatePrepareReceiver(prepare_result),
554        CreateResponseReceiver(
555            BrowserThread::UI, done, blob_context_.get(), result));
556  }
557
558  void StopOnIOThread(const base::Closure& done,
559                      ServiceWorkerStatusCode* result) {
560    ASSERT_TRUE(version_.get());
561    version_->StopWorker(CreateReceiver(BrowserThread::UI, done, result));
562  }
563
564  void SyncEventOnIOThread(const base::Closure& done,
565                           ServiceWorkerStatusCode* result) {
566    ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO));
567    version_->SetStatus(ServiceWorkerVersion::ACTIVATED);
568    version_->DispatchSyncEvent(
569        CreateReceiver(BrowserThread::UI, done, result));
570  }
571
572 protected:
573  scoped_refptr<ServiceWorkerRegistration> registration_;
574  scoped_refptr<ServiceWorkerVersion> version_;
575  scoped_refptr<ChromeBlobStorageContext> blob_context_;
576};
577
578IN_PROC_BROWSER_TEST_F(EmbeddedWorkerBrowserTest, StartAndStop) {
579  // Start a worker and wait until OnStarted() is called.
580  base::RunLoop start_run_loop;
581  done_closure_ = start_run_loop.QuitClosure();
582  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
583                          base::Bind(&self::StartOnIOThread, this));
584  start_run_loop.Run();
585
586  ASSERT_EQ(EmbeddedWorkerInstance::RUNNING, last_worker_status_);
587
588  // Stop a worker and wait until OnStopped() is called.
589  base::RunLoop stop_run_loop;
590  done_closure_ = stop_run_loop.QuitClosure();
591  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
592                          base::Bind(&self::StopOnIOThread, this));
593  stop_run_loop.Run();
594
595  ASSERT_EQ(EmbeddedWorkerInstance::STOPPED, last_worker_status_);
596}
597
598IN_PROC_BROWSER_TEST_F(EmbeddedWorkerBrowserTest, StartPaused_ThenResume) {
599  pause_mode_ = PAUSE_THEN_RESUME;
600  base::RunLoop start_run_loop;
601  done_closure_ = start_run_loop.QuitClosure();
602  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
603                          base::Bind(&self::StartOnIOThread, this));
604  start_run_loop.Run();
605  ASSERT_EQ(EmbeddedWorkerInstance::RUNNING, last_worker_status_);
606}
607
608IN_PROC_BROWSER_TEST_F(EmbeddedWorkerBrowserTest,
609                       StartPaused_ThenStop) {
610  pause_mode_ = PAUSE_THEN_STOP;
611  base::RunLoop start_run_loop;
612  done_closure_ = start_run_loop.QuitClosure();
613  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
614                          base::Bind(&self::StartOnIOThread, this));
615  start_run_loop.Run();
616  ASSERT_EQ(EmbeddedWorkerInstance::STOPPED, last_worker_status_);
617}
618
619IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartAndStop) {
620  RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
621                           "/service_worker/worker.js"));
622
623  // Start a worker.
624  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
625  base::RunLoop start_run_loop;
626  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
627                          base::Bind(&self::StartOnIOThread, this,
628                                     start_run_loop.QuitClosure(),
629                                     &status));
630  start_run_loop.Run();
631  ASSERT_EQ(SERVICE_WORKER_OK, status);
632
633  // Stop the worker.
634  status = SERVICE_WORKER_ERROR_FAILED;
635  base::RunLoop stop_run_loop;
636  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
637                          base::Bind(&self::StopOnIOThread, this,
638                                     stop_run_loop.QuitClosure(),
639                                     &status));
640  stop_run_loop.Run();
641  ASSERT_EQ(SERVICE_WORKER_OK, status);
642}
643
644IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, StartNotFound) {
645  RunOnIOThread(base::Bind(&self::SetUpRegistrationOnIOThread, this,
646                           "/service_worker/nonexistent.js"));
647
648  // Start a worker for nonexistent URL.
649  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
650  base::RunLoop start_run_loop;
651  BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
652                          base::Bind(&self::StartOnIOThread, this,
653                                     start_run_loop.QuitClosure(),
654                                     &status));
655  start_run_loop.Run();
656  ASSERT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, status);
657}
658
659IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Install) {
660  InstallTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
661}
662
663IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
664                       InstallWithWaitUntil_Fulfilled) {
665  InstallTestHelper("/service_worker/worker_install_fulfilled.js",
666                    SERVICE_WORKER_OK);
667}
668
669// Check that ServiceWorker script requests set a "Service-Worker: script"
670// header.
671IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
672                       ServiceWorkerScriptHeader) {
673  embedded_test_server()->RegisterRequestHandler(
674      base::Bind(&VerifyServiceWorkerHeaderInRequest));
675  InstallTestHelper("/service_worker/generated_sw.js", SERVICE_WORKER_OK);
676}
677
678IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
679                       Activate_NoEventListener) {
680  ActivateTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK);
681  ASSERT_EQ(ServiceWorkerVersion::ACTIVATING, version_->status());
682}
683
684IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Activate_Rejected) {
685  ActivateTestHelper("/service_worker/worker_activate_rejected.js",
686                     SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED);
687}
688
689IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
690                       InstallWithWaitUntil_Rejected) {
691  InstallTestHelper("/service_worker/worker_install_rejected.js",
692                    SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
693}
694
695IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchEvent_Response) {
696  ServiceWorkerFetchEventResult result;
697  ServiceWorkerResponse response;
698  scoped_ptr<storage::BlobDataHandle> blob_data_handle;
699  FetchTestHelper("/service_worker/fetch_event.js",
700                  &result, &response, &blob_data_handle);
701  ASSERT_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE, result);
702  EXPECT_EQ(301, response.status_code);
703  EXPECT_EQ("Moved Permanently", response.status_text);
704  ServiceWorkerHeaderMap expected_headers;
705  expected_headers["content-language"] = "fi";
706  expected_headers["content-type"] = "text/html; charset=UTF-8";
707  EXPECT_EQ(expected_headers, response.headers);
708
709  std::string body;
710  RunOnIOThread(
711      base::Bind(&ReadResponseBody,
712                 &body, base::Owned(blob_data_handle.release())));
713  EXPECT_EQ("This resource is gone. Gone, gone, gone.", body);
714}
715
716IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest,
717                       SyncAbortedWithoutFlag) {
718  RunOnIOThread(base::Bind(
719      &self::SetUpRegistrationOnIOThread, this, "/service_worker/sync.js"));
720
721  // Run the sync event.
722  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
723  base::RunLoop sync_run_loop;
724  BrowserThread::PostTask(BrowserThread::IO,
725                          FROM_HERE,
726                          base::Bind(&self::SyncEventOnIOThread,
727                                     this,
728                                     sync_run_loop.QuitClosure(),
729                                     &status));
730  sync_run_loop.Run();
731  ASSERT_EQ(SERVICE_WORKER_ERROR_ABORT, status);
732}
733
734IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, SyncEventHandled) {
735  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
736  command_line->AppendSwitch(switches::kEnableServiceWorkerSync);
737
738  RunOnIOThread(base::Bind(
739      &self::SetUpRegistrationOnIOThread, this, "/service_worker/sync.js"));
740  ServiceWorkerFetchEventResult result;
741  ServiceWorkerResponse response;
742  scoped_ptr<storage::BlobDataHandle> blob_data_handle;
743  // Should 404 before sync event.
744  FetchOnRegisteredWorker(&result, &response, &blob_data_handle);
745  EXPECT_EQ(404, response.status_code);
746
747  // Run the sync event.
748  ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
749  base::RunLoop sync_run_loop;
750  BrowserThread::PostTask(BrowserThread::IO,
751                          FROM_HERE,
752                          base::Bind(&self::SyncEventOnIOThread,
753                                     this,
754                                     sync_run_loop.QuitClosure(),
755                                     &status));
756  sync_run_loop.Run();
757  ASSERT_EQ(SERVICE_WORKER_OK, status);
758
759  // Should 200 after sync event.
760  FetchOnRegisteredWorker(&result, &response, &blob_data_handle);
761  EXPECT_EQ(200, response.status_code);
762}
763
764// ServiceWorkerBrowserTest.Reload is flaky on Android crbug.com/393486
765#if defined(OS_ANDROID)
766#define MAYBE_Reload DISABLED_Reload
767#else
768#define MAYBE_Reload Reload
769#endif
770IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, MAYBE_Reload) {
771  const std::string kPageUrl = "/service_worker/reload.html";
772  const std::string kWorkerUrl = "/service_worker/fetch_event_reload.js";
773  {
774    scoped_refptr<WorkerActivatedObserver> observer =
775        new WorkerActivatedObserver(wrapper());
776    observer->Init();
777    public_context()->RegisterServiceWorker(
778        embedded_test_server()->GetURL(kPageUrl),
779        embedded_test_server()->GetURL(kWorkerUrl),
780        base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing)));
781    observer->Wait();
782  }
783  {
784    const base::string16 title = base::ASCIIToUTF16("reload=false");
785    TitleWatcher title_watcher(shell()->web_contents(), title);
786    NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl));
787    EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
788  }
789  {
790    const base::string16 title = base::ASCIIToUTF16("reload=true");
791    TitleWatcher title_watcher(shell()->web_contents(), title);
792    ReloadBlockUntilNavigationsComplete(shell(), 1);
793    EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
794  }
795  shell()->Close();
796  {
797    base::RunLoop run_loop;
798    public_context()->UnregisterServiceWorker(
799        embedded_test_server()->GetURL(kPageUrl),
800        base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
801    run_loop.Run();
802  }
803}
804
805IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, ImportsBustMemcache) {
806  const std::string kScopeUrl = "/service_worker/imports_bust_memcache_scope/";
807  const std::string kPageUrl = "/service_worker/imports_bust_memcache.html";
808  const std::string kScriptUrl = "/service_worker/worker_with_one_import.js";
809  const std::string kImportUrl = "/service_worker/long_lived_import.js";
810  const base::string16 kOKTitle(base::ASCIIToUTF16("OK"));
811  const base::string16 kFailTitle(base::ASCIIToUTF16("FAIL"));
812
813  RunOnIOThread(
814      base::Bind(&CreateLongLivedResourceInterceptors,
815                 embedded_test_server()->GetURL(kScriptUrl),
816                 embedded_test_server()->GetURL(kImportUrl)));
817
818  TitleWatcher title_watcher(shell()->web_contents(), kOKTitle);
819  title_watcher.AlsoWaitForTitle(kFailTitle);
820  NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl));
821  base::string16 title = title_watcher.WaitAndGetTitle();
822  EXPECT_EQ(kOKTitle, title);
823
824  // Verify the number of resources in the implicit script cache is correct.
825  const int kExpectedNumResources = 2;
826  int num_resources = 0;
827  RunOnIOThread(
828      base::Bind(&CountScriptResources,
829                 base::Unretained(wrapper()),
830                 embedded_test_server()->GetURL(kScopeUrl),
831                 &num_resources));
832  EXPECT_EQ(kExpectedNumResources, num_resources);
833}
834
835class ServiceWorkerBlackBoxBrowserTest : public ServiceWorkerBrowserTest {
836 public:
837  typedef ServiceWorkerBlackBoxBrowserTest self;
838
839  void FindRegistrationOnIO(const GURL& document_url,
840                            ServiceWorkerStatusCode* status,
841                            const base::Closure& continuation) {
842    wrapper()->context()->storage()->FindRegistrationForDocument(
843        document_url,
844        base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO2,
845                   this,
846                   status,
847                   continuation));
848  }
849
850  void FindRegistrationOnIO2(
851      ServiceWorkerStatusCode* out_status,
852      const base::Closure& continuation,
853      ServiceWorkerStatusCode status,
854      const scoped_refptr<ServiceWorkerRegistration>& registration) {
855    *out_status = status;
856    if (!registration.get())
857      EXPECT_NE(SERVICE_WORKER_OK, status);
858    continuation.Run();
859  }
860};
861
862static int CountRenderProcessHosts() {
863  int result = 0;
864  for (RenderProcessHost::iterator iter(RenderProcessHost::AllHostsIterator());
865       !iter.IsAtEnd();
866       iter.Advance()) {
867    result++;
868  }
869  return result;
870}
871
872// Crashes on Android and flakes on CrOS: http://crbug.com/387045
873#if defined(OS_ANDROID) || defined(OS_CHROMEOS)
874#define MAYBE_Registration DISABLED_Registration
875#else
876#define MAYBE_Registration Registration
877#endif
878IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, MAYBE_Registration) {
879  // Close the only window to be sure we're not re-using its RenderProcessHost.
880  shell()->Close();
881  EXPECT_EQ(0, CountRenderProcessHosts());
882
883  const std::string kWorkerUrl = "/service_worker/fetch_event.js";
884
885  // Unregistering nothing should return false.
886  {
887    base::RunLoop run_loop;
888    public_context()->UnregisterServiceWorker(
889        embedded_test_server()->GetURL("/"),
890        base::Bind(&ExpectResultAndRun, false, run_loop.QuitClosure()));
891    run_loop.Run();
892  }
893
894  // If we use a worker URL that doesn't exist, registration fails.
895  {
896    base::RunLoop run_loop;
897    public_context()->RegisterServiceWorker(
898        embedded_test_server()->GetURL("/"),
899        embedded_test_server()->GetURL("/does/not/exist"),
900        base::Bind(&ExpectResultAndRun, false, run_loop.QuitClosure()));
901    run_loop.Run();
902  }
903  EXPECT_EQ(0, CountRenderProcessHosts());
904
905  // Register returns when the promise would be resolved.
906  {
907    base::RunLoop run_loop;
908    public_context()->RegisterServiceWorker(
909        embedded_test_server()->GetURL("/"),
910        embedded_test_server()->GetURL(kWorkerUrl),
911        base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
912    run_loop.Run();
913  }
914  EXPECT_EQ(1, CountRenderProcessHosts());
915
916  // Registering again should succeed, although the algo still
917  // might not be complete.
918  {
919    base::RunLoop run_loop;
920    public_context()->RegisterServiceWorker(
921        embedded_test_server()->GetURL("/"),
922        embedded_test_server()->GetURL(kWorkerUrl),
923        base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
924    run_loop.Run();
925  }
926
927  // The registration algo might not be far enough along to have
928  // stored the registration data, so it may not be findable
929  // at this point.
930
931  // Unregistering something should return true.
932  {
933    base::RunLoop run_loop;
934    public_context()->UnregisterServiceWorker(
935        embedded_test_server()->GetURL("/"),
936        base::Bind(&ExpectResultAndRun, true, run_loop.QuitClosure()));
937    run_loop.Run();
938  }
939  EXPECT_GE(1, CountRenderProcessHosts()) << "Unregistering doesn't stop the "
940                                             "workers eagerly, so their RPHs "
941                                             "can still be running.";
942
943  // Should not be able to find it.
944  {
945    ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
946    RunOnIOThread(
947        base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO,
948                   this,
949                   embedded_test_server()->GetURL("/service_worker/empty.html"),
950                   &status));
951    EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND, status);
952  }
953}
954
955}  // namespace content
956