[ServiceWorker] Implement WebServiceWorkerContextClient::openWindow().
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_version.cc
blobe767a3a94e731e1c207fa5b564dc7dfe259c0e9f
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_version.h"
7 #include "base/command_line.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "content/browser/message_port_message_filter.h"
13 #include "content/browser/message_port_service.h"
14 #include "content/browser/service_worker/embedded_worker_instance.h"
15 #include "content/browser/service_worker/embedded_worker_registry.h"
16 #include "content/browser/service_worker/service_worker_context_core.h"
17 #include "content/browser/service_worker/service_worker_context_wrapper.h"
18 #include "content/browser/service_worker/service_worker_registration.h"
19 #include "content/browser/service_worker/service_worker_utils.h"
20 #include "content/browser/storage_partition_impl.h"
21 #include "content/common/service_worker/service_worker_messages.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/content_browser_client.h"
24 #include "content/public/browser/page_navigator.h"
25 #include "content/public/browser/render_frame_host.h"
26 #include "content/public/browser/render_process_host.h"
27 #include "content/public/browser/web_contents.h"
28 #include "content/public/browser/web_contents_observer.h"
29 #include "content/public/common/child_process_host.h"
30 #include "content/public/common/content_client.h"
31 #include "content/public/common/content_switches.h"
32 #include "content/public/common/result_codes.h"
33 #include "net/http/http_response_info.h"
35 namespace content {
37 typedef ServiceWorkerVersion::StatusCallback StatusCallback;
38 typedef ServiceWorkerVersion::MessageCallback MessageCallback;
40 class ServiceWorkerVersion::GetClientDocumentsCallback
41 : public base::RefCounted<GetClientDocumentsCallback> {
42 public:
43 GetClientDocumentsCallback(int request_id,
44 ServiceWorkerVersion* version)
45 : request_id_(request_id),
46 version_(version) {
47 DCHECK(version_);
50 void AddClientInfo(int client_id, const ServiceWorkerClientInfo& info) {
51 clients_.push_back(info);
52 clients_.back().client_id = client_id;
55 private:
56 friend class base::RefCounted<GetClientDocumentsCallback>;
58 virtual ~GetClientDocumentsCallback() {
59 DCHECK_CURRENTLY_ON(BrowserThread::IO);
61 if (version_->running_status() == RUNNING) {
62 version_->embedded_worker_->SendMessage(
63 ServiceWorkerMsg_DidGetClientDocuments(request_id_, clients_));
67 std::vector<ServiceWorkerClientInfo> clients_;
68 int request_id_;
69 scoped_refptr<ServiceWorkerVersion> version_;
71 DISALLOW_COPY_AND_ASSIGN(GetClientDocumentsCallback);
74 namespace {
76 // Default delay for scheduled stop.
77 // (Note that if all references to the version is dropped the worker
78 // is also stopped without delay)
79 const int64 kStopWorkerDelay = 30; // 30 secs.
81 // Delay for attempting to stop a doomed worker with in-flight requests.
82 const int64 kStopDoomedWorkerDelay = 5; // 5 secs.
84 // Default delay for scheduled update.
85 const int kUpdateDelaySeconds = 1;
87 const char kClaimClientsStateErrorMesage[] =
88 "Only the active worker can claim clients.";
90 const char kClaimClientsShutdownErrorMesage[] =
91 "Failed to claim clients due to Service Worker system shutdown.";
93 void RunSoon(const base::Closure& callback) {
94 if (!callback.is_null())
95 base::MessageLoop::current()->PostTask(FROM_HERE, callback);
98 template <typename CallbackArray, typename Arg>
99 void RunCallbacks(ServiceWorkerVersion* version,
100 CallbackArray* callbacks_ptr,
101 const Arg& arg) {
102 CallbackArray callbacks;
103 callbacks.swap(*callbacks_ptr);
104 scoped_refptr<ServiceWorkerVersion> protect(version);
105 for (const auto& callback : callbacks)
106 callback.Run(arg);
109 template <typename IDMAP, typename... Params>
110 void RunIDMapCallbacks(IDMAP* callbacks, const Params&... params) {
111 typename IDMAP::iterator iter(callbacks);
112 while (!iter.IsAtEnd()) {
113 iter.GetCurrentValue()->Run(params...);
114 iter.Advance();
116 callbacks->Clear();
119 // A callback adapter to start a |task| after StartWorker.
120 void RunTaskAfterStartWorker(
121 base::WeakPtr<ServiceWorkerVersion> version,
122 const StatusCallback& error_callback,
123 const base::Closure& task,
124 ServiceWorkerStatusCode status) {
125 if (status != SERVICE_WORKER_OK) {
126 if (!error_callback.is_null())
127 error_callback.Run(status);
128 return;
130 if (version->running_status() != ServiceWorkerVersion::RUNNING) {
131 // We've tried to start the worker (and it has succeeded), but
132 // it looks it's not running yet.
133 NOTREACHED() << "The worker's not running after successful StartWorker";
134 if (!error_callback.is_null())
135 error_callback.Run(SERVICE_WORKER_ERROR_START_WORKER_FAILED);
136 return;
138 task.Run();
141 void RunErrorFetchCallback(const ServiceWorkerVersion::FetchCallback& callback,
142 ServiceWorkerStatusCode status) {
143 callback.Run(status,
144 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
145 ServiceWorkerResponse());
148 void RunErrorMessageCallback(
149 const std::vector<int>& sent_message_port_ids,
150 const ServiceWorkerVersion::StatusCallback& callback,
151 ServiceWorkerStatusCode status) {
152 // Transfering the message ports failed, so destroy the ports.
153 for (int message_port_id : sent_message_port_ids) {
154 MessagePortService::GetInstance()->ClosePort(message_port_id);
156 callback.Run(status);
159 void RunErrorCrossOriginConnectCallback(
160 const ServiceWorkerVersion::CrossOriginConnectCallback& callback,
161 ServiceWorkerStatusCode status) {
162 callback.Run(status, false);
165 using WindowOpenedCallback = base::Callback<void(int, int)>;
167 // The WindowOpenedObserver class is a WebContentsObserver that will wait for a
168 // new Window's WebContents to be initialized, run the |callback| passed to its
169 // constructor then self destroy.
170 // The callback will receive the process and frame ids. If something went wrong
171 // those will be (kInvalidUniqueID, MSG_ROUTING_NONE).
172 // The callback will be called in the IO thread.
173 class WindowOpenedObserver : public WebContentsObserver {
174 public:
175 WindowOpenedObserver(WebContents* web_contents,
176 const WindowOpenedCallback& callback)
177 : WebContentsObserver(web_contents),
178 callback_(callback)
181 void DidCommitProvisionalLoadForFrame(
182 RenderFrameHost* render_frame_host,
183 const GURL& validated_url,
184 ui::PageTransition transition_type) override {
185 DCHECK(web_contents());
187 if (render_frame_host != web_contents()->GetMainFrame())
188 return;
190 RunCallback(render_frame_host->GetProcess()->GetID(),
191 render_frame_host->GetRoutingID());
194 void RenderProcessGone(base::TerminationStatus status) override {
195 RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
198 void WebContentsDestroyed() override {
199 RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
202 private:
203 void RunCallback(int render_process_id, int render_frame_id) {
204 // After running the callback, |this| will stop observing, thus
205 // web_contents() should return nullptr and |RunCallback| should no longer
206 // be called. Then, |this| will self destroy.
207 DCHECK(web_contents());
209 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
210 base::Bind(callback_,
211 render_process_id,
212 render_frame_id));
213 Observe(nullptr);
214 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
217 const WindowOpenedCallback callback_;
219 DISALLOW_COPY_AND_ASSIGN(WindowOpenedObserver);
222 void OpenWindowOnUI(
223 const GURL& url,
224 const GURL& script_url,
225 int process_id,
226 const scoped_refptr<ServiceWorkerContextWrapper>& context_wrapper,
227 const WindowOpenedCallback& callback) {
228 DCHECK_CURRENTLY_ON(BrowserThread::UI);
230 BrowserContext* browser_context = context_wrapper->storage_partition()
231 ? context_wrapper->storage_partition()->browser_context()
232 : nullptr;
233 // We are shutting down.
234 if (!browser_context)
235 return;
237 RenderProcessHost* render_process_host =
238 RenderProcessHost::FromID(process_id);
239 if (render_process_host->IsIsolatedGuest()) {
240 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
241 base::Bind(callback,
242 ChildProcessHost::kInvalidUniqueID,
243 MSG_ROUTING_NONE));
244 return;
247 OpenURLParams params(url,
248 Referrer(script_url, blink::WebReferrerPolicyDefault),
249 NEW_FOREGROUND_TAB,
250 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
251 true /* is_renderer_initiated */);
253 WebContents* web_contents =
254 GetContentClient()->browser()->OpenURL(browser_context, params);
255 DCHECK(web_contents);
257 new WindowOpenedObserver(web_contents, callback);
260 void KillEmbeddedWorkerProcess(int process_id, ResultCode code) {
261 DCHECK_CURRENTLY_ON(BrowserThread::UI);
263 RenderProcessHost* render_process_host =
264 RenderProcessHost::FromID(process_id);
265 if (render_process_host->GetHandle() != base::kNullProcessHandle)
266 render_process_host->ReceivedBadMessage();
269 } // namespace
271 ServiceWorkerVersion::ServiceWorkerVersion(
272 ServiceWorkerRegistration* registration,
273 const GURL& script_url,
274 int64 version_id,
275 base::WeakPtr<ServiceWorkerContextCore> context)
276 : version_id_(version_id),
277 registration_id_(kInvalidServiceWorkerVersionId),
278 script_url_(script_url),
279 status_(NEW),
280 context_(context),
281 script_cache_map_(this, context),
282 is_doomed_(false),
283 skip_waiting_(false),
284 weak_factory_(this) {
285 DCHECK(context_);
286 DCHECK(registration);
287 if (registration) {
288 registration_id_ = registration->id();
289 scope_ = registration->pattern();
291 context_->AddLiveVersion(this);
292 embedded_worker_ = context_->embedded_worker_registry()->CreateWorker();
293 embedded_worker_->AddListener(this);
296 ServiceWorkerVersion::~ServiceWorkerVersion() {
297 embedded_worker_->RemoveListener(this);
298 if (context_)
299 context_->RemoveLiveVersion(version_id_);
300 // EmbeddedWorker's dtor sends StopWorker if it's still running.
303 void ServiceWorkerVersion::SetStatus(Status status) {
304 if (status_ == status)
305 return;
307 status_ = status;
309 if (skip_waiting_ && status_ == ACTIVATED) {
310 for (int request_id : pending_skip_waiting_requests_)
311 DidSkipWaiting(request_id);
312 pending_skip_waiting_requests_.clear();
315 std::vector<base::Closure> callbacks;
316 callbacks.swap(status_change_callbacks_);
317 for (const auto& callback : callbacks)
318 callback.Run();
320 FOR_EACH_OBSERVER(Listener, listeners_, OnVersionStateChanged(this));
323 void ServiceWorkerVersion::RegisterStatusChangeCallback(
324 const base::Closure& callback) {
325 status_change_callbacks_.push_back(callback);
328 ServiceWorkerVersionInfo ServiceWorkerVersion::GetInfo() {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
330 return ServiceWorkerVersionInfo(
331 running_status(),
332 status(),
333 script_url(),
334 version_id(),
335 embedded_worker()->process_id(),
336 embedded_worker()->thread_id(),
337 embedded_worker()->worker_devtools_agent_route_id());
340 void ServiceWorkerVersion::StartWorker(const StatusCallback& callback) {
341 StartWorker(false, callback);
344 void ServiceWorkerVersion::StartWorker(
345 bool pause_after_download,
346 const StatusCallback& callback) {
347 if (is_doomed()) {
348 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED));
349 return;
351 switch (running_status()) {
352 case RUNNING:
353 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
354 return;
355 case STOPPING:
356 case STOPPED:
357 case STARTING:
358 start_callbacks_.push_back(callback);
359 if (running_status() == STOPPED) {
360 DCHECK(!cache_listener_.get());
361 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_));
362 embedded_worker_->Start(
363 version_id_,
364 scope_,
365 script_url_,
366 pause_after_download,
367 base::Bind(&ServiceWorkerVersion::OnStartMessageSent,
368 weak_factory_.GetWeakPtr()));
370 return;
374 void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) {
375 if (running_status() == STOPPED) {
376 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
377 return;
379 if (stop_callbacks_.empty()) {
380 ServiceWorkerStatusCode status = embedded_worker_->Stop();
381 if (status != SERVICE_WORKER_OK) {
382 RunSoon(base::Bind(callback, status));
383 return;
386 stop_callbacks_.push_back(callback);
389 void ServiceWorkerVersion::ScheduleUpdate() {
390 if (update_timer_.IsRunning()) {
391 update_timer_.Reset();
392 return;
394 update_timer_.Start(
395 FROM_HERE, base::TimeDelta::FromSeconds(kUpdateDelaySeconds),
396 base::Bind(&ServiceWorkerVersion::StartUpdate,
397 weak_factory_.GetWeakPtr()));
400 void ServiceWorkerVersion::DeferScheduledUpdate() {
401 if (update_timer_.IsRunning())
402 update_timer_.Reset();
405 void ServiceWorkerVersion::StartUpdate() {
406 update_timer_.Stop();
407 if (!context_)
408 return;
409 ServiceWorkerRegistration* registration =
410 context_->GetLiveRegistration(registration_id_);
411 if (!registration || !registration->GetNewestVersion())
412 return;
413 context_->UpdateServiceWorker(registration);
416 void ServiceWorkerVersion::SendMessage(
417 const IPC::Message& message, const StatusCallback& callback) {
418 if (running_status() != RUNNING) {
419 // Schedule calling this method after starting the worker.
420 StartWorker(base::Bind(&RunTaskAfterStartWorker,
421 weak_factory_.GetWeakPtr(), callback,
422 base::Bind(&self::SendMessage,
423 weak_factory_.GetWeakPtr(),
424 message, callback)));
425 return;
428 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(message);
429 RunSoon(base::Bind(callback, status));
432 void ServiceWorkerVersion::DispatchMessageEvent(
433 const base::string16& message,
434 const std::vector<int>& sent_message_port_ids,
435 const StatusCallback& callback) {
436 for (int message_port_id : sent_message_port_ids) {
437 MessagePortService::GetInstance()->HoldMessages(message_port_id);
440 DispatchMessageEventInternal(message, sent_message_port_ids, callback);
443 void ServiceWorkerVersion::DispatchMessageEventInternal(
444 const base::string16& message,
445 const std::vector<int>& sent_message_port_ids,
446 const StatusCallback& callback) {
447 if (running_status() != RUNNING) {
448 // Schedule calling this method after starting the worker.
449 StartWorker(base::Bind(
450 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
451 base::Bind(&RunErrorMessageCallback, sent_message_port_ids, callback),
452 base::Bind(&self::DispatchMessageEventInternal,
453 weak_factory_.GetWeakPtr(), message, sent_message_port_ids,
454 callback)));
455 return;
458 MessagePortMessageFilter* filter =
459 embedded_worker_->message_port_message_filter();
460 std::vector<int> new_routing_ids;
461 filter->UpdateMessagePortsWithNewRoutes(sent_message_port_ids,
462 &new_routing_ids);
463 ServiceWorkerStatusCode status =
464 embedded_worker_->SendMessage(ServiceWorkerMsg_MessageToWorker(
465 message, sent_message_port_ids, new_routing_ids));
466 RunSoon(base::Bind(callback, status));
469 void ServiceWorkerVersion::DispatchInstallEvent(
470 int active_version_id,
471 const StatusCallback& callback) {
472 DCHECK_EQ(INSTALLING, status()) << status();
474 if (running_status() != RUNNING) {
475 // Schedule calling this method after starting the worker.
476 StartWorker(
477 base::Bind(&RunTaskAfterStartWorker,
478 weak_factory_.GetWeakPtr(),
479 callback,
480 base::Bind(&self::DispatchInstallEventAfterStartWorker,
481 weak_factory_.GetWeakPtr(),
482 active_version_id,
483 callback)));
484 } else {
485 DispatchInstallEventAfterStartWorker(active_version_id, callback);
489 void ServiceWorkerVersion::DispatchActivateEvent(
490 const StatusCallback& callback) {
491 DCHECK_EQ(ACTIVATING, status()) << status();
493 if (running_status() != RUNNING) {
494 // Schedule calling this method after starting the worker.
495 StartWorker(
496 base::Bind(&RunTaskAfterStartWorker,
497 weak_factory_.GetWeakPtr(),
498 callback,
499 base::Bind(&self::DispatchActivateEventAfterStartWorker,
500 weak_factory_.GetWeakPtr(),
501 callback)));
502 } else {
503 DispatchActivateEventAfterStartWorker(callback);
507 void ServiceWorkerVersion::DispatchFetchEvent(
508 const ServiceWorkerFetchRequest& request,
509 const base::Closure& prepare_callback,
510 const FetchCallback& fetch_callback) {
511 DCHECK_EQ(ACTIVATED, status()) << status();
513 if (running_status() != RUNNING) {
514 // Schedule calling this method after starting the worker.
515 StartWorker(base::Bind(&RunTaskAfterStartWorker,
516 weak_factory_.GetWeakPtr(),
517 base::Bind(&RunErrorFetchCallback, fetch_callback),
518 base::Bind(&self::DispatchFetchEvent,
519 weak_factory_.GetWeakPtr(),
520 request,
521 prepare_callback,
522 fetch_callback)));
523 return;
526 prepare_callback.Run();
528 int request_id = fetch_callbacks_.Add(new FetchCallback(fetch_callback));
529 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
530 ServiceWorkerMsg_FetchEvent(request_id, request));
531 if (status != SERVICE_WORKER_OK) {
532 fetch_callbacks_.Remove(request_id);
533 RunSoon(base::Bind(&RunErrorFetchCallback,
534 fetch_callback,
535 SERVICE_WORKER_ERROR_FAILED));
539 void ServiceWorkerVersion::DispatchSyncEvent(const StatusCallback& callback) {
540 DCHECK_EQ(ACTIVATED, status()) << status();
542 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
543 switches::kEnableServiceWorkerSync)) {
544 callback.Run(SERVICE_WORKER_ERROR_ABORT);
545 return;
548 if (running_status() != RUNNING) {
549 // Schedule calling this method after starting the worker.
550 StartWorker(base::Bind(&RunTaskAfterStartWorker,
551 weak_factory_.GetWeakPtr(), callback,
552 base::Bind(&self::DispatchSyncEvent,
553 weak_factory_.GetWeakPtr(),
554 callback)));
555 return;
558 int request_id = sync_callbacks_.Add(new StatusCallback(callback));
559 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
560 ServiceWorkerMsg_SyncEvent(request_id));
561 if (status != SERVICE_WORKER_OK) {
562 sync_callbacks_.Remove(request_id);
563 RunSoon(base::Bind(callback, status));
567 void ServiceWorkerVersion::DispatchNotificationClickEvent(
568 const StatusCallback& callback,
569 const std::string& notification_id,
570 const PlatformNotificationData& notification_data) {
571 DCHECK_EQ(ACTIVATED, status()) << status();
573 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
574 switches::kEnableExperimentalWebPlatformFeatures)) {
575 callback.Run(SERVICE_WORKER_ERROR_ABORT);
576 return;
579 if (running_status() != RUNNING) {
580 // Schedule calling this method after starting the worker.
581 StartWorker(base::Bind(&RunTaskAfterStartWorker,
582 weak_factory_.GetWeakPtr(), callback,
583 base::Bind(&self::DispatchNotificationClickEvent,
584 weak_factory_.GetWeakPtr(),
585 callback, notification_id,
586 notification_data)));
587 return;
590 int request_id =
591 notification_click_callbacks_.Add(new StatusCallback(callback));
592 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
593 ServiceWorkerMsg_NotificationClickEvent(request_id,
594 notification_id,
595 notification_data));
596 if (status != SERVICE_WORKER_OK) {
597 notification_click_callbacks_.Remove(request_id);
598 RunSoon(base::Bind(callback, status));
602 void ServiceWorkerVersion::DispatchPushEvent(const StatusCallback& callback,
603 const std::string& data) {
604 DCHECK_EQ(ACTIVATED, status()) << status();
606 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
607 switches::kEnableExperimentalWebPlatformFeatures)) {
608 callback.Run(SERVICE_WORKER_ERROR_ABORT);
609 return;
612 if (running_status() != RUNNING) {
613 // Schedule calling this method after starting the worker.
614 StartWorker(base::Bind(&RunTaskAfterStartWorker,
615 weak_factory_.GetWeakPtr(), callback,
616 base::Bind(&self::DispatchPushEvent,
617 weak_factory_.GetWeakPtr(),
618 callback, data)));
619 return;
622 int request_id = push_callbacks_.Add(new StatusCallback(callback));
623 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
624 ServiceWorkerMsg_PushEvent(request_id, data));
625 if (status != SERVICE_WORKER_OK) {
626 push_callbacks_.Remove(request_id);
627 RunSoon(base::Bind(callback, status));
631 void ServiceWorkerVersion::DispatchGeofencingEvent(
632 const StatusCallback& callback,
633 blink::WebGeofencingEventType event_type,
634 const std::string& region_id,
635 const blink::WebCircularGeofencingRegion& region) {
636 DCHECK_EQ(ACTIVATED, status()) << status();
638 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
639 switches::kEnableExperimentalWebPlatformFeatures)) {
640 callback.Run(SERVICE_WORKER_ERROR_ABORT);
641 return;
644 if (running_status() != RUNNING) {
645 // Schedule calling this method after starting the worker.
646 StartWorker(base::Bind(&RunTaskAfterStartWorker,
647 weak_factory_.GetWeakPtr(),
648 callback,
649 base::Bind(&self::DispatchGeofencingEvent,
650 weak_factory_.GetWeakPtr(),
651 callback,
652 event_type,
653 region_id,
654 region)));
655 return;
658 int request_id = geofencing_callbacks_.Add(new StatusCallback(callback));
659 ServiceWorkerStatusCode status =
660 embedded_worker_->SendMessage(ServiceWorkerMsg_GeofencingEvent(
661 request_id, event_type, region_id, region));
662 if (status != SERVICE_WORKER_OK) {
663 geofencing_callbacks_.Remove(request_id);
664 RunSoon(base::Bind(callback, status));
668 void ServiceWorkerVersion::DispatchCrossOriginConnectEvent(
669 const CrossOriginConnectCallback& callback,
670 const NavigatorConnectClient& client) {
671 DCHECK_EQ(ACTIVATED, status()) << status();
673 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
674 switches::kEnableExperimentalWebPlatformFeatures)) {
675 callback.Run(SERVICE_WORKER_ERROR_ABORT, false);
676 return;
679 if (running_status() != RUNNING) {
680 // Schedule calling this method after starting the worker.
681 StartWorker(
682 base::Bind(&RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
683 base::Bind(&RunErrorCrossOriginConnectCallback, callback),
684 base::Bind(&self::DispatchCrossOriginConnectEvent,
685 weak_factory_.GetWeakPtr(), callback, client)));
686 return;
689 int request_id = cross_origin_connect_callbacks_.Add(
690 new CrossOriginConnectCallback(callback));
691 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
692 ServiceWorkerMsg_CrossOriginConnectEvent(request_id, client));
693 if (status != SERVICE_WORKER_OK) {
694 cross_origin_connect_callbacks_.Remove(request_id);
695 RunSoon(base::Bind(callback, status, false));
699 void ServiceWorkerVersion::DispatchCrossOriginMessageEvent(
700 const NavigatorConnectClient& client,
701 const base::string16& message,
702 const std::vector<int>& sent_message_port_ids,
703 const StatusCallback& callback) {
704 // Unlike in the case of DispatchMessageEvent, here the caller is assumed to
705 // have already put all the sent message ports on hold. So no need to do that
706 // here again.
708 if (running_status() != RUNNING) {
709 // Schedule calling this method after starting the worker.
710 StartWorker(base::Bind(
711 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(), callback,
712 base::Bind(&self::DispatchCrossOriginMessageEvent,
713 weak_factory_.GetWeakPtr(), client, message,
714 sent_message_port_ids, callback)));
715 return;
718 MessagePortMessageFilter* filter =
719 embedded_worker_->message_port_message_filter();
720 std::vector<int> new_routing_ids;
721 filter->UpdateMessagePortsWithNewRoutes(sent_message_port_ids,
722 &new_routing_ids);
723 ServiceWorkerStatusCode status =
724 embedded_worker_->SendMessage(ServiceWorkerMsg_CrossOriginMessageToWorker(
725 client, message, sent_message_port_ids, new_routing_ids));
726 RunSoon(base::Bind(callback, status));
728 void ServiceWorkerVersion::AddControllee(
729 ServiceWorkerProviderHost* provider_host) {
730 DCHECK(!ContainsKey(controllee_map_, provider_host));
731 int controllee_id = controllee_by_id_.Add(provider_host);
732 // IDMap<>'s last index is kInvalidServiceWorkerClientId.
733 CHECK(controllee_id != kInvalidServiceWorkerClientId);
734 controllee_map_[provider_host] = controllee_id;
735 // Reset the timer if it's running (so that it's kept alive a bit longer
736 // right after a new controllee is added).
737 ScheduleStopWorker();
740 void ServiceWorkerVersion::RemoveControllee(
741 ServiceWorkerProviderHost* provider_host) {
742 ControlleeMap::iterator found = controllee_map_.find(provider_host);
743 DCHECK(found != controllee_map_.end());
744 controllee_by_id_.Remove(found->second);
745 controllee_map_.erase(found);
746 if (HasControllee())
747 return;
748 FOR_EACH_OBSERVER(Listener, listeners_, OnNoControllees(this));
749 if (is_doomed_) {
750 DoomInternal();
751 return;
753 // Schedule the stop-worker-timer if it's not running.
754 if (!stop_worker_timer_.IsRunning())
755 ScheduleStopWorker();
758 void ServiceWorkerVersion::AddStreamingURLRequestJob(
759 const ServiceWorkerURLRequestJob* request_job) {
760 DCHECK(streaming_url_request_jobs_.find(request_job) ==
761 streaming_url_request_jobs_.end());
762 streaming_url_request_jobs_.insert(request_job);
765 void ServiceWorkerVersion::RemoveStreamingURLRequestJob(
766 const ServiceWorkerURLRequestJob* request_job) {
767 streaming_url_request_jobs_.erase(request_job);
768 if (is_doomed_)
769 StopWorkerIfIdle();
772 void ServiceWorkerVersion::AddListener(Listener* listener) {
773 listeners_.AddObserver(listener);
776 void ServiceWorkerVersion::RemoveListener(Listener* listener) {
777 listeners_.RemoveObserver(listener);
780 void ServiceWorkerVersion::Doom() {
781 if (is_doomed_)
782 return;
783 is_doomed_ = true;
784 if (!HasControllee())
785 DoomInternal();
788 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
789 embedded_worker()->set_devtools_attached(attached);
790 if (!attached && !stop_worker_timer_.IsRunning()) {
791 // If devtools is detached from this version and stop-worker-timer is not
792 // running, try scheduling stop-worker-timer now.
793 ScheduleStopWorker();
797 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
798 const net::HttpResponseInfo& http_info) {
799 main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
802 const net::HttpResponseInfo*
803 ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
804 return main_script_http_info_.get();
807 void ServiceWorkerVersion::OnStarted() {
808 DCHECK_EQ(RUNNING, running_status());
809 DCHECK(cache_listener_.get());
810 ScheduleStopWorker();
812 // Fire all start callbacks.
813 scoped_refptr<ServiceWorkerVersion> protect(this);
814 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_OK);
815 FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStarted(this));
818 void ServiceWorkerVersion::OnStopped(
819 EmbeddedWorkerInstance::Status old_status) {
820 DCHECK_EQ(STOPPED, running_status());
821 scoped_refptr<ServiceWorkerVersion> protect(this);
823 bool should_restart = !is_doomed() && !start_callbacks_.empty() &&
824 (old_status != EmbeddedWorkerInstance::STARTING);
826 // Fire all stop callbacks.
827 RunCallbacks(this, &stop_callbacks_, SERVICE_WORKER_OK);
829 if (!should_restart) {
830 // Let all start callbacks fail.
831 RunCallbacks(this, &start_callbacks_,
832 SERVICE_WORKER_ERROR_START_WORKER_FAILED);
835 // Let all message callbacks fail (this will also fire and clear all
836 // callbacks for events).
837 // TODO(kinuko): Consider if we want to add queue+resend mechanism here.
838 RunIDMapCallbacks(&activate_callbacks_,
839 SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED);
840 RunIDMapCallbacks(&install_callbacks_,
841 SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
842 RunIDMapCallbacks(&fetch_callbacks_,
843 SERVICE_WORKER_ERROR_FAILED,
844 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
845 ServiceWorkerResponse());
846 RunIDMapCallbacks(&sync_callbacks_,
847 SERVICE_WORKER_ERROR_FAILED);
848 RunIDMapCallbacks(&notification_click_callbacks_,
849 SERVICE_WORKER_ERROR_FAILED);
850 RunIDMapCallbacks(&push_callbacks_,
851 SERVICE_WORKER_ERROR_FAILED);
852 RunIDMapCallbacks(&geofencing_callbacks_,
853 SERVICE_WORKER_ERROR_FAILED);
854 RunIDMapCallbacks(&cross_origin_connect_callbacks_,
855 SERVICE_WORKER_ERROR_FAILED,
856 false);
858 streaming_url_request_jobs_.clear();
860 FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStopped(this));
862 // There should be no more communication from/to a stopped worker. Deleting
863 // the listener prevents any pending completion callbacks from causing
864 // messages to be sent to the stopped worker.
865 cache_listener_.reset();
867 // Restart worker if we have any start callbacks and the worker isn't doomed.
868 if (should_restart) {
869 cache_listener_.reset(new ServiceWorkerCacheListener(this, context_));
870 embedded_worker_->Start(
871 version_id_, scope_, script_url_, false /* pause_after_download */,
872 base::Bind(&ServiceWorkerVersion::OnStartMessageSent,
873 weak_factory_.GetWeakPtr()));
877 void ServiceWorkerVersion::OnReportException(
878 const base::string16& error_message,
879 int line_number,
880 int column_number,
881 const GURL& source_url) {
882 FOR_EACH_OBSERVER(
883 Listener,
884 listeners_,
885 OnErrorReported(
886 this, error_message, line_number, column_number, source_url));
889 void ServiceWorkerVersion::OnReportConsoleMessage(int source_identifier,
890 int message_level,
891 const base::string16& message,
892 int line_number,
893 const GURL& source_url) {
894 FOR_EACH_OBSERVER(Listener,
895 listeners_,
896 OnReportConsoleMessage(this,
897 source_identifier,
898 message_level,
899 message,
900 line_number,
901 source_url));
904 bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message& message) {
905 bool handled = true;
906 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerVersion, message)
907 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetClientDocuments,
908 OnGetClientDocuments)
909 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ActivateEventFinished,
910 OnActivateEventFinished)
911 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_InstallEventFinished,
912 OnInstallEventFinished)
913 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FetchEventFinished,
914 OnFetchEventFinished)
915 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SyncEventFinished,
916 OnSyncEventFinished)
917 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_NotificationClickEventFinished,
918 OnNotificationClickEventFinished)
919 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PushEventFinished,
920 OnPushEventFinished)
921 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GeofencingEventFinished,
922 OnGeofencingEventFinished)
923 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_CrossOriginConnectEventFinished,
924 OnCrossOriginConnectEventFinished)
925 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_OpenWindow,
926 OnOpenWindow)
927 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToDocument,
928 OnPostMessageToDocument)
929 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FocusClient,
930 OnFocusClient)
931 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting,
932 OnSkipWaiting)
933 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients,
934 OnClaimClients)
935 IPC_MESSAGE_UNHANDLED(handled = false)
936 IPC_END_MESSAGE_MAP()
937 return handled;
940 void ServiceWorkerVersion::OnStartMessageSent(
941 ServiceWorkerStatusCode status) {
942 if (status != SERVICE_WORKER_OK)
943 RunCallbacks(this, &start_callbacks_, status);
946 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
947 int active_version_id,
948 const StatusCallback& callback) {
949 DCHECK_EQ(RUNNING, running_status())
950 << "Worker stopped too soon after it was started.";
952 int request_id = install_callbacks_.Add(new StatusCallback(callback));
953 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
954 ServiceWorkerMsg_InstallEvent(request_id, active_version_id));
955 if (status != SERVICE_WORKER_OK) {
956 install_callbacks_.Remove(request_id);
957 RunSoon(base::Bind(callback, status));
961 void ServiceWorkerVersion::DispatchActivateEventAfterStartWorker(
962 const StatusCallback& callback) {
963 DCHECK_EQ(RUNNING, running_status())
964 << "Worker stopped too soon after it was started.";
966 int request_id = activate_callbacks_.Add(new StatusCallback(callback));
967 ServiceWorkerStatusCode status =
968 embedded_worker_->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id));
969 if (status != SERVICE_WORKER_OK) {
970 activate_callbacks_.Remove(request_id);
971 RunSoon(base::Bind(callback, status));
975 void ServiceWorkerVersion::OnGetClientDocuments(int request_id) {
976 if (controllee_by_id_.IsEmpty()) {
977 if (running_status() == RUNNING) {
978 embedded_worker_->SendMessage(
979 ServiceWorkerMsg_DidGetClientDocuments(request_id,
980 std::vector<ServiceWorkerClientInfo>()));
982 return;
984 scoped_refptr<GetClientDocumentsCallback> callback(
985 new GetClientDocumentsCallback(request_id, this));
986 ControlleeByIDMap::iterator it(&controllee_by_id_);
987 TRACE_EVENT0("ServiceWorker",
988 "ServiceWorkerVersion::OnGetClientDocuments");
989 while (!it.IsAtEnd()) {
990 // TODO(mlamouri): we could coalesce those requests into one.
991 it.GetCurrentValue()->GetClientInfo(
992 base::Bind(&ServiceWorkerVersion::DidGetClientInfo,
993 weak_factory_.GetWeakPtr(), it.GetCurrentKey(), callback));
994 it.Advance();
998 void ServiceWorkerVersion::OnActivateEventFinished(
999 int request_id,
1000 blink::WebServiceWorkerEventResult result) {
1001 DCHECK(ACTIVATING == status() ||
1002 REDUNDANT == status()) << status();
1003 TRACE_EVENT0("ServiceWorker",
1004 "ServiceWorkerVersion::OnActivateEventFinished");
1006 StatusCallback* callback = activate_callbacks_.Lookup(request_id);
1007 if (!callback) {
1008 NOTREACHED() << "Got unexpected message: " << request_id;
1009 return;
1011 ServiceWorkerStatusCode rv = SERVICE_WORKER_OK;
1012 if (result == blink::WebServiceWorkerEventResultRejected ||
1013 status() != ACTIVATING) {
1014 rv = SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED;
1017 scoped_refptr<ServiceWorkerVersion> protect(this);
1018 callback->Run(rv);
1019 RemoveCallbackAndStopIfDoomed(&activate_callbacks_, request_id);
1022 void ServiceWorkerVersion::OnInstallEventFinished(
1023 int request_id,
1024 blink::WebServiceWorkerEventResult result) {
1025 // Status is REDUNDANT if the worker was doomed while handling the install
1026 // event, and finished handling before being terminated.
1027 DCHECK(status() == INSTALLING || status() == REDUNDANT) << status();
1028 TRACE_EVENT0("ServiceWorker",
1029 "ServiceWorkerVersion::OnInstallEventFinished");
1031 StatusCallback* callback = install_callbacks_.Lookup(request_id);
1032 if (!callback) {
1033 NOTREACHED() << "Got unexpected message: " << request_id;
1034 return;
1036 ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
1037 if (result == blink::WebServiceWorkerEventResultRejected)
1038 status = SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED;
1040 scoped_refptr<ServiceWorkerVersion> protect(this);
1041 callback->Run(status);
1042 RemoveCallbackAndStopIfDoomed(&install_callbacks_, request_id);
1045 void ServiceWorkerVersion::OnFetchEventFinished(
1046 int request_id,
1047 ServiceWorkerFetchEventResult result,
1048 const ServiceWorkerResponse& response) {
1049 TRACE_EVENT1("ServiceWorker",
1050 "ServiceWorkerVersion::OnFetchEventFinished",
1051 "Request id", request_id);
1052 FetchCallback* callback = fetch_callbacks_.Lookup(request_id);
1053 if (!callback) {
1054 NOTREACHED() << "Got unexpected message: " << request_id;
1055 return;
1058 scoped_refptr<ServiceWorkerVersion> protect(this);
1059 callback->Run(SERVICE_WORKER_OK, result, response);
1060 RemoveCallbackAndStopIfDoomed(&fetch_callbacks_, request_id);
1063 void ServiceWorkerVersion::OnSyncEventFinished(
1064 int request_id) {
1065 TRACE_EVENT1("ServiceWorker",
1066 "ServiceWorkerVersion::OnSyncEventFinished",
1067 "Request id", request_id);
1068 StatusCallback* callback = sync_callbacks_.Lookup(request_id);
1069 if (!callback) {
1070 NOTREACHED() << "Got unexpected message: " << request_id;
1071 return;
1074 scoped_refptr<ServiceWorkerVersion> protect(this);
1075 callback->Run(SERVICE_WORKER_OK);
1076 RemoveCallbackAndStopIfDoomed(&sync_callbacks_, request_id);
1079 void ServiceWorkerVersion::OnNotificationClickEventFinished(
1080 int request_id) {
1081 TRACE_EVENT1("ServiceWorker",
1082 "ServiceWorkerVersion::OnNotificationClickEventFinished",
1083 "Request id", request_id);
1084 StatusCallback* callback = notification_click_callbacks_.Lookup(request_id);
1085 if (!callback) {
1086 NOTREACHED() << "Got unexpected message: " << request_id;
1087 return;
1090 scoped_refptr<ServiceWorkerVersion> protect(this);
1091 callback->Run(SERVICE_WORKER_OK);
1092 RemoveCallbackAndStopIfDoomed(&notification_click_callbacks_, request_id);
1095 void ServiceWorkerVersion::OnPushEventFinished(
1096 int request_id,
1097 blink::WebServiceWorkerEventResult result) {
1098 TRACE_EVENT1("ServiceWorker",
1099 "ServiceWorkerVersion::OnPushEventFinished",
1100 "Request id", request_id);
1101 StatusCallback* callback = push_callbacks_.Lookup(request_id);
1102 if (!callback) {
1103 NOTREACHED() << "Got unexpected message: " << request_id;
1104 return;
1106 ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
1107 if (result == blink::WebServiceWorkerEventResultRejected)
1108 status = SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED;
1110 scoped_refptr<ServiceWorkerVersion> protect(this);
1111 callback->Run(status);
1112 RemoveCallbackAndStopIfDoomed(&push_callbacks_, request_id);
1115 void ServiceWorkerVersion::OnGeofencingEventFinished(int request_id) {
1116 TRACE_EVENT1("ServiceWorker",
1117 "ServiceWorkerVersion::OnGeofencingEventFinished",
1118 "Request id",
1119 request_id);
1120 StatusCallback* callback = geofencing_callbacks_.Lookup(request_id);
1121 if (!callback) {
1122 NOTREACHED() << "Got unexpected message: " << request_id;
1123 return;
1126 scoped_refptr<ServiceWorkerVersion> protect(this);
1127 callback->Run(SERVICE_WORKER_OK);
1128 RemoveCallbackAndStopIfDoomed(&geofencing_callbacks_, request_id);
1131 void ServiceWorkerVersion::OnCrossOriginConnectEventFinished(
1132 int request_id,
1133 bool accept_connection) {
1134 TRACE_EVENT1("ServiceWorker",
1135 "ServiceWorkerVersion::OnCrossOriginConnectEventFinished",
1136 "Request id", request_id);
1137 CrossOriginConnectCallback* callback =
1138 cross_origin_connect_callbacks_.Lookup(request_id);
1139 if (!callback) {
1140 NOTREACHED() << "Got unexpected message: " << request_id;
1141 return;
1144 scoped_refptr<ServiceWorkerVersion> protect(this);
1145 callback->Run(SERVICE_WORKER_OK, accept_connection);
1146 RemoveCallbackAndStopIfDoomed(&cross_origin_connect_callbacks_, request_id);
1149 void ServiceWorkerVersion::OnOpenWindow(int request_id, const GURL& url) {
1150 // Just abort if we are shutting down.
1151 if (!context_)
1152 return;
1154 if (url.GetOrigin() != script_url_.GetOrigin()) {
1155 // There should be a same origin check by Blink, if the request is still not
1156 // same origin, the process might be compromised and should be eliminated.
1157 DVLOG(1) << "Received a cross origin openWindow() request from a service "
1158 "worker. Killing associated process.";
1159 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
1160 base::Bind(&KillEmbeddedWorkerProcess,
1161 embedded_worker_->process_id(),
1162 RESULT_CODE_KILLED_BAD_MESSAGE));
1163 return;
1166 BrowserThread::PostTask(
1167 BrowserThread::UI, FROM_HERE,
1168 base::Bind(&OpenWindowOnUI,
1169 url,
1170 script_url_,
1171 embedded_worker_->process_id(),
1172 make_scoped_refptr(context_->wrapper()),
1173 base::Bind(&ServiceWorkerVersion::DidOpenWindow,
1174 weak_factory_.GetWeakPtr(),
1175 request_id)));
1178 void ServiceWorkerVersion::DidOpenWindow(int request_id,
1179 int render_process_id,
1180 int render_frame_id) {
1181 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1183 if (running_status() != RUNNING)
1184 return;
1186 if (render_process_id == ChildProcessHost::kInvalidUniqueID &&
1187 render_frame_id == MSG_ROUTING_NONE) {
1188 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowError(request_id));
1189 return;
1192 for (const auto& it : controllee_map_) {
1193 const ServiceWorkerProviderHost* provider_host = it.first;
1194 if (provider_host->process_id() != render_process_id ||
1195 provider_host->frame_id() != render_frame_id) {
1196 continue;
1199 // it.second is the client_id associated with the provider_host.
1200 provider_host->GetClientInfo(
1201 base::Bind(&ServiceWorkerVersion::OnOpenWindowFinished,
1202 weak_factory_.GetWeakPtr(), request_id, it.second));
1203 return;
1206 // If here, it means that no provider_host was found, in which case, the
1207 // renderer should still be informed that the window was opened.
1208 OnOpenWindowFinished(request_id, 0, ServiceWorkerClientInfo());
1211 void ServiceWorkerVersion::OnOpenWindowFinished(
1212 int request_id,
1213 int client_id,
1214 const ServiceWorkerClientInfo& client_info) {
1215 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1217 if (running_status() != RUNNING)
1218 return;
1220 ServiceWorkerClientInfo client(client_info);
1222 // If the |client_info| is empty, it means that the opened window wasn't
1223 // controlled but the action still succeeded. The renderer process is
1224 // expecting an empty client in such case.
1225 if (!client.IsEmpty())
1226 client.client_id = client_id;
1228 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowResponse(
1229 request_id, client));
1232 void ServiceWorkerVersion::OnPostMessageToDocument(
1233 int client_id,
1234 const base::string16& message,
1235 const std::vector<int>& sent_message_port_ids) {
1236 TRACE_EVENT1("ServiceWorker",
1237 "ServiceWorkerVersion::OnPostMessageToDocument",
1238 "Client id", client_id);
1239 ServiceWorkerProviderHost* provider_host =
1240 controllee_by_id_.Lookup(client_id);
1241 if (!provider_host) {
1242 // The client may already have been closed, just ignore.
1243 return;
1245 provider_host->PostMessage(message, sent_message_port_ids);
1248 void ServiceWorkerVersion::OnFocusClient(int request_id, int client_id) {
1249 TRACE_EVENT2("ServiceWorker",
1250 "ServiceWorkerVersion::OnFocusDocument",
1251 "Request id", request_id,
1252 "Client id", client_id);
1253 ServiceWorkerProviderHost* provider_host =
1254 controllee_by_id_.Lookup(client_id);
1255 if (!provider_host) {
1256 // The client may already have been closed, just ignore.
1257 return;
1260 provider_host->Focus(
1261 base::Bind(&ServiceWorkerVersion::OnFocusClientFinished,
1262 weak_factory_.GetWeakPtr(),
1263 request_id));
1266 void ServiceWorkerVersion::OnFocusClientFinished(int request_id, bool result) {
1267 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1269 if (running_status() != RUNNING)
1270 return;
1272 embedded_worker_->SendMessage(ServiceWorkerMsg_FocusClientResponse(
1273 request_id, result));
1276 void ServiceWorkerVersion::OnSkipWaiting(int request_id) {
1277 skip_waiting_ = true;
1278 if (status_ != INSTALLED)
1279 return DidSkipWaiting(request_id);
1281 if (!context_)
1282 return;
1283 ServiceWorkerRegistration* registration =
1284 context_->GetLiveRegistration(registration_id_);
1285 if (!registration)
1286 return;
1287 pending_skip_waiting_requests_.push_back(request_id);
1288 if (pending_skip_waiting_requests_.size() == 1)
1289 registration->ActivateWaitingVersionWhenReady();
1292 void ServiceWorkerVersion::DidSkipWaiting(int request_id) {
1293 if (running_status() == STARTING || running_status() == RUNNING)
1294 embedded_worker_->SendMessage(ServiceWorkerMsg_DidSkipWaiting(request_id));
1297 void ServiceWorkerVersion::OnClaimClients(int request_id) {
1298 StatusCallback callback = base::Bind(&ServiceWorkerVersion::DidClaimClients,
1299 weak_factory_.GetWeakPtr(), request_id);
1300 if (status_ != ACTIVATING && status_ != ACTIVATED) {
1301 callback.Run(SERVICE_WORKER_ERROR_STATE);
1302 return;
1304 if (!context_) {
1305 callback.Run(SERVICE_WORKER_ERROR_ABORT);
1306 return;
1309 ServiceWorkerRegistration* registration =
1310 context_->GetLiveRegistration(registration_id_);
1311 if (!registration) {
1312 callback.Run(SERVICE_WORKER_ERROR_ABORT);
1313 return;
1315 registration->ClaimClients(callback);
1318 void ServiceWorkerVersion::DidClaimClients(
1319 int request_id, ServiceWorkerStatusCode status) {
1320 if (status == SERVICE_WORKER_ERROR_STATE) {
1321 embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
1322 request_id, blink::WebServiceWorkerError::ErrorTypeState,
1323 base::ASCIIToUTF16(kClaimClientsStateErrorMesage)));
1324 return;
1326 if (status == SERVICE_WORKER_ERROR_ABORT) {
1327 embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
1328 request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
1329 base::ASCIIToUTF16(kClaimClientsShutdownErrorMesage)));
1330 return;
1332 DCHECK(status == SERVICE_WORKER_OK);
1333 embedded_worker_->SendMessage(ServiceWorkerMsg_DidClaimClients(request_id));
1336 void ServiceWorkerVersion::DidGetClientInfo(
1337 int client_id,
1338 scoped_refptr<GetClientDocumentsCallback> callback,
1339 const ServiceWorkerClientInfo& info) {
1340 // If the request to the provider_host returned an empty
1341 // ServiceWorkerClientInfo, that means that it wasn't possible to associate
1342 // it with a valid RenderFrameHost. It might be because the frame was killed
1343 // or navigated in between.
1344 if (info.IsEmpty())
1345 return;
1347 // We can get info for a frame that was navigating end ended up with a
1348 // different URL than expected. In such case, we should make sure to not
1349 // expose cross-origin WindowClient.
1350 if (info.url.GetOrigin() != script_url_.GetOrigin())
1351 return;
1353 callback->AddClientInfo(client_id, info);
1356 void ServiceWorkerVersion::ScheduleStopWorker() {
1357 if (running_status() != RUNNING)
1358 return;
1359 stop_worker_timer_.Stop();
1360 stop_worker_timer_.Start(
1361 FROM_HERE, base::TimeDelta::FromSeconds(
1362 is_doomed_ ? kStopDoomedWorkerDelay : kStopWorkerDelay),
1363 base::Bind(&ServiceWorkerVersion::StopWorkerIfIdle,
1364 weak_factory_.GetWeakPtr()));
1367 void ServiceWorkerVersion::StopWorkerIfIdle() {
1368 // Reschedule the stop the worker while there're inflight requests.
1369 // (Note: we'll probably need to revisit this so that we can kill 'bad' SW.
1370 // See https://github.com/slightlyoff/ServiceWorker/issues/527)
1371 if (HasInflightRequests()) {
1372 ScheduleStopWorker();
1373 return;
1375 if (running_status() == STOPPED || running_status() == STOPPING ||
1376 !stop_callbacks_.empty()) {
1377 return;
1379 embedded_worker_->StopIfIdle();
1382 bool ServiceWorkerVersion::HasInflightRequests() const {
1383 return
1384 !activate_callbacks_.IsEmpty() ||
1385 !install_callbacks_.IsEmpty() ||
1386 !fetch_callbacks_.IsEmpty() ||
1387 !sync_callbacks_.IsEmpty() ||
1388 !notification_click_callbacks_.IsEmpty() ||
1389 !push_callbacks_.IsEmpty() ||
1390 !geofencing_callbacks_.IsEmpty() ||
1391 !cross_origin_connect_callbacks_.IsEmpty() ||
1392 !streaming_url_request_jobs_.empty();
1395 void ServiceWorkerVersion::DoomInternal() {
1396 DCHECK(is_doomed_);
1397 DCHECK(!HasControllee());
1398 SetStatus(REDUNDANT);
1399 StopWorkerIfIdle();
1400 if (!context_)
1401 return;
1402 std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
1403 script_cache_map_.GetResources(&resources);
1404 context_->storage()->PurgeResources(resources);
1407 template <typename IDMAP>
1408 void ServiceWorkerVersion::RemoveCallbackAndStopIfDoomed(
1409 IDMAP* callbacks,
1410 int request_id) {
1411 callbacks->Remove(request_id);
1412 if (is_doomed_) {
1413 // The stop should be already scheduled, but try to stop immediately, in
1414 // order to release worker resources soon.
1415 StopWorkerIfIdle();
1419 } // namespace content