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 42 namespace content { 43 44 namespace { 45 46 struct FetchResult { 47 ServiceWorkerStatusCode status; 48 ServiceWorkerFetchEventResult result; 49 ServiceWorkerResponse response; 50 scoped_ptr<storage::BlobDataHandle> blob_data_handle; 51 }; 52 53 void 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 60 void 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 69 void 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 83 void ReceivePrepareResult(bool* is_prepared) { 84 *is_prepared = true; 85 } 86 87 base::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. 94 void 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 113 ServiceWorkerVersion::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 123 void 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 131 void ExpectResultAndRun(bool expected, 132 const base::Closure& continuation, 133 bool actual) { 134 EXPECT_EQ(expected, actual); 135 continuation.Run(); 136 } 137 138 class 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 172 scoped_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. 191 class 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 216 void 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 232 void 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 261 class 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 306 class 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 417 class 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 578 IN_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 598 IN_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 608 IN_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 619 IN_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 644 IN_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 659 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Install) { 660 InstallTestHelper("/service_worker/worker.js", SERVICE_WORKER_OK); 661 } 662 663 IN_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. 671 IN_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 678 IN_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 684 IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, Activate_Rejected) { 685 ActivateTestHelper("/service_worker/worker_activate_rejected.js", 686 SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED); 687 } 688 689 IN_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 695 IN_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 716 IN_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 734 IN_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 770 IN_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 805 IN_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 835 class 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 862 static 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 878 IN_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