[refactor] More post-NSS WebCrypto cleanups (utility functions).
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_version.cc
blob8f6aa3c001dd9c437098907c18e64e41d554fb57
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 <algorithm>
8 #include <map>
9 #include <string>
11 #include "base/command_line.h"
12 #include "base/guid.h"
13 #include "base/location.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/thread_task_runner_handle.h"
21 #include "base/time/time.h"
22 #include "content/browser/bad_message.h"
23 #include "content/browser/child_process_security_policy_impl.h"
24 #include "content/browser/message_port_message_filter.h"
25 #include "content/browser/message_port_service.h"
26 #include "content/browser/service_worker/embedded_worker_instance.h"
27 #include "content/browser/service_worker/embedded_worker_registry.h"
28 #include "content/browser/service_worker/service_worker_context_core.h"
29 #include "content/browser/service_worker/service_worker_context_wrapper.h"
30 #include "content/browser/service_worker/service_worker_metrics.h"
31 #include "content/browser/service_worker/service_worker_registration.h"
32 #include "content/browser/storage_partition_impl.h"
33 #include "content/common/service_worker/service_worker_messages.h"
34 #include "content/common/service_worker/service_worker_type_converters.h"
35 #include "content/common/service_worker/service_worker_utils.h"
36 #include "content/public/browser/browser_thread.h"
37 #include "content/public/browser/content_browser_client.h"
38 #include "content/public/browser/page_navigator.h"
39 #include "content/public/browser/render_frame_host.h"
40 #include "content/public/browser/render_process_host.h"
41 #include "content/public/browser/web_contents.h"
42 #include "content/public/browser/web_contents_observer.h"
43 #include "content/public/common/background_sync.mojom.h"
44 #include "content/public/common/child_process_host.h"
45 #include "content/public/common/content_client.h"
46 #include "content/public/common/content_switches.h"
47 #include "content/public/common/result_codes.h"
48 #include "content/public/common/service_registry.h"
49 #include "mojo/common/common_type_converters.h"
50 #include "mojo/common/url_type_converters.h"
51 #include "net/http/http_response_headers.h"
52 #include "net/http/http_response_info.h"
54 namespace content {
56 using StatusCallback = ServiceWorkerVersion::StatusCallback;
57 using ServiceWorkerClients = std::vector<ServiceWorkerClientInfo>;
58 using GetClientsCallback =
59 base::Callback<void(scoped_ptr<ServiceWorkerClients>)>;
61 namespace {
63 // Delay between the timeout timer firing.
64 const int kTimeoutTimerDelaySeconds = 30;
66 // Time to wait until stopping an idle worker.
67 const int kIdleWorkerTimeoutSeconds = 30;
69 // Time until a stopping worker is considered stalled.
70 const int kStopWorkerTimeoutSeconds = 30;
72 // Default delay for scheduled update.
73 const int kUpdateDelaySeconds = 1;
75 // Timeout for waiting for a response to a ping.
76 const int kPingTimeoutSeconds = 30;
78 // If the SW was destructed while starting up, how many seconds it
79 // had to start up for this to be considered a timeout occurrence.
80 const int kDestructedStartingWorkerTimeoutThresholdSeconds = 5;
82 const char kClaimClientsStateErrorMesage[] =
83 "Only the active worker can claim clients.";
85 const char kClaimClientsShutdownErrorMesage[] =
86 "Failed to claim clients due to Service Worker system shutdown.";
88 void RunSoon(const base::Closure& callback) {
89 if (!callback.is_null())
90 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
93 template <typename CallbackArray, typename Arg>
94 void RunCallbacks(ServiceWorkerVersion* version,
95 CallbackArray* callbacks_ptr,
96 const Arg& arg) {
97 CallbackArray callbacks;
98 callbacks.swap(*callbacks_ptr);
99 for (const auto& callback : callbacks)
100 callback.Run(arg);
103 template <typename IDMAP, typename... Params>
104 void RunIDMapCallbacks(IDMAP* requests, const Params&... params) {
105 typename IDMAP::iterator iter(requests);
106 while (!iter.IsAtEnd()) {
107 iter.GetCurrentValue()->callback.Run(params...);
108 iter.Advance();
110 requests->Clear();
113 template <typename CallbackType, typename... Params>
114 bool RunIDMapCallback(IDMap<CallbackType, IDMapOwnPointer>* requests,
115 int request_id,
116 const Params&... params) {
117 CallbackType* request = requests->Lookup(request_id);
118 if (!request)
119 return false;
121 request->callback.Run(params...);
122 requests->Remove(request_id);
123 return true;
126 void RunStartWorkerCallback(
127 const StatusCallback& callback,
128 scoped_refptr<ServiceWorkerRegistration> protect,
129 ServiceWorkerStatusCode status) {
130 callback.Run(status);
133 // A callback adapter to start a |task| after StartWorker.
134 void RunTaskAfterStartWorker(
135 base::WeakPtr<ServiceWorkerVersion> version,
136 const StatusCallback& error_callback,
137 const base::Closure& task,
138 ServiceWorkerStatusCode status) {
139 if (status != SERVICE_WORKER_OK) {
140 if (!error_callback.is_null())
141 error_callback.Run(status);
142 return;
144 if (version->running_status() != ServiceWorkerVersion::RUNNING) {
145 // We've tried to start the worker (and it has succeeded), but
146 // it looks it's not running yet.
147 NOTREACHED() << "The worker's not running after successful StartWorker";
148 if (!error_callback.is_null())
149 error_callback.Run(SERVICE_WORKER_ERROR_START_WORKER_FAILED);
150 return;
152 task.Run();
155 void RunErrorFetchCallback(const ServiceWorkerVersion::FetchCallback& callback,
156 ServiceWorkerStatusCode status) {
157 callback.Run(status,
158 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
159 ServiceWorkerResponse());
162 void RunErrorMessageCallback(
163 const std::vector<TransferredMessagePort>& sent_message_ports,
164 const ServiceWorkerVersion::StatusCallback& callback,
165 ServiceWorkerStatusCode status) {
166 // Transfering the message ports failed, so destroy the ports.
167 for (const TransferredMessagePort& port : sent_message_ports) {
168 MessagePortService::GetInstance()->ClosePort(port.id);
170 callback.Run(status);
173 void RunErrorServicePortConnectCallback(
174 const ServiceWorkerVersion::ServicePortConnectCallback& callback,
175 ServiceWorkerStatusCode status) {
176 callback.Run(status, false /* accept_connection */, base::string16(),
177 base::string16());
180 using OpenURLCallback = base::Callback<void(int, int)>;
182 // The OpenURLObserver class is a WebContentsObserver that will wait for a
183 // WebContents to be initialized, run the |callback| passed to its constructor
184 // then self destroy.
185 // The callback will receive the process and frame ids. If something went wrong
186 // those will be (kInvalidUniqueID, MSG_ROUTING_NONE).
187 // The callback will be called in the IO thread.
188 class OpenURLObserver : public WebContentsObserver {
189 public:
190 OpenURLObserver(WebContents* web_contents, const OpenURLCallback& callback)
191 : WebContentsObserver(web_contents), callback_(callback) {}
193 void DidCommitProvisionalLoadForFrame(
194 RenderFrameHost* render_frame_host,
195 const GURL& validated_url,
196 ui::PageTransition transition_type) override {
197 DCHECK(web_contents());
199 if (render_frame_host != web_contents()->GetMainFrame())
200 return;
202 RunCallback(render_frame_host->GetProcess()->GetID(),
203 render_frame_host->GetRoutingID());
206 void RenderProcessGone(base::TerminationStatus status) override {
207 RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
210 void WebContentsDestroyed() override {
211 RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
214 private:
215 void RunCallback(int render_process_id, int render_frame_id) {
216 // After running the callback, |this| will stop observing, thus
217 // web_contents() should return nullptr and |RunCallback| should no longer
218 // be called. Then, |this| will self destroy.
219 DCHECK(web_contents());
221 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
222 base::Bind(callback_,
223 render_process_id,
224 render_frame_id));
225 Observe(nullptr);
226 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
229 const OpenURLCallback callback_;
231 DISALLOW_COPY_AND_ASSIGN(OpenURLObserver);
234 void DidOpenURL(const OpenURLCallback& callback, WebContents* web_contents) {
235 DCHECK(web_contents);
237 new OpenURLObserver(web_contents, callback);
240 void NavigateClientOnUI(const GURL& url,
241 const GURL& script_url,
242 int process_id,
243 int frame_id,
244 const OpenURLCallback& callback) {
245 DCHECK_CURRENTLY_ON(BrowserThread::UI);
247 RenderFrameHost* render_frame_host =
248 RenderFrameHost::FromID(process_id, frame_id);
249 WebContents* web_contents =
250 WebContents::FromRenderFrameHost(render_frame_host);
252 if (!render_frame_host || !web_contents) {
253 BrowserThread::PostTask(
254 BrowserThread::IO, FROM_HERE,
255 base::Bind(callback, ChildProcessHost::kInvalidUniqueID,
256 MSG_ROUTING_NONE));
257 return;
260 OpenURLParams params(
261 url, Referrer::SanitizeForRequest(
262 url, Referrer(script_url, blink::WebReferrerPolicyDefault)),
263 CURRENT_TAB, ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
264 true /* is_renderer_initiated */);
265 web_contents->OpenURL(params);
266 DidOpenURL(callback, web_contents);
269 void OpenWindowOnUI(
270 const GURL& url,
271 const GURL& script_url,
272 int process_id,
273 const scoped_refptr<ServiceWorkerContextWrapper>& context_wrapper,
274 const OpenURLCallback& callback) {
275 DCHECK_CURRENTLY_ON(BrowserThread::UI);
277 BrowserContext* browser_context = context_wrapper->storage_partition()
278 ? context_wrapper->storage_partition()->browser_context()
279 : nullptr;
280 // We are shutting down.
281 if (!browser_context)
282 return;
284 RenderProcessHost* render_process_host =
285 RenderProcessHost::FromID(process_id);
286 if (render_process_host->IsForGuestsOnly()) {
287 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
288 base::Bind(callback,
289 ChildProcessHost::kInvalidUniqueID,
290 MSG_ROUTING_NONE));
291 return;
294 OpenURLParams params(
295 url, Referrer::SanitizeForRequest(
296 url, Referrer(script_url, blink::WebReferrerPolicyDefault)),
297 NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
298 true /* is_renderer_initiated */);
300 GetContentClient()->browser()->OpenURL(
301 browser_context, params,
302 base::Bind(&DidOpenURL, callback));
305 void KillEmbeddedWorkerProcess(int process_id, ResultCode code) {
306 DCHECK_CURRENTLY_ON(BrowserThread::UI);
307 RenderProcessHost* render_process_host =
308 RenderProcessHost::FromID(process_id);
309 if (render_process_host->GetHandle() != base::kNullProcessHandle) {
310 bad_message::ReceivedBadMessage(render_process_host,
311 bad_message::SERVICE_WORKER_BAD_URL);
315 void ClearTick(base::TimeTicks* time) {
316 *time = base::TimeTicks();
319 void RestartTick(base::TimeTicks* time) {
320 *time = base::TimeTicks().Now();
323 base::TimeDelta GetTickDuration(const base::TimeTicks& time) {
324 if (time.is_null())
325 return base::TimeDelta();
326 return base::TimeTicks().Now() - time;
329 void OnGetWindowClientsFromUI(
330 // The tuple contains process_id, frame_id, client_uuid.
331 const std::vector<base::Tuple<int, int, std::string>>& clients_info,
332 const GURL& script_url,
333 const GetClientsCallback& callback) {
334 scoped_ptr<ServiceWorkerClients> clients(new ServiceWorkerClients);
336 for (const auto& it : clients_info) {
337 ServiceWorkerClientInfo info =
338 ServiceWorkerProviderHost::GetWindowClientInfoOnUI(base::get<0>(it),
339 base::get<1>(it));
341 // If the request to the provider_host returned an empty
342 // ServiceWorkerClientInfo, that means that it wasn't possible to associate
343 // it with a valid RenderFrameHost. It might be because the frame was killed
344 // or navigated in between.
345 if (info.IsEmpty())
346 continue;
348 // We can get info for a frame that was navigating end ended up with a
349 // different URL than expected. In such case, we should make sure to not
350 // expose cross-origin WindowClient.
351 if (info.url.GetOrigin() != script_url.GetOrigin())
352 continue;
354 info.client_uuid = base::get<2>(it);
355 clients->push_back(info);
358 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
359 base::Bind(callback, base::Passed(&clients)));
362 void AddWindowClient(
363 ServiceWorkerProviderHost* host,
364 std::vector<base::Tuple<int, int, std::string>>* client_info) {
365 if (host->client_type() != blink::WebServiceWorkerClientTypeWindow)
366 return;
367 client_info->push_back(base::MakeTuple(host->process_id(), host->frame_id(),
368 host->client_uuid()));
371 void AddNonWindowClient(ServiceWorkerProviderHost* host,
372 const ServiceWorkerClientQueryOptions& options,
373 ServiceWorkerClients* clients) {
374 blink::WebServiceWorkerClientType host_client_type = host->client_type();
375 if (host_client_type == blink::WebServiceWorkerClientTypeWindow)
376 return;
377 if (options.client_type != blink::WebServiceWorkerClientTypeAll &&
378 options.client_type != host_client_type)
379 return;
381 ServiceWorkerClientInfo client_info(blink::WebPageVisibilityStateHidden,
382 false, // is_focused
383 host->document_url(),
384 REQUEST_CONTEXT_FRAME_TYPE_NONE,
385 base::TimeTicks(), host_client_type);
386 client_info.client_uuid = host->client_uuid();
387 clients->push_back(client_info);
390 bool IsInstalled(ServiceWorkerVersion::Status status) {
391 switch (status) {
392 case ServiceWorkerVersion::NEW:
393 case ServiceWorkerVersion::INSTALLING:
394 case ServiceWorkerVersion::REDUNDANT:
395 return false;
396 case ServiceWorkerVersion::INSTALLED:
397 case ServiceWorkerVersion::ACTIVATING:
398 case ServiceWorkerVersion::ACTIVATED:
399 return true;
401 NOTREACHED() << "Unexpected status: " << status;
402 return false;
405 struct ServiceWorkerClientInfoSortMRU {
406 bool operator()(const ServiceWorkerClientInfo& a,
407 const ServiceWorkerClientInfo& b) const {
408 return a.last_focus_time > b.last_focus_time;
412 } // namespace
414 const int ServiceWorkerVersion::kStartWorkerTimeoutMinutes = 5;
415 const int ServiceWorkerVersion::kRequestTimeoutMinutes = 5;
417 class ServiceWorkerVersion::Metrics {
418 public:
419 using EventType = ServiceWorkerMetrics::EventType;
420 explicit Metrics(ServiceWorkerVersion* owner) : owner_(owner) {}
421 ~Metrics() {
422 if (owner_->should_exclude_from_uma_)
423 return;
424 for (const auto& ev : event_stats_) {
425 ServiceWorkerMetrics::RecordEventHandledRatio(
426 ev.first, ev.second.handled_events, ev.second.fired_events);
430 void RecordEventHandledStatus(EventType event, bool handled) {
431 event_stats_[event].fired_events++;
432 if (handled)
433 event_stats_[event].handled_events++;
436 void NotifyStopping() {
437 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STOPPING;
440 void NotifyStopped() {
441 switch (stop_status_) {
442 case ServiceWorkerMetrics::STOP_STATUS_STOPPED:
443 case ServiceWorkerMetrics::STOP_STATUS_STALLED_THEN_STOPPED:
444 return;
445 case ServiceWorkerMetrics::STOP_STATUS_STOPPING:
446 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STOPPED;
447 break;
448 case ServiceWorkerMetrics::STOP_STATUS_STALLED:
449 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STALLED_THEN_STOPPED;
450 break;
451 case ServiceWorkerMetrics::NUM_STOP_STATUS_TYPES:
452 NOTREACHED();
453 return;
455 if (IsInstalled(owner_->status()))
456 ServiceWorkerMetrics::RecordStopWorkerStatus(stop_status_);
459 void NotifyStalledInStopping() {
460 if (stop_status_ != ServiceWorkerMetrics::STOP_STATUS_STOPPING)
461 return;
462 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STALLED;
463 if (IsInstalled(owner_->status()))
464 ServiceWorkerMetrics::RecordStopWorkerStatus(stop_status_);
467 private:
468 struct EventStat {
469 size_t fired_events = 0;
470 size_t handled_events = 0;
473 ServiceWorkerVersion* owner_;
474 std::map<EventType, EventStat> event_stats_;
475 ServiceWorkerMetrics::StopWorkerStatus stop_status_ =
476 ServiceWorkerMetrics::STOP_STATUS_STOPPING;
478 DISALLOW_COPY_AND_ASSIGN(Metrics);
481 // A controller for periodically sending a ping to the worker to see
482 // if the worker is not stalling.
483 class ServiceWorkerVersion::PingController {
484 public:
485 explicit PingController(ServiceWorkerVersion* version) : version_(version) {}
486 ~PingController() {}
488 void Activate() { ping_state_ = PINGING; }
490 void Deactivate() {
491 ClearTick(&ping_time_);
492 ping_state_ = NOT_PINGING;
495 void OnPongReceived() { ClearTick(&ping_time_); }
497 bool IsTimedOut() { return ping_state_ == PING_TIMED_OUT; }
499 // Checks ping status. This is supposed to be called periodically.
500 // This may call:
501 // - OnPingTimeout() if the worker hasn't reponded within a certain period.
502 // - PingWorker() if we're running ping timer and can send next ping.
503 void CheckPingStatus() {
504 if (GetTickDuration(ping_time_) >
505 base::TimeDelta::FromSeconds(kPingTimeoutSeconds)) {
506 ping_state_ = PING_TIMED_OUT;
507 version_->OnPingTimeout();
508 return;
511 // Check if we want to send a next ping.
512 if (ping_state_ != PINGING || !ping_time_.is_null())
513 return;
515 if (version_->PingWorker() != SERVICE_WORKER_OK) {
516 // TODO(falken): Maybe try resending Ping a few times first?
517 ping_state_ = PING_TIMED_OUT;
518 version_->OnPingTimeout();
519 return;
521 RestartTick(&ping_time_);
524 void SimulateTimeoutForTesting() {
525 version_->PingWorker();
526 ping_state_ = PING_TIMED_OUT;
527 version_->OnPingTimeout();
530 private:
531 enum PingState { NOT_PINGING, PINGING, PING_TIMED_OUT };
532 ServiceWorkerVersion* version_; // Not owned.
533 base::TimeTicks ping_time_;
534 PingState ping_state_ = NOT_PINGING;
535 DISALLOW_COPY_AND_ASSIGN(PingController);
538 ServiceWorkerVersion::ServiceWorkerVersion(
539 ServiceWorkerRegistration* registration,
540 const GURL& script_url,
541 int64 version_id,
542 base::WeakPtr<ServiceWorkerContextCore> context)
543 : version_id_(version_id),
544 registration_id_(registration->id()),
545 script_url_(script_url),
546 scope_(registration->pattern()),
547 context_(context),
548 script_cache_map_(this, context),
549 ping_controller_(new PingController(this)),
550 should_exclude_from_uma_(
551 ServiceWorkerMetrics::ShouldExcludeURLFromHistogram(scope_)),
552 weak_factory_(this) {
553 DCHECK(context_);
554 DCHECK(registration);
555 context_->AddLiveVersion(this);
556 embedded_worker_ = context_->embedded_worker_registry()->CreateWorker();
557 embedded_worker_->AddListener(this);
560 ServiceWorkerVersion::~ServiceWorkerVersion() {
561 in_dtor_ = true;
563 // The user may have closed the tab waiting for SW to start up.
564 if (GetTickDuration(start_time_) >
565 base::TimeDelta::FromSeconds(
566 kDestructedStartingWorkerTimeoutThresholdSeconds)) {
567 DCHECK(timeout_timer_.IsRunning());
568 DCHECK(!embedded_worker_->devtools_attached());
569 RecordStartWorkerResult(SERVICE_WORKER_ERROR_TIMEOUT);
572 // Same with stopping.
573 if (GetTickDuration(stop_time_) >
574 base::TimeDelta::FromSeconds(kStopWorkerTimeoutSeconds)) {
575 metrics_->NotifyStalledInStopping();
578 if (context_)
579 context_->RemoveLiveVersion(version_id_);
581 if (running_status() == STARTING || running_status() == RUNNING)
582 embedded_worker_->Stop();
583 embedded_worker_->RemoveListener(this);
586 void ServiceWorkerVersion::SetStatus(Status status) {
587 if (status_ == status)
588 return;
590 status_ = status;
592 if (skip_waiting_ && status_ == ACTIVATED) {
593 for (int request_id : pending_skip_waiting_requests_)
594 DidSkipWaiting(request_id);
595 pending_skip_waiting_requests_.clear();
598 std::vector<base::Closure> callbacks;
599 callbacks.swap(status_change_callbacks_);
600 for (const auto& callback : callbacks)
601 callback.Run();
603 FOR_EACH_OBSERVER(Listener, listeners_, OnVersionStateChanged(this));
606 void ServiceWorkerVersion::RegisterStatusChangeCallback(
607 const base::Closure& callback) {
608 status_change_callbacks_.push_back(callback);
611 ServiceWorkerVersionInfo ServiceWorkerVersion::GetInfo() {
612 DCHECK_CURRENTLY_ON(BrowserThread::IO);
613 ServiceWorkerVersionInfo info(
614 running_status(), status(), script_url(), registration_id(), version_id(),
615 embedded_worker()->process_id(), embedded_worker()->thread_id(),
616 embedded_worker()->worker_devtools_agent_route_id());
617 for (const auto& controllee : controllee_map_) {
618 const ServiceWorkerProviderHost* host = controllee.second;
619 info.clients.insert(std::make_pair(
620 host->client_uuid(),
621 ServiceWorkerVersionInfo::ClientInfo(
622 host->process_id(), host->route_id(), host->provider_type())));
624 if (!main_script_http_info_)
625 return info;
626 info.script_response_time = main_script_http_info_->response_time;
627 if (main_script_http_info_->headers)
628 main_script_http_info_->headers->GetLastModifiedValue(
629 &info.script_last_modified);
630 return info;
633 void ServiceWorkerVersion::StartWorker(const StatusCallback& callback) {
634 DCHECK_CURRENTLY_ON(BrowserThread::IO);
635 if (!context_) {
636 RecordStartWorkerResult(SERVICE_WORKER_ERROR_ABORT);
637 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_ABORT));
638 return;
640 if (is_redundant()) {
641 RecordStartWorkerResult(SERVICE_WORKER_ERROR_REDUNDANT);
642 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_REDUNDANT));
643 return;
645 // Check that the worker is allowed to start on the given scope. Since this
646 // worker might not be used for a specific frame/process, use -1.
647 // resource_context() can return null in unit tests.
648 if (context_->wrapper()->resource_context() &&
649 !GetContentClient()->browser()->AllowServiceWorker(
650 scope_, scope_, context_->wrapper()->resource_context(), -1, -1)) {
651 RecordStartWorkerResult(SERVICE_WORKER_ERROR_DISALLOWED);
652 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_DISALLOWED));
653 return;
656 prestart_status_ = status_;
658 // Ensure the live registration during starting worker so that the worker can
659 // get associated with it in SWDispatcherHost::OnSetHostedVersionId().
660 context_->storage()->FindRegistrationForId(
661 registration_id_,
662 scope_.GetOrigin(),
663 base::Bind(&ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker,
664 weak_factory_.GetWeakPtr(),
665 callback));
668 void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) {
669 if (running_status() == STOPPED) {
670 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
671 return;
673 if (stop_callbacks_.empty()) {
674 ServiceWorkerStatusCode status = embedded_worker_->Stop();
675 if (status != SERVICE_WORKER_OK) {
676 RunSoon(base::Bind(callback, status));
677 return;
680 stop_callbacks_.push_back(callback);
683 void ServiceWorkerVersion::ScheduleUpdate() {
684 if (!context_)
685 return;
686 if (update_timer_.IsRunning()) {
687 update_timer_.Reset();
688 return;
690 if (is_update_scheduled_)
691 return;
692 is_update_scheduled_ = true;
694 // Protect |this| until the timer fires, since we may be stopping
695 // and soon no one might hold a reference to us.
696 context_->ProtectVersion(make_scoped_refptr(this));
697 update_timer_.Start(FROM_HERE,
698 base::TimeDelta::FromSeconds(kUpdateDelaySeconds),
699 base::Bind(&ServiceWorkerVersion::StartUpdate,
700 weak_factory_.GetWeakPtr()));
703 void ServiceWorkerVersion::StartUpdate() {
704 if (!context_)
705 return;
706 context_->storage()->FindRegistrationForId(
707 registration_id_, scope_.GetOrigin(),
708 base::Bind(&ServiceWorkerVersion::FoundRegistrationForUpdate,
709 weak_factory_.GetWeakPtr()));
712 void ServiceWorkerVersion::DeferScheduledUpdate() {
713 if (update_timer_.IsRunning())
714 update_timer_.Reset();
717 void ServiceWorkerVersion::DispatchMessageEvent(
718 const base::string16& message,
719 const std::vector<TransferredMessagePort>& sent_message_ports,
720 const StatusCallback& callback) {
721 for (const TransferredMessagePort& port : sent_message_ports) {
722 MessagePortService::GetInstance()->HoldMessages(port.id);
725 DispatchMessageEventInternal(message, sent_message_ports, callback);
728 void ServiceWorkerVersion::DispatchMessageEventInternal(
729 const base::string16& message,
730 const std::vector<TransferredMessagePort>& sent_message_ports,
731 const StatusCallback& callback) {
732 OnBeginEvent();
733 if (running_status() != RUNNING) {
734 // Schedule calling this method after starting the worker.
735 StartWorker(base::Bind(
736 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
737 base::Bind(&RunErrorMessageCallback, sent_message_ports, callback),
738 base::Bind(&self::DispatchMessageEventInternal,
739 weak_factory_.GetWeakPtr(), message, sent_message_ports,
740 callback)));
741 return;
744 // TODO(kinuko): Cleanup this (and corresponding unit test) when message
745 // event becomes extendable, round-trip event. (crbug.com/498596)
746 RestartTick(&idle_time_);
748 MessagePortMessageFilter* filter =
749 embedded_worker_->message_port_message_filter();
750 std::vector<int> new_routing_ids;
751 filter->UpdateMessagePortsWithNewRoutes(sent_message_ports, &new_routing_ids);
752 ServiceWorkerStatusCode status =
753 embedded_worker_->SendMessage(ServiceWorkerMsg_MessageToWorker(
754 message, sent_message_ports, new_routing_ids));
755 RunSoon(base::Bind(callback, status));
758 void ServiceWorkerVersion::DispatchInstallEvent(
759 const StatusCallback& callback) {
760 OnBeginEvent();
761 DCHECK_EQ(INSTALLING, status()) << status();
763 if (running_status() != RUNNING) {
764 // Schedule calling this method after starting the worker.
765 StartWorker(
766 base::Bind(&RunTaskAfterStartWorker,
767 weak_factory_.GetWeakPtr(),
768 callback,
769 base::Bind(&self::DispatchInstallEventAfterStartWorker,
770 weak_factory_.GetWeakPtr(),
771 callback)));
772 } else {
773 DispatchInstallEventAfterStartWorker(callback);
777 void ServiceWorkerVersion::DispatchActivateEvent(
778 const StatusCallback& callback) {
779 OnBeginEvent();
780 DCHECK_EQ(ACTIVATING, status()) << status();
782 if (running_status() != RUNNING) {
783 // Schedule calling this method after starting the worker.
784 StartWorker(
785 base::Bind(&RunTaskAfterStartWorker,
786 weak_factory_.GetWeakPtr(),
787 callback,
788 base::Bind(&self::DispatchActivateEventAfterStartWorker,
789 weak_factory_.GetWeakPtr(),
790 callback)));
791 } else {
792 DispatchActivateEventAfterStartWorker(callback);
796 void ServiceWorkerVersion::DispatchFetchEvent(
797 const ServiceWorkerFetchRequest& request,
798 const base::Closure& prepare_callback,
799 const FetchCallback& fetch_callback) {
800 OnBeginEvent();
801 DCHECK_EQ(ACTIVATED, status()) << status();
803 if (running_status() != RUNNING) {
804 // Schedule calling this method after starting the worker.
805 StartWorker(base::Bind(&RunTaskAfterStartWorker,
806 weak_factory_.GetWeakPtr(),
807 base::Bind(&RunErrorFetchCallback, fetch_callback),
808 base::Bind(&self::DispatchFetchEvent,
809 weak_factory_.GetWeakPtr(),
810 request,
811 prepare_callback,
812 fetch_callback)));
813 return;
816 prepare_callback.Run();
818 int request_id = AddRequest(fetch_callback, &fetch_requests_, REQUEST_FETCH);
819 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
820 ServiceWorkerMsg_FetchEvent(request_id, request));
821 if (status != SERVICE_WORKER_OK) {
822 fetch_requests_.Remove(request_id);
823 RunSoon(base::Bind(&RunErrorFetchCallback,
824 fetch_callback,
825 SERVICE_WORKER_ERROR_FAILED));
829 void ServiceWorkerVersion::DispatchSyncEvent(
830 BackgroundSyncRegistrationHandle::HandleId handle_id,
831 const StatusCallback& callback) {
832 OnBeginEvent();
833 DCHECK_EQ(ACTIVATED, status()) << status();
834 if (running_status() != RUNNING) {
835 // Schedule calling this method after starting the worker.
836 StartWorker(base::Bind(
837 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(), callback,
838 base::Bind(&self::DispatchSyncEvent, weak_factory_.GetWeakPtr(),
839 handle_id, callback)));
840 return;
843 int request_id = AddRequest(callback, &sync_requests_, REQUEST_SYNC);
844 if (!background_sync_dispatcher_) {
845 embedded_worker_->GetServiceRegistry()->ConnectToRemoteService(
846 mojo::GetProxy(&background_sync_dispatcher_));
847 background_sync_dispatcher_.set_connection_error_handler(base::Bind(
848 &ServiceWorkerVersion::OnBackgroundSyncDispatcherConnectionError,
849 weak_factory_.GetWeakPtr()));
852 background_sync_dispatcher_->Sync(
853 handle_id, base::Bind(&self::OnSyncEventFinished,
854 weak_factory_.GetWeakPtr(), request_id));
857 void ServiceWorkerVersion::DispatchNotificationClickEvent(
858 const StatusCallback& callback,
859 int64_t persistent_notification_id,
860 const PlatformNotificationData& notification_data,
861 int action_index) {
862 OnBeginEvent();
863 DCHECK_EQ(ACTIVATED, status()) << status();
864 if (running_status() != RUNNING) {
865 // Schedule calling this method after starting the worker.
866 StartWorker(base::Bind(
867 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(), callback,
868 base::Bind(&self::DispatchNotificationClickEvent,
869 weak_factory_.GetWeakPtr(), callback,
870 persistent_notification_id, notification_data,
871 action_index)));
872 return;
875 int request_id = AddRequest(callback, &notification_click_requests_,
876 REQUEST_NOTIFICATION_CLICK);
877 ServiceWorkerStatusCode status =
878 embedded_worker_->SendMessage(ServiceWorkerMsg_NotificationClickEvent(
879 request_id, persistent_notification_id, notification_data,
880 action_index));
881 if (status != SERVICE_WORKER_OK) {
882 notification_click_requests_.Remove(request_id);
883 RunSoon(base::Bind(callback, status));
887 void ServiceWorkerVersion::DispatchPushEvent(const StatusCallback& callback,
888 const std::string& data) {
889 OnBeginEvent();
890 DCHECK_EQ(ACTIVATED, status()) << status();
891 if (running_status() != RUNNING) {
892 // Schedule calling this method after starting the worker.
893 StartWorker(base::Bind(&RunTaskAfterStartWorker,
894 weak_factory_.GetWeakPtr(), callback,
895 base::Bind(&self::DispatchPushEvent,
896 weak_factory_.GetWeakPtr(),
897 callback, data)));
898 return;
901 int request_id = AddRequest(callback, &push_requests_, REQUEST_PUSH);
902 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
903 ServiceWorkerMsg_PushEvent(request_id, data));
904 if (status != SERVICE_WORKER_OK) {
905 push_requests_.Remove(request_id);
906 RunSoon(base::Bind(callback, status));
910 void ServiceWorkerVersion::DispatchGeofencingEvent(
911 const StatusCallback& callback,
912 blink::WebGeofencingEventType event_type,
913 const std::string& region_id,
914 const blink::WebCircularGeofencingRegion& region) {
915 OnBeginEvent();
916 DCHECK_EQ(ACTIVATED, status()) << status();
918 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
919 switches::kEnableExperimentalWebPlatformFeatures)) {
920 callback.Run(SERVICE_WORKER_ERROR_ABORT);
921 return;
924 if (running_status() != RUNNING) {
925 // Schedule calling this method after starting the worker.
926 StartWorker(base::Bind(&RunTaskAfterStartWorker,
927 weak_factory_.GetWeakPtr(),
928 callback,
929 base::Bind(&self::DispatchGeofencingEvent,
930 weak_factory_.GetWeakPtr(),
931 callback,
932 event_type,
933 region_id,
934 region)));
935 return;
938 int request_id =
939 AddRequest(callback, &geofencing_requests_, REQUEST_GEOFENCING);
940 ServiceWorkerStatusCode status =
941 embedded_worker_->SendMessage(ServiceWorkerMsg_GeofencingEvent(
942 request_id, event_type, region_id, region));
943 if (status != SERVICE_WORKER_OK) {
944 geofencing_requests_.Remove(request_id);
945 RunSoon(base::Bind(callback, status));
949 void ServiceWorkerVersion::DispatchServicePortConnectEvent(
950 const ServicePortConnectCallback& callback,
951 const GURL& target_url,
952 const GURL& origin,
953 int port_id) {
954 OnBeginEvent();
955 DCHECK_EQ(ACTIVATED, status()) << status();
957 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
958 switches::kEnableExperimentalWebPlatformFeatures)) {
959 callback.Run(SERVICE_WORKER_ERROR_ABORT, false, base::string16(),
960 base::string16());
961 return;
964 if (running_status() != RUNNING) {
965 // Schedule calling this method after starting the worker.
966 StartWorker(
967 base::Bind(&RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
968 base::Bind(&RunErrorServicePortConnectCallback, callback),
969 base::Bind(&self::DispatchServicePortConnectEvent,
970 weak_factory_.GetWeakPtr(), callback, target_url,
971 origin, port_id)));
972 return;
975 int request_id = AddRequest(callback, &service_port_connect_requests_,
976 REQUEST_SERVICE_PORT_CONNECT);
977 if (!service_port_dispatcher_) {
978 embedded_worker_->GetServiceRegistry()->ConnectToRemoteService(
979 mojo::GetProxy(&service_port_dispatcher_));
980 service_port_dispatcher_.set_connection_error_handler(base::Bind(
981 &ServiceWorkerVersion::OnServicePortDispatcherConnectionError,
982 weak_factory_.GetWeakPtr()));
984 service_port_dispatcher_->Connect(
985 mojo::String::From(target_url), mojo::String::From(origin), port_id,
986 base::Bind(&ServiceWorkerVersion::OnServicePortConnectEventFinished,
987 weak_factory_.GetWeakPtr(), request_id));
990 void ServiceWorkerVersion::DispatchCrossOriginMessageEvent(
991 const NavigatorConnectClient& client,
992 const base::string16& message,
993 const std::vector<TransferredMessagePort>& sent_message_ports,
994 const StatusCallback& callback) {
995 OnBeginEvent();
996 // Unlike in the case of DispatchMessageEvent, here the caller is assumed to
997 // have already put all the sent message ports on hold. So no need to do that
998 // here again.
1000 if (running_status() != RUNNING) {
1001 // Schedule calling this method after starting the worker.
1002 StartWorker(base::Bind(
1003 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
1004 base::Bind(&RunErrorMessageCallback, sent_message_ports, callback),
1005 base::Bind(&self::DispatchCrossOriginMessageEvent,
1006 weak_factory_.GetWeakPtr(), client, message,
1007 sent_message_ports, callback)));
1008 return;
1011 MessagePortMessageFilter* filter =
1012 embedded_worker_->message_port_message_filter();
1013 std::vector<int> new_routing_ids;
1014 filter->UpdateMessagePortsWithNewRoutes(sent_message_ports, &new_routing_ids);
1015 ServiceWorkerStatusCode status =
1016 embedded_worker_->SendMessage(ServiceWorkerMsg_CrossOriginMessageToWorker(
1017 client, message, sent_message_ports, new_routing_ids));
1018 RunSoon(base::Bind(callback, status));
1021 void ServiceWorkerVersion::AddControllee(
1022 ServiceWorkerProviderHost* provider_host) {
1023 const std::string& uuid = provider_host->client_uuid();
1024 CHECK(!provider_host->client_uuid().empty());
1025 DCHECK(!ContainsKey(controllee_map_, uuid));
1026 controllee_map_[uuid] = provider_host;
1027 // Keep the worker alive a bit longer right after a new controllee is added.
1028 RestartTick(&idle_time_);
1029 FOR_EACH_OBSERVER(Listener, listeners_,
1030 OnControlleeAdded(this, provider_host));
1033 void ServiceWorkerVersion::RemoveControllee(
1034 ServiceWorkerProviderHost* provider_host) {
1035 const std::string& uuid = provider_host->client_uuid();
1036 DCHECK(ContainsKey(controllee_map_, uuid));
1037 controllee_map_.erase(uuid);
1038 FOR_EACH_OBSERVER(Listener, listeners_,
1039 OnControlleeRemoved(this, provider_host));
1040 if (HasControllee())
1041 return;
1042 FOR_EACH_OBSERVER(Listener, listeners_, OnNoControllees(this));
1045 bool ServiceWorkerVersion::HasWindowClients() {
1046 return !GetWindowClientsInternal(false /* include_uncontrolled */).empty();
1049 void ServiceWorkerVersion::AddStreamingURLRequestJob(
1050 const ServiceWorkerURLRequestJob* request_job) {
1051 DCHECK(streaming_url_request_jobs_.find(request_job) ==
1052 streaming_url_request_jobs_.end());
1053 streaming_url_request_jobs_.insert(request_job);
1056 void ServiceWorkerVersion::RemoveStreamingURLRequestJob(
1057 const ServiceWorkerURLRequestJob* request_job) {
1058 streaming_url_request_jobs_.erase(request_job);
1059 if (is_redundant())
1060 StopWorkerIfIdle();
1063 void ServiceWorkerVersion::AddListener(Listener* listener) {
1064 listeners_.AddObserver(listener);
1067 void ServiceWorkerVersion::RemoveListener(Listener* listener) {
1068 listeners_.RemoveObserver(listener);
1071 void ServiceWorkerVersion::ReportError(ServiceWorkerStatusCode status,
1072 const std::string& status_message) {
1073 if (status_message.empty()) {
1074 OnReportException(base::UTF8ToUTF16(ServiceWorkerStatusToString(status)),
1075 -1, -1, GURL());
1076 } else {
1077 OnReportException(base::UTF8ToUTF16(status_message), -1, -1, GURL());
1081 void ServiceWorkerVersion::SetStartWorkerStatusCode(
1082 ServiceWorkerStatusCode status) {
1083 start_worker_status_ = status;
1086 void ServiceWorkerVersion::Doom() {
1087 DCHECK(!HasControllee());
1088 SetStatus(REDUNDANT);
1089 if (running_status() == STARTING || running_status() == RUNNING)
1090 embedded_worker_->Stop();
1091 if (!context_)
1092 return;
1093 std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
1094 script_cache_map_.GetResources(&resources);
1095 context_->storage()->PurgeResources(resources);
1098 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
1099 embedded_worker()->set_devtools_attached(attached);
1100 if (attached) {
1101 // TODO(falken): Canceling the timeouts when debugging could cause
1102 // heisenbugs; we should instead run them as normal show an educational
1103 // message in DevTools when they occur. crbug.com/470419
1105 // Don't record the startup time metric once DevTools is attached.
1106 ClearTick(&start_time_);
1107 skip_recording_startup_time_ = true;
1109 // Cancel request timeouts.
1110 SetAllRequestTimes(base::TimeTicks());
1111 return;
1113 if (!start_callbacks_.empty()) {
1114 // Reactivate the timer for start timeout.
1115 DCHECK(timeout_timer_.IsRunning());
1116 DCHECK(running_status() == STARTING || running_status() == STOPPING)
1117 << running_status();
1118 RestartTick(&start_time_);
1121 // Reactivate request timeouts.
1122 SetAllRequestTimes(base::TimeTicks::Now());
1125 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
1126 const net::HttpResponseInfo& http_info) {
1127 main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
1128 FOR_EACH_OBSERVER(Listener, listeners_,
1129 OnMainScriptHttpResponseInfoSet(this));
1132 void ServiceWorkerVersion::SimulatePingTimeoutForTesting() {
1133 ping_controller_->SimulateTimeoutForTesting();
1136 const net::HttpResponseInfo*
1137 ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
1138 return main_script_http_info_.get();
1141 ServiceWorkerVersion::RequestInfo::RequestInfo(int id,
1142 RequestType type,
1143 const base::TimeTicks& now)
1144 : id(id), type(type), time(now) {
1147 ServiceWorkerVersion::RequestInfo::~RequestInfo() {
1150 template <typename CallbackType>
1151 ServiceWorkerVersion::PendingRequest<CallbackType>::PendingRequest(
1152 const CallbackType& callback,
1153 const base::TimeTicks& time)
1154 : callback(callback), start_time(time) {
1157 template <typename CallbackType>
1158 ServiceWorkerVersion::PendingRequest<CallbackType>::~PendingRequest() {
1161 void ServiceWorkerVersion::OnThreadStarted() {
1162 if (running_status() == STOPPING)
1163 return;
1164 DCHECK_EQ(STARTING, running_status());
1165 // Activate ping/pong now that JavaScript execution will start.
1166 ping_controller_->Activate();
1169 void ServiceWorkerVersion::OnStarting() {
1170 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1173 void ServiceWorkerVersion::OnStarted() {
1174 DCHECK_EQ(RUNNING, running_status());
1175 RestartTick(&idle_time_);
1177 // Fire all start callbacks.
1178 scoped_refptr<ServiceWorkerVersion> protect(this);
1179 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_OK);
1180 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1183 void ServiceWorkerVersion::OnStopping() {
1184 metrics_->NotifyStopping();
1185 RestartTick(&stop_time_);
1186 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1189 void ServiceWorkerVersion::OnStopped(
1190 EmbeddedWorkerInstance::Status old_status) {
1191 metrics_->NotifyStopped();
1192 if (!stop_time_.is_null())
1193 ServiceWorkerMetrics::RecordStopWorkerTime(GetTickDuration(stop_time_));
1195 OnStoppedInternal(old_status);
1198 void ServiceWorkerVersion::OnDetached(
1199 EmbeddedWorkerInstance::Status old_status) {
1200 OnStoppedInternal(old_status);
1203 void ServiceWorkerVersion::OnReportException(
1204 const base::string16& error_message,
1205 int line_number,
1206 int column_number,
1207 const GURL& source_url) {
1208 FOR_EACH_OBSERVER(
1209 Listener,
1210 listeners_,
1211 OnErrorReported(
1212 this, error_message, line_number, column_number, source_url));
1215 void ServiceWorkerVersion::OnReportConsoleMessage(int source_identifier,
1216 int message_level,
1217 const base::string16& message,
1218 int line_number,
1219 const GURL& source_url) {
1220 FOR_EACH_OBSERVER(Listener,
1221 listeners_,
1222 OnReportConsoleMessage(this,
1223 source_identifier,
1224 message_level,
1225 message,
1226 line_number,
1227 source_url));
1230 bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message& message) {
1231 bool handled = true;
1232 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerVersion, message)
1233 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetClients,
1234 OnGetClients)
1235 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ActivateEventFinished,
1236 OnActivateEventFinished)
1237 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_InstallEventFinished,
1238 OnInstallEventFinished)
1239 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FetchEventFinished,
1240 OnFetchEventFinished)
1241 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_NotificationClickEventFinished,
1242 OnNotificationClickEventFinished)
1243 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PushEventFinished,
1244 OnPushEventFinished)
1245 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GeofencingEventFinished,
1246 OnGeofencingEventFinished)
1247 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_OpenWindow,
1248 OnOpenWindow)
1249 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SetCachedMetadata,
1250 OnSetCachedMetadata)
1251 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClearCachedMetadata,
1252 OnClearCachedMetadata)
1253 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToClient,
1254 OnPostMessageToClient)
1255 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FocusClient,
1256 OnFocusClient)
1257 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_NavigateClient, OnNavigateClient)
1258 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting,
1259 OnSkipWaiting)
1260 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients,
1261 OnClaimClients)
1262 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_Pong, OnPongFromWorker)
1263 IPC_MESSAGE_UNHANDLED(handled = false)
1264 IPC_END_MESSAGE_MAP()
1265 return handled;
1268 void ServiceWorkerVersion::OnStartSentAndScriptEvaluated(
1269 ServiceWorkerStatusCode status) {
1270 if (status != SERVICE_WORKER_OK) {
1271 scoped_refptr<ServiceWorkerVersion> protect(this);
1272 RunCallbacks(this, &start_callbacks_,
1273 DeduceStartWorkerFailureReason(status));
1277 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
1278 const StatusCallback& callback) {
1279 DCHECK_EQ(RUNNING, running_status())
1280 << "Worker stopped too soon after it was started.";
1282 int request_id = AddRequest(callback, &install_requests_, REQUEST_INSTALL);
1283 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
1284 ServiceWorkerMsg_InstallEvent(request_id));
1285 if (status != SERVICE_WORKER_OK) {
1286 install_requests_.Remove(request_id);
1287 RunSoon(base::Bind(callback, status));
1291 void ServiceWorkerVersion::DispatchActivateEventAfterStartWorker(
1292 const StatusCallback& callback) {
1293 DCHECK_EQ(RUNNING, running_status())
1294 << "Worker stopped too soon after it was started.";
1296 int request_id = AddRequest(callback, &activate_requests_, REQUEST_ACTIVATE);
1297 ServiceWorkerStatusCode status =
1298 embedded_worker_->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id));
1299 if (status != SERVICE_WORKER_OK) {
1300 activate_requests_.Remove(request_id);
1301 RunSoon(base::Bind(callback, status));
1305 void ServiceWorkerVersion::OnGetClients(
1306 int request_id,
1307 const ServiceWorkerClientQueryOptions& options) {
1308 TRACE_EVENT_ASYNC_BEGIN2(
1309 "ServiceWorker", "ServiceWorkerVersion::OnGetClients", request_id,
1310 "client_type", options.client_type, "include_uncontrolled",
1311 options.include_uncontrolled);
1313 ServiceWorkerClients clients;
1314 if (controllee_map_.empty() && !options.include_uncontrolled) {
1315 OnGetClientsFinished(request_id, &clients);
1316 return;
1319 // For Window clients we want to query the info on the UI thread first.
1320 if (options.client_type == blink::WebServiceWorkerClientTypeWindow ||
1321 options.client_type == blink::WebServiceWorkerClientTypeAll) {
1322 GetWindowClients(request_id, options);
1323 return;
1326 GetNonWindowClients(request_id, options, &clients);
1327 OnGetClientsFinished(request_id, &clients);
1330 void ServiceWorkerVersion::OnGetClientsFinished(int request_id,
1331 ServiceWorkerClients* clients) {
1332 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1333 TRACE_EVENT_ASYNC_END1("ServiceWorker", "ServiceWorkerVersion::OnGetClients",
1334 request_id, "The number of clients", clients->size());
1336 if (running_status() != RUNNING)
1337 return;
1338 // Sort clients so that the most recently active tab is in the front.
1339 std::sort(clients->begin(), clients->end(), ServiceWorkerClientInfoSortMRU());
1340 embedded_worker_->SendMessage(
1341 ServiceWorkerMsg_DidGetClients(request_id, *clients));
1344 void ServiceWorkerVersion::OnActivateEventFinished(
1345 int request_id,
1346 blink::WebServiceWorkerEventResult result) {
1347 DCHECK(ACTIVATING == status() ||
1348 REDUNDANT == status()) << status();
1349 TRACE_EVENT0("ServiceWorker",
1350 "ServiceWorkerVersion::OnActivateEventFinished");
1352 PendingRequest<StatusCallback>* request =
1353 activate_requests_.Lookup(request_id);
1354 if (!request) {
1355 NOTREACHED() << "Got unexpected message: " << request_id;
1356 return;
1358 ServiceWorkerStatusCode rv = SERVICE_WORKER_OK;
1359 if (result == blink::WebServiceWorkerEventResultRejected)
1360 rv = SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED;
1362 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ActivateEvent.Time",
1363 base::TimeTicks::Now() - request->start_time);
1365 scoped_refptr<ServiceWorkerVersion> protect(this);
1366 request->callback.Run(rv);
1367 RemoveCallbackAndStopIfRedundant(&activate_requests_, request_id);
1370 void ServiceWorkerVersion::OnInstallEventFinished(
1371 int request_id,
1372 blink::WebServiceWorkerEventResult result) {
1373 // Status is REDUNDANT if the worker was doomed while handling the install
1374 // event, and finished handling before being terminated.
1375 DCHECK(status() == INSTALLING || status() == REDUNDANT) << status();
1376 TRACE_EVENT0("ServiceWorker",
1377 "ServiceWorkerVersion::OnInstallEventFinished");
1379 PendingRequest<StatusCallback>* request =
1380 install_requests_.Lookup(request_id);
1381 if (!request) {
1382 NOTREACHED() << "Got unexpected message: " << request_id;
1383 return;
1385 ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
1386 if (result == blink::WebServiceWorkerEventResultRejected)
1387 status = SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED;
1389 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.InstallEvent.Time",
1390 base::TimeTicks::Now() - request->start_time);
1392 scoped_refptr<ServiceWorkerVersion> protect(this);
1393 request->callback.Run(status);
1394 RemoveCallbackAndStopIfRedundant(&install_requests_, request_id);
1397 void ServiceWorkerVersion::OnFetchEventFinished(
1398 int request_id,
1399 ServiceWorkerFetchEventResult result,
1400 const ServiceWorkerResponse& response) {
1401 TRACE_EVENT1("ServiceWorker",
1402 "ServiceWorkerVersion::OnFetchEventFinished",
1403 "Request id", request_id);
1404 PendingRequest<FetchCallback>* request = fetch_requests_.Lookup(request_id);
1405 if (!request) {
1406 NOTREACHED() << "Got unexpected message: " << request_id;
1407 return;
1410 // TODO(kinuko): Record other event statuses too.
1411 const bool handled = (result == SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE);
1412 metrics_->RecordEventHandledStatus(ServiceWorkerMetrics::EVENT_TYPE_FETCH,
1413 handled);
1415 ServiceWorkerMetrics::RecordFetchEventTime(
1416 result, base::TimeTicks::Now() - request->start_time);
1418 scoped_refptr<ServiceWorkerVersion> protect(this);
1419 request->callback.Run(SERVICE_WORKER_OK, result, response);
1420 RemoveCallbackAndStopIfRedundant(&fetch_requests_, request_id);
1423 void ServiceWorkerVersion::OnSyncEventFinished(
1424 int request_id,
1425 ServiceWorkerEventStatus status) {
1426 TRACE_EVENT1("ServiceWorker",
1427 "ServiceWorkerVersion::OnSyncEventFinished",
1428 "Request id", request_id);
1429 PendingRequest<StatusCallback>* request = sync_requests_.Lookup(request_id);
1430 if (!request) {
1431 NOTREACHED() << "Got unexpected message: " << request_id;
1432 return;
1435 scoped_refptr<ServiceWorkerVersion> protect(this);
1436 request->callback.Run(mojo::ConvertTo<ServiceWorkerStatusCode>(status));
1437 RemoveCallbackAndStopIfRedundant(&sync_requests_, request_id);
1440 void ServiceWorkerVersion::OnNotificationClickEventFinished(
1441 int request_id) {
1442 TRACE_EVENT1("ServiceWorker",
1443 "ServiceWorkerVersion::OnNotificationClickEventFinished",
1444 "Request id", request_id);
1445 PendingRequest<StatusCallback>* request =
1446 notification_click_requests_.Lookup(request_id);
1447 if (!request) {
1448 NOTREACHED() << "Got unexpected message: " << request_id;
1449 return;
1452 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.NotificationClickEvent.Time",
1453 base::TimeTicks::Now() - request->start_time);
1455 scoped_refptr<ServiceWorkerVersion> protect(this);
1456 request->callback.Run(SERVICE_WORKER_OK);
1457 RemoveCallbackAndStopIfRedundant(&notification_click_requests_, request_id);
1460 void ServiceWorkerVersion::OnPushEventFinished(
1461 int request_id,
1462 blink::WebServiceWorkerEventResult result) {
1463 TRACE_EVENT1("ServiceWorker",
1464 "ServiceWorkerVersion::OnPushEventFinished",
1465 "Request id", request_id);
1466 PendingRequest<StatusCallback>* request = push_requests_.Lookup(request_id);
1467 if (!request) {
1468 NOTREACHED() << "Got unexpected message: " << request_id;
1469 return;
1471 ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
1472 if (result == blink::WebServiceWorkerEventResultRejected)
1473 status = SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED;
1475 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.PushEvent.Time",
1476 base::TimeTicks::Now() - request->start_time);
1478 scoped_refptr<ServiceWorkerVersion> protect(this);
1479 request->callback.Run(status);
1480 RemoveCallbackAndStopIfRedundant(&push_requests_, request_id);
1483 void ServiceWorkerVersion::OnGeofencingEventFinished(int request_id) {
1484 TRACE_EVENT1("ServiceWorker",
1485 "ServiceWorkerVersion::OnGeofencingEventFinished",
1486 "Request id",
1487 request_id);
1488 PendingRequest<StatusCallback>* request =
1489 geofencing_requests_.Lookup(request_id);
1490 if (!request) {
1491 NOTREACHED() << "Got unexpected message: " << request_id;
1492 return;
1495 scoped_refptr<ServiceWorkerVersion> protect(this);
1496 request->callback.Run(SERVICE_WORKER_OK);
1497 RemoveCallbackAndStopIfRedundant(&geofencing_requests_, request_id);
1500 void ServiceWorkerVersion::OnServicePortConnectEventFinished(
1501 int request_id,
1502 ServicePortConnectResult result,
1503 const mojo::String& name,
1504 const mojo::String& data) {
1505 TRACE_EVENT1("ServiceWorker",
1506 "ServiceWorkerVersion::OnServicePortConnectEventFinished",
1507 "Request id", request_id);
1508 PendingRequest<ServicePortConnectCallback>* request =
1509 service_port_connect_requests_.Lookup(request_id);
1510 if (!request) {
1511 NOTREACHED() << "Got unexpected message: " << request_id;
1512 return;
1515 scoped_refptr<ServiceWorkerVersion> protect(this);
1516 request->callback.Run(SERVICE_WORKER_OK,
1517 result == SERVICE_PORT_CONNECT_RESULT_ACCEPT,
1518 name.To<base::string16>(), data.To<base::string16>());
1519 RemoveCallbackAndStopIfRedundant(&service_port_connect_requests_, request_id);
1522 void ServiceWorkerVersion::OnOpenWindow(int request_id, GURL url) {
1523 // Just abort if we are shutting down.
1524 if (!context_)
1525 return;
1527 if (!url.is_valid()) {
1528 DVLOG(1) << "Received unexpected invalid URL from renderer process.";
1529 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
1530 base::Bind(&KillEmbeddedWorkerProcess,
1531 embedded_worker_->process_id(),
1532 RESULT_CODE_KILLED_BAD_MESSAGE));
1533 return;
1536 // The renderer treats all URLs in the about: scheme as being about:blank.
1537 // Canonicalize about: URLs to about:blank.
1538 if (url.SchemeIs(url::kAboutScheme))
1539 url = GURL(url::kAboutBlankURL);
1541 // Reject requests for URLs that the process is not allowed to access. It's
1542 // possible to receive such requests since the renderer-side checks are
1543 // slightly different. For example, the view-source scheme will not be
1544 // filtered out by Blink.
1545 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
1546 embedded_worker_->process_id(), url)) {
1547 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowError(
1548 request_id, url.spec() + " cannot be opened."));
1549 return;
1552 BrowserThread::PostTask(
1553 BrowserThread::UI, FROM_HERE,
1554 base::Bind(&OpenWindowOnUI,
1555 url,
1556 script_url_,
1557 embedded_worker_->process_id(),
1558 make_scoped_refptr(context_->wrapper()),
1559 base::Bind(&ServiceWorkerVersion::DidOpenWindow,
1560 weak_factory_.GetWeakPtr(),
1561 request_id)));
1564 void ServiceWorkerVersion::DidOpenWindow(int request_id,
1565 int render_process_id,
1566 int render_frame_id) {
1567 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1569 if (running_status() != RUNNING)
1570 return;
1572 if (render_process_id == ChildProcessHost::kInvalidUniqueID &&
1573 render_frame_id == MSG_ROUTING_NONE) {
1574 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowError(
1575 request_id, "Something went wrong while trying to open the window."));
1576 return;
1579 for (auto it =
1580 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1581 !it->IsAtEnd(); it->Advance()) {
1582 ServiceWorkerProviderHost* provider_host = it->GetProviderHost();
1583 if (provider_host->process_id() != render_process_id ||
1584 provider_host->frame_id() != render_frame_id) {
1585 continue;
1587 provider_host->GetWindowClientInfo(base::Bind(
1588 &ServiceWorkerVersion::OnOpenWindowFinished, weak_factory_.GetWeakPtr(),
1589 request_id, provider_host->client_uuid()));
1590 return;
1593 // If here, it means that no provider_host was found, in which case, the
1594 // renderer should still be informed that the window was opened.
1595 OnOpenWindowFinished(request_id, std::string(), ServiceWorkerClientInfo());
1598 void ServiceWorkerVersion::OnOpenWindowFinished(
1599 int request_id,
1600 const std::string& client_uuid,
1601 const ServiceWorkerClientInfo& client_info) {
1602 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1604 if (running_status() != RUNNING)
1605 return;
1607 ServiceWorkerClientInfo client(client_info);
1609 // If the |client_info| is empty, it means that the opened window wasn't
1610 // controlled but the action still succeeded. The renderer process is
1611 // expecting an empty client in such case.
1612 if (!client.IsEmpty())
1613 client.client_uuid = client_uuid;
1615 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowResponse(
1616 request_id, client));
1619 void ServiceWorkerVersion::OnSetCachedMetadata(const GURL& url,
1620 const std::vector<char>& data) {
1621 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
1622 TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
1623 "ServiceWorkerVersion::OnSetCachedMetadata",
1624 callback_id, "URL", url.spec());
1625 script_cache_map_.WriteMetadata(
1626 url, data, base::Bind(&ServiceWorkerVersion::OnSetCachedMetadataFinished,
1627 weak_factory_.GetWeakPtr(), callback_id));
1630 void ServiceWorkerVersion::OnSetCachedMetadataFinished(int64 callback_id,
1631 int result) {
1632 TRACE_EVENT_ASYNC_END1("ServiceWorker",
1633 "ServiceWorkerVersion::OnSetCachedMetadata",
1634 callback_id, "result", result);
1635 FOR_EACH_OBSERVER(Listener, listeners_, OnCachedMetadataUpdated(this));
1638 void ServiceWorkerVersion::OnClearCachedMetadata(const GURL& url) {
1639 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
1640 TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
1641 "ServiceWorkerVersion::OnClearCachedMetadata",
1642 callback_id, "URL", url.spec());
1643 script_cache_map_.ClearMetadata(
1644 url, base::Bind(&ServiceWorkerVersion::OnClearCachedMetadataFinished,
1645 weak_factory_.GetWeakPtr(), callback_id));
1648 void ServiceWorkerVersion::OnClearCachedMetadataFinished(int64 callback_id,
1649 int result) {
1650 TRACE_EVENT_ASYNC_END1("ServiceWorker",
1651 "ServiceWorkerVersion::OnClearCachedMetadata",
1652 callback_id, "result", result);
1653 FOR_EACH_OBSERVER(Listener, listeners_, OnCachedMetadataUpdated(this));
1656 void ServiceWorkerVersion::OnPostMessageToClient(
1657 const std::string& client_uuid,
1658 const base::string16& message,
1659 const std::vector<TransferredMessagePort>& sent_message_ports) {
1660 if (!context_)
1661 return;
1662 TRACE_EVENT1("ServiceWorker",
1663 "ServiceWorkerVersion::OnPostMessageToDocument",
1664 "Client id", client_uuid);
1665 ServiceWorkerProviderHost* provider_host =
1666 context_->GetProviderHostByClientID(client_uuid);
1667 if (!provider_host) {
1668 // The client may already have been closed, just ignore.
1669 return;
1671 if (provider_host->document_url().GetOrigin() != script_url_.GetOrigin()) {
1672 // The client does not belong to the same origin as this ServiceWorker,
1673 // possibly due to timing issue or bad message.
1674 return;
1676 provider_host->PostMessage(this, message, sent_message_ports);
1679 void ServiceWorkerVersion::OnFocusClient(int request_id,
1680 const std::string& client_uuid) {
1681 if (!context_)
1682 return;
1683 TRACE_EVENT2("ServiceWorker",
1684 "ServiceWorkerVersion::OnFocusClient",
1685 "Request id", request_id,
1686 "Client id", client_uuid);
1687 ServiceWorkerProviderHost* provider_host =
1688 context_->GetProviderHostByClientID(client_uuid);
1689 if (!provider_host) {
1690 // The client may already have been closed, just ignore.
1691 return;
1693 if (provider_host->document_url().GetOrigin() != script_url_.GetOrigin()) {
1694 // The client does not belong to the same origin as this ServiceWorker,
1695 // possibly due to timing issue or bad message.
1696 return;
1698 provider_host->Focus(base::Bind(&ServiceWorkerVersion::OnFocusClientFinished,
1699 weak_factory_.GetWeakPtr(), request_id,
1700 client_uuid));
1703 void ServiceWorkerVersion::OnFocusClientFinished(
1704 int request_id,
1705 const std::string& client_uuid,
1706 const ServiceWorkerClientInfo& client) {
1707 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1709 if (running_status() != RUNNING)
1710 return;
1712 ServiceWorkerClientInfo client_info(client);
1713 client_info.client_uuid = client_uuid;
1715 embedded_worker_->SendMessage(ServiceWorkerMsg_FocusClientResponse(
1716 request_id, client_info));
1719 void ServiceWorkerVersion::OnNavigateClient(int request_id,
1720 const std::string& client_uuid,
1721 const GURL& url) {
1722 if (!context_)
1723 return;
1725 TRACE_EVENT2("ServiceWorker", "ServiceWorkerVersion::OnNavigateClient",
1726 "Request id", request_id, "Client id", client_uuid);
1728 if (!url.is_valid() || !base::IsValidGUID(client_uuid)) {
1729 DVLOG(1) << "Received unexpected invalid URL/UUID from renderer process.";
1730 BrowserThread::PostTask(
1731 BrowserThread::UI, FROM_HERE,
1732 base::Bind(&KillEmbeddedWorkerProcess, embedded_worker_->process_id(),
1733 RESULT_CODE_KILLED_BAD_MESSAGE));
1734 return;
1737 // Reject requests for URLs that the process is not allowed to access. It's
1738 // possible to receive such requests since the renderer-side checks are
1739 // slightly different. For example, the view-source scheme will not be
1740 // filtered out by Blink.
1741 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
1742 embedded_worker_->process_id(), url)) {
1743 embedded_worker_->SendMessage(
1744 ServiceWorkerMsg_NavigateClientError(request_id, url));
1745 return;
1748 ServiceWorkerProviderHost* provider_host =
1749 context_->GetProviderHostByClientID(client_uuid);
1750 if (!provider_host || provider_host->active_version() != this) {
1751 embedded_worker_->SendMessage(
1752 ServiceWorkerMsg_NavigateClientError(request_id, url));
1753 return;
1756 BrowserThread::PostTask(
1757 BrowserThread::UI, FROM_HERE,
1758 base::Bind(&NavigateClientOnUI, url, script_url_,
1759 provider_host->process_id(), provider_host->frame_id(),
1760 base::Bind(&ServiceWorkerVersion::DidNavigateClient,
1761 weak_factory_.GetWeakPtr(), request_id)));
1764 void ServiceWorkerVersion::DidNavigateClient(int request_id,
1765 int render_process_id,
1766 int render_frame_id) {
1767 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1769 if (running_status() != RUNNING)
1770 return;
1772 if (render_process_id == ChildProcessHost::kInvalidUniqueID &&
1773 render_frame_id == MSG_ROUTING_NONE) {
1774 embedded_worker_->SendMessage(
1775 ServiceWorkerMsg_NavigateClientError(request_id, GURL()));
1776 return;
1779 for (auto it =
1780 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1781 !it->IsAtEnd(); it->Advance()) {
1782 ServiceWorkerProviderHost* provider_host = it->GetProviderHost();
1783 if (provider_host->process_id() != render_process_id ||
1784 provider_host->frame_id() != render_frame_id) {
1785 continue;
1787 provider_host->GetWindowClientInfo(base::Bind(
1788 &ServiceWorkerVersion::OnNavigateClientFinished,
1789 weak_factory_.GetWeakPtr(), request_id, provider_host->client_uuid()));
1790 return;
1793 OnNavigateClientFinished(request_id, std::string(),
1794 ServiceWorkerClientInfo());
1797 void ServiceWorkerVersion::OnNavigateClientFinished(
1798 int request_id,
1799 const std::string& client_uuid,
1800 const ServiceWorkerClientInfo& client_info) {
1801 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1803 if (running_status() != RUNNING)
1804 return;
1806 ServiceWorkerClientInfo client(client_info);
1808 // If the |client_info| is empty, it means that the navigated client wasn't
1809 // controlled but the action still succeeded. The renderer process is
1810 // expecting an empty client in such case.
1811 if (!client.IsEmpty())
1812 client.client_uuid = client_uuid;
1814 embedded_worker_->SendMessage(
1815 ServiceWorkerMsg_NavigateClientResponse(request_id, client));
1818 void ServiceWorkerVersion::OnSkipWaiting(int request_id) {
1819 skip_waiting_ = true;
1820 if (status_ != INSTALLED)
1821 return DidSkipWaiting(request_id);
1823 if (!context_)
1824 return;
1825 ServiceWorkerRegistration* registration =
1826 context_->GetLiveRegistration(registration_id_);
1827 if (!registration)
1828 return;
1829 pending_skip_waiting_requests_.push_back(request_id);
1830 if (pending_skip_waiting_requests_.size() == 1)
1831 registration->ActivateWaitingVersionWhenReady();
1834 void ServiceWorkerVersion::DidSkipWaiting(int request_id) {
1835 if (running_status() == STARTING || running_status() == RUNNING)
1836 embedded_worker_->SendMessage(ServiceWorkerMsg_DidSkipWaiting(request_id));
1839 void ServiceWorkerVersion::OnClaimClients(int request_id) {
1840 if (status_ != ACTIVATING && status_ != ACTIVATED) {
1841 embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
1842 request_id, blink::WebServiceWorkerError::ErrorTypeState,
1843 base::ASCIIToUTF16(kClaimClientsStateErrorMesage)));
1844 return;
1846 if (context_) {
1847 if (ServiceWorkerRegistration* registration =
1848 context_->GetLiveRegistration(registration_id_)) {
1849 registration->ClaimClients();
1850 embedded_worker_->SendMessage(
1851 ServiceWorkerMsg_DidClaimClients(request_id));
1852 return;
1856 embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
1857 request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
1858 base::ASCIIToUTF16(kClaimClientsShutdownErrorMesage)));
1861 void ServiceWorkerVersion::OnPongFromWorker() {
1862 ping_controller_->OnPongReceived();
1865 void ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker(
1866 const StatusCallback& callback,
1867 ServiceWorkerStatusCode status,
1868 const scoped_refptr<ServiceWorkerRegistration>& protect) {
1869 if (status != SERVICE_WORKER_OK) {
1870 RecordStartWorkerResult(status);
1871 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED));
1872 return;
1874 if (is_redundant()) {
1875 RecordStartWorkerResult(SERVICE_WORKER_ERROR_REDUNDANT);
1876 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_REDUNDANT));
1877 return;
1880 MarkIfStale();
1882 switch (running_status()) {
1883 case RUNNING:
1884 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
1885 return;
1886 case STOPPING:
1887 case STOPPED:
1888 case STARTING:
1889 if (start_callbacks_.empty()) {
1890 start_callbacks_.push_back(
1891 base::Bind(&ServiceWorkerVersion::RecordStartWorkerResult,
1892 weak_factory_.GetWeakPtr()));
1894 // Keep the live registration while starting the worker.
1895 start_callbacks_.push_back(
1896 base::Bind(&RunStartWorkerCallback, callback, protect));
1897 StartWorkerInternal();
1898 return;
1902 void ServiceWorkerVersion::StartWorkerInternal() {
1903 if (!metrics_)
1904 metrics_.reset(new Metrics(this));
1906 if (!timeout_timer_.IsRunning())
1907 StartTimeoutTimer();
1908 if (running_status() == STOPPED) {
1909 embedded_worker_->Start(
1910 version_id_, scope_, script_url_,
1911 base::Bind(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated,
1912 weak_factory_.GetWeakPtr()));
1916 void ServiceWorkerVersion::GetWindowClients(
1917 int request_id,
1918 const ServiceWorkerClientQueryOptions& options) {
1919 DCHECK(options.client_type == blink::WebServiceWorkerClientTypeWindow ||
1920 options.client_type == blink::WebServiceWorkerClientTypeAll);
1921 const std::vector<base::Tuple<int, int, std::string>>& clients_info =
1922 GetWindowClientsInternal(options.include_uncontrolled);
1924 if (clients_info.empty()) {
1925 DidGetWindowClients(request_id, options,
1926 make_scoped_ptr(new ServiceWorkerClients));
1927 return;
1930 BrowserThread::PostTask(
1931 BrowserThread::UI, FROM_HERE,
1932 base::Bind(&OnGetWindowClientsFromUI, clients_info, script_url_,
1933 base::Bind(&ServiceWorkerVersion::DidGetWindowClients,
1934 weak_factory_.GetWeakPtr(), request_id, options)));
1937 const std::vector<base::Tuple<int, int, std::string>>
1938 ServiceWorkerVersion::GetWindowClientsInternal(bool include_uncontrolled) {
1939 std::vector<base::Tuple<int, int, std::string>> clients_info;
1940 if (!include_uncontrolled) {
1941 for (auto& controllee : controllee_map_)
1942 AddWindowClient(controllee.second, &clients_info);
1943 } else {
1944 for (auto it =
1945 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1946 !it->IsAtEnd(); it->Advance()) {
1947 AddWindowClient(it->GetProviderHost(), &clients_info);
1950 return clients_info;
1953 void ServiceWorkerVersion::DidGetWindowClients(
1954 int request_id,
1955 const ServiceWorkerClientQueryOptions& options,
1956 scoped_ptr<ServiceWorkerClients> clients) {
1957 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1958 if (options.client_type == blink::WebServiceWorkerClientTypeAll)
1959 GetNonWindowClients(request_id, options, clients.get());
1960 OnGetClientsFinished(request_id, clients.get());
1963 void ServiceWorkerVersion::GetNonWindowClients(
1964 int request_id,
1965 const ServiceWorkerClientQueryOptions& options,
1966 ServiceWorkerClients* clients) {
1967 if (!options.include_uncontrolled) {
1968 for (auto& controllee : controllee_map_) {
1969 AddNonWindowClient(controllee.second, options, clients);
1971 } else {
1972 for (auto it =
1973 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1974 !it->IsAtEnd(); it->Advance()) {
1975 AddNonWindowClient(it->GetProviderHost(), options, clients);
1980 void ServiceWorkerVersion::StartTimeoutTimer() {
1981 DCHECK(!timeout_timer_.IsRunning());
1983 if (embedded_worker_->devtools_attached()) {
1984 // Don't record the startup time metric once DevTools is attached.
1985 ClearTick(&start_time_);
1986 skip_recording_startup_time_ = true;
1987 } else {
1988 RestartTick(&start_time_);
1989 skip_recording_startup_time_ = false;
1992 // The worker is starting up and not yet idle.
1993 ClearTick(&idle_time_);
1995 // Ping will be activated in OnScriptLoaded.
1996 ping_controller_->Deactivate();
1998 timeout_timer_.Start(FROM_HERE,
1999 base::TimeDelta::FromSeconds(kTimeoutTimerDelaySeconds),
2000 this, &ServiceWorkerVersion::OnTimeoutTimer);
2003 void ServiceWorkerVersion::StopTimeoutTimer() {
2004 timeout_timer_.Stop();
2005 ClearTick(&idle_time_);
2007 // Trigger update if worker is stale.
2008 if (!in_dtor_ && !stale_time_.is_null()) {
2009 ClearTick(&stale_time_);
2010 if (!update_timer_.IsRunning())
2011 ScheduleUpdate();
2015 void ServiceWorkerVersion::OnTimeoutTimer() {
2016 DCHECK(running_status() == STARTING || running_status() == RUNNING ||
2017 running_status() == STOPPING)
2018 << running_status();
2020 MarkIfStale();
2022 if (GetTickDuration(stop_time_) >
2023 base::TimeDelta::FromSeconds(kStopWorkerTimeoutSeconds)) {
2024 metrics_->NotifyStalledInStopping();
2027 // Trigger update if worker is stale and we waited long enough for it to go
2028 // idle.
2029 if (GetTickDuration(stale_time_) >
2030 base::TimeDelta::FromMinutes(kRequestTimeoutMinutes)) {
2031 ClearTick(&stale_time_);
2032 if (!update_timer_.IsRunning())
2033 ScheduleUpdate();
2036 // Starting a worker hasn't finished within a certain period.
2037 if (GetTickDuration(start_time_) >
2038 base::TimeDelta::FromMinutes(kStartWorkerTimeoutMinutes)) {
2039 DCHECK(running_status() == STARTING || running_status() == STOPPING)
2040 << running_status();
2041 scoped_refptr<ServiceWorkerVersion> protect(this);
2042 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_ERROR_TIMEOUT);
2043 if (running_status() == STARTING)
2044 embedded_worker_->Stop();
2045 return;
2048 // Requests have not finished within a certain period.
2049 bool request_timed_out = false;
2050 while (!requests_.empty()) {
2051 RequestInfo info = requests_.front();
2052 if (GetTickDuration(info.time) <
2053 base::TimeDelta::FromMinutes(kRequestTimeoutMinutes))
2054 break;
2055 if (MaybeTimeOutRequest(info)) {
2056 request_timed_out = true;
2057 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.RequestTimeouts.Count",
2058 info.type, NUM_REQUEST_TYPES);
2060 requests_.pop();
2062 if (request_timed_out && running_status() != STOPPING)
2063 embedded_worker_->Stop();
2065 // For the timeouts below, there are no callbacks to timeout so there is
2066 // nothing more to do if the worker is already stopping.
2067 if (running_status() == STOPPING)
2068 return;
2070 // The worker has been idle for longer than a certain period.
2071 if (GetTickDuration(idle_time_) >
2072 base::TimeDelta::FromSeconds(kIdleWorkerTimeoutSeconds)) {
2073 StopWorkerIfIdle();
2074 return;
2077 // Check ping status.
2078 ping_controller_->CheckPingStatus();
2081 ServiceWorkerStatusCode ServiceWorkerVersion::PingWorker() {
2082 DCHECK(running_status() == STARTING || running_status() == RUNNING);
2083 return embedded_worker_->SendMessage(ServiceWorkerMsg_Ping());
2086 void ServiceWorkerVersion::OnPingTimeout() {
2087 DCHECK(running_status() == STARTING || running_status() == RUNNING);
2088 // TODO(falken): Show a message to the developer that the SW was stopped due
2089 // to timeout (crbug.com/457968). Also, change the error code to
2090 // SERVICE_WORKER_ERROR_TIMEOUT.
2091 StopWorkerIfIdle();
2094 void ServiceWorkerVersion::StopWorkerIfIdle() {
2095 if (HasInflightRequests() && !ping_controller_->IsTimedOut())
2096 return;
2097 if (running_status() == STOPPED || running_status() == STOPPING ||
2098 !stop_callbacks_.empty()) {
2099 return;
2102 // TODO(falken): We may need to handle StopIfIdle failure and
2103 // forcibly fail pending callbacks so no one is stuck waiting
2104 // for the worker.
2105 embedded_worker_->StopIfIdle();
2108 bool ServiceWorkerVersion::HasInflightRequests() const {
2109 return !activate_requests_.IsEmpty() || !install_requests_.IsEmpty() ||
2110 !fetch_requests_.IsEmpty() || !sync_requests_.IsEmpty() ||
2111 !notification_click_requests_.IsEmpty() || !push_requests_.IsEmpty() ||
2112 !geofencing_requests_.IsEmpty() ||
2113 !service_port_connect_requests_.IsEmpty() ||
2114 !streaming_url_request_jobs_.empty();
2117 void ServiceWorkerVersion::RecordStartWorkerResult(
2118 ServiceWorkerStatusCode status) {
2119 base::TimeTicks start_time = start_time_;
2120 ClearTick(&start_time_);
2122 ServiceWorkerMetrics::RecordStartWorkerStatus(status,
2123 IsInstalled(prestart_status_));
2125 if (status == SERVICE_WORKER_OK && !start_time.is_null() &&
2126 !skip_recording_startup_time_) {
2127 ServiceWorkerMetrics::RecordStartWorkerTime(GetTickDuration(start_time),
2128 IsInstalled(prestart_status_));
2131 if (status != SERVICE_WORKER_ERROR_TIMEOUT)
2132 return;
2133 EmbeddedWorkerInstance::StartingPhase phase =
2134 EmbeddedWorkerInstance::NOT_STARTING;
2135 EmbeddedWorkerInstance::Status running_status = embedded_worker_->status();
2136 // Build an artifical JavaScript exception to show in the ServiceWorker
2137 // log for developers; it's not user-facing so it's not a localized resource.
2138 std::string message = "ServiceWorker startup timed out. ";
2139 if (running_status != EmbeddedWorkerInstance::STARTING) {
2140 message.append("The worker had unexpected status: ");
2141 message.append(EmbeddedWorkerInstance::StatusToString(running_status));
2142 } else {
2143 phase = embedded_worker_->starting_phase();
2144 message.append("The worker was in startup phase: ");
2145 message.append(EmbeddedWorkerInstance::StartingPhaseToString(phase));
2147 message.append(".");
2148 OnReportException(base::UTF8ToUTF16(message), -1, -1, GURL());
2149 DVLOG(1) << message;
2150 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.TimeoutPhase",
2151 phase,
2152 EmbeddedWorkerInstance::STARTING_PHASE_MAX_VALUE);
2155 template <typename IDMAP>
2156 void ServiceWorkerVersion::RemoveCallbackAndStopIfRedundant(IDMAP* callbacks,
2157 int request_id) {
2158 RestartTick(&idle_time_);
2159 callbacks->Remove(request_id);
2160 if (is_redundant()) {
2161 // The stop should be already scheduled, but try to stop immediately, in
2162 // order to release worker resources soon.
2163 StopWorkerIfIdle();
2167 template <typename CallbackType>
2168 int ServiceWorkerVersion::AddRequest(
2169 const CallbackType& callback,
2170 IDMap<PendingRequest<CallbackType>, IDMapOwnPointer>* callback_map,
2171 RequestType request_type) {
2172 base::TimeTicks now = base::TimeTicks::Now();
2173 int request_id =
2174 callback_map->Add(new PendingRequest<CallbackType>(callback, now));
2175 requests_.push(RequestInfo(request_id, request_type, now));
2176 return request_id;
2179 bool ServiceWorkerVersion::MaybeTimeOutRequest(const RequestInfo& info) {
2180 switch (info.type) {
2181 case REQUEST_ACTIVATE:
2182 return RunIDMapCallback(&activate_requests_, info.id,
2183 SERVICE_WORKER_ERROR_TIMEOUT);
2184 case REQUEST_INSTALL:
2185 return RunIDMapCallback(&install_requests_, info.id,
2186 SERVICE_WORKER_ERROR_TIMEOUT);
2187 case REQUEST_FETCH:
2188 return RunIDMapCallback(
2189 &fetch_requests_, info.id, SERVICE_WORKER_ERROR_TIMEOUT,
2190 /* The other args are ignored for non-OK status. */
2191 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK, ServiceWorkerResponse());
2192 case REQUEST_SYNC:
2193 return RunIDMapCallback(&sync_requests_, info.id,
2194 SERVICE_WORKER_ERROR_TIMEOUT);
2195 case REQUEST_NOTIFICATION_CLICK:
2196 return RunIDMapCallback(&notification_click_requests_, info.id,
2197 SERVICE_WORKER_ERROR_TIMEOUT);
2198 case REQUEST_PUSH:
2199 return RunIDMapCallback(&push_requests_, info.id,
2200 SERVICE_WORKER_ERROR_TIMEOUT);
2201 case REQUEST_GEOFENCING:
2202 return RunIDMapCallback(&geofencing_requests_, info.id,
2203 SERVICE_WORKER_ERROR_TIMEOUT);
2204 case REQUEST_SERVICE_PORT_CONNECT:
2205 return RunIDMapCallback(&service_port_connect_requests_, info.id,
2206 SERVICE_WORKER_ERROR_TIMEOUT,
2207 false /* accept_connection */, base::string16(),
2208 base::string16());
2209 case NUM_REQUEST_TYPES:
2210 break;
2212 NOTREACHED() << "Got unexpected request type: " << info.type;
2213 return false;
2216 void ServiceWorkerVersion::SetAllRequestTimes(const base::TimeTicks& ticks) {
2217 std::queue<RequestInfo> new_requests;
2218 while (!requests_.empty()) {
2219 RequestInfo info = requests_.front();
2220 info.time = ticks;
2221 new_requests.push(info);
2222 requests_.pop();
2224 requests_ = new_requests;
2227 ServiceWorkerStatusCode ServiceWorkerVersion::DeduceStartWorkerFailureReason(
2228 ServiceWorkerStatusCode default_code) {
2229 if (ping_controller_->IsTimedOut())
2230 return SERVICE_WORKER_ERROR_TIMEOUT;
2232 if (start_worker_status_ != SERVICE_WORKER_OK)
2233 return start_worker_status_;
2235 const net::URLRequestStatus& main_script_status =
2236 script_cache_map()->main_script_status();
2237 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
2238 switch (main_script_status.error()) {
2239 case net::ERR_INSECURE_RESPONSE:
2240 case net::ERR_UNSAFE_REDIRECT:
2241 return SERVICE_WORKER_ERROR_SECURITY;
2242 case net::ERR_ABORTED:
2243 return SERVICE_WORKER_ERROR_ABORT;
2244 default:
2245 return SERVICE_WORKER_ERROR_NETWORK;
2249 return default_code;
2252 void ServiceWorkerVersion::MarkIfStale() {
2253 if (!context_)
2254 return;
2255 if (update_timer_.IsRunning() || !stale_time_.is_null())
2256 return;
2257 ServiceWorkerRegistration* registration =
2258 context_->GetLiveRegistration(registration_id_);
2259 if (!registration || registration->active_version() != this)
2260 return;
2261 base::TimeDelta time_since_last_check =
2262 base::Time::Now() - registration->last_update_check();
2263 if (time_since_last_check >
2264 base::TimeDelta::FromHours(kServiceWorkerScriptMaxCacheAgeInHours))
2265 RestartTick(&stale_time_);
2268 void ServiceWorkerVersion::FoundRegistrationForUpdate(
2269 ServiceWorkerStatusCode status,
2270 const scoped_refptr<ServiceWorkerRegistration>& registration) {
2271 if (!context_)
2272 return;
2274 const scoped_refptr<ServiceWorkerVersion> protect = this;
2275 if (is_update_scheduled_) {
2276 context_->UnprotectVersion(version_id_);
2277 is_update_scheduled_ = false;
2280 if (status != SERVICE_WORKER_OK || registration->active_version() != this)
2281 return;
2282 context_->UpdateServiceWorker(registration.get(),
2283 false /* force_bypass_cache */);
2286 void ServiceWorkerVersion::OnStoppedInternal(
2287 EmbeddedWorkerInstance::Status old_status) {
2288 DCHECK_EQ(STOPPED, running_status());
2289 scoped_refptr<ServiceWorkerVersion> protect;
2290 if (!in_dtor_)
2291 protect = this;
2293 DCHECK(metrics_);
2294 metrics_.reset();
2296 bool should_restart = !is_redundant() && !start_callbacks_.empty() &&
2297 (old_status != EmbeddedWorkerInstance::STARTING) &&
2298 !in_dtor_ && !ping_controller_->IsTimedOut();
2300 ClearTick(&stop_time_);
2301 StopTimeoutTimer();
2303 // Fire all stop callbacks.
2304 RunCallbacks(this, &stop_callbacks_, SERVICE_WORKER_OK);
2306 if (!should_restart) {
2307 // Let all start callbacks fail.
2308 RunCallbacks(this, &start_callbacks_,
2309 DeduceStartWorkerFailureReason(
2310 SERVICE_WORKER_ERROR_START_WORKER_FAILED));
2313 // Let all message callbacks fail (this will also fire and clear all
2314 // callbacks for events).
2315 // TODO(kinuko): Consider if we want to add queue+resend mechanism here.
2316 RunIDMapCallbacks(&activate_requests_,
2317 SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED);
2318 RunIDMapCallbacks(&install_requests_,
2319 SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
2320 RunIDMapCallbacks(&fetch_requests_, SERVICE_WORKER_ERROR_FAILED,
2321 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
2322 ServiceWorkerResponse());
2323 RunIDMapCallbacks(&sync_requests_, SERVICE_WORKER_ERROR_FAILED);
2324 RunIDMapCallbacks(&notification_click_requests_, SERVICE_WORKER_ERROR_FAILED);
2325 RunIDMapCallbacks(&push_requests_, SERVICE_WORKER_ERROR_FAILED);
2326 RunIDMapCallbacks(&geofencing_requests_, SERVICE_WORKER_ERROR_FAILED);
2328 // Close all mojo services. This will also fire and clear all callbacks
2329 // for messages that are still outstanding for those services.
2330 OnServicePortDispatcherConnectionError();
2332 OnBackgroundSyncDispatcherConnectionError();
2334 streaming_url_request_jobs_.clear();
2336 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
2338 if (should_restart)
2339 StartWorkerInternal();
2342 void ServiceWorkerVersion::OnServicePortDispatcherConnectionError() {
2343 RunIDMapCallbacks(&service_port_connect_requests_,
2344 SERVICE_WORKER_ERROR_FAILED, false, base::string16(),
2345 base::string16());
2346 service_port_dispatcher_.reset();
2349 void ServiceWorkerVersion::OnBackgroundSyncDispatcherConnectionError() {
2350 RunIDMapCallbacks(&sync_requests_, SERVICE_WORKER_ERROR_FAILED);
2351 background_sync_dispatcher_.reset();
2354 void ServiceWorkerVersion::OnBeginEvent() {
2355 if (should_exclude_from_uma_ || running_status() != RUNNING ||
2356 idle_time_.is_null()) {
2357 return;
2359 ServiceWorkerMetrics::RecordTimeBetweenEvents(base::TimeTicks::Now() -
2360 idle_time_);
2363 } // namespace content