Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_version.cc
blob9d232d518e710ab0fc8674e80cd8f2ded4e8f0f6
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 <map>
8 #include <string>
10 #include "base/command_line.h"
11 #include "base/location.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string16.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/time/time.h"
20 #include "content/browser/bad_message.h"
21 #include "content/browser/child_process_security_policy_impl.h"
22 #include "content/browser/message_port_message_filter.h"
23 #include "content/browser/message_port_service.h"
24 #include "content/browser/service_worker/embedded_worker_instance.h"
25 #include "content/browser/service_worker/embedded_worker_registry.h"
26 #include "content/browser/service_worker/service_worker_context_core.h"
27 #include "content/browser/service_worker/service_worker_context_wrapper.h"
28 #include "content/browser/service_worker/service_worker_metrics.h"
29 #include "content/browser/service_worker/service_worker_registration.h"
30 #include "content/browser/service_worker/service_worker_utils.h"
31 #include "content/browser/storage_partition_impl.h"
32 #include "content/common/service_worker/service_worker_messages.h"
33 #include "content/common/service_worker/service_worker_type_converters.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/content_browser_client.h"
36 #include "content/public/browser/page_navigator.h"
37 #include "content/public/browser/render_frame_host.h"
38 #include "content/public/browser/render_process_host.h"
39 #include "content/public/browser/web_contents.h"
40 #include "content/public/browser/web_contents_observer.h"
41 #include "content/public/common/background_sync.mojom.h"
42 #include "content/public/common/child_process_host.h"
43 #include "content/public/common/content_client.h"
44 #include "content/public/common/content_switches.h"
45 #include "content/public/common/result_codes.h"
46 #include "content/public/common/service_registry.h"
47 #include "mojo/common/common_type_converters.h"
48 #include "mojo/common/url_type_converters.h"
49 #include "net/http/http_response_headers.h"
50 #include "net/http/http_response_info.h"
52 namespace content {
54 using StatusCallback = ServiceWorkerVersion::StatusCallback;
55 using ServiceWorkerClients = std::vector<ServiceWorkerClientInfo>;
56 using GetClientsCallback =
57 base::Callback<void(scoped_ptr<ServiceWorkerClients>)>;
59 namespace {
61 // Delay between the timeout timer firing.
62 const int kTimeoutTimerDelaySeconds = 30;
64 // Time to wait until stopping an idle worker.
65 const int kIdleWorkerTimeoutSeconds = 30;
67 // Time until a stopping worker is considered stalled.
68 const int kStopWorkerTimeoutSeconds = 30;
70 // Default delay for scheduled update.
71 const int kUpdateDelaySeconds = 1;
73 // Timeout for waiting for a response to a ping.
74 const int kPingTimeoutSeconds = 30;
76 // If the SW was destructed while starting up, how many seconds it
77 // had to start up for this to be considered a timeout occurrence.
78 const int kDestructedStartingWorkerTimeoutThresholdSeconds = 5;
80 const char kClaimClientsStateErrorMesage[] =
81 "Only the active worker can claim clients.";
83 const char kClaimClientsShutdownErrorMesage[] =
84 "Failed to claim clients due to Service Worker system shutdown.";
86 void RunSoon(const base::Closure& callback) {
87 if (!callback.is_null())
88 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
91 template <typename CallbackArray, typename Arg>
92 void RunCallbacks(ServiceWorkerVersion* version,
93 CallbackArray* callbacks_ptr,
94 const Arg& arg) {
95 CallbackArray callbacks;
96 callbacks.swap(*callbacks_ptr);
97 scoped_refptr<ServiceWorkerVersion> protect(version);
98 for (const auto& callback : callbacks)
99 callback.Run(arg);
102 template <typename IDMAP, typename... Params>
103 void RunIDMapCallbacks(IDMAP* requests, const Params&... params) {
104 typename IDMAP::iterator iter(requests);
105 while (!iter.IsAtEnd()) {
106 iter.GetCurrentValue()->callback.Run(params...);
107 iter.Advance();
109 requests->Clear();
112 template <typename CallbackType, typename... Params>
113 bool RunIDMapCallback(IDMap<CallbackType, IDMapOwnPointer>* requests,
114 int request_id,
115 const Params&... params) {
116 CallbackType* request = requests->Lookup(request_id);
117 if (!request)
118 return false;
120 request->callback.Run(params...);
121 requests->Remove(request_id);
122 return true;
125 void RunStartWorkerCallback(
126 const StatusCallback& callback,
127 scoped_refptr<ServiceWorkerRegistration> protect,
128 ServiceWorkerStatusCode status) {
129 callback.Run(status);
132 // A callback adapter to start a |task| after StartWorker.
133 void RunTaskAfterStartWorker(
134 base::WeakPtr<ServiceWorkerVersion> version,
135 const StatusCallback& error_callback,
136 const base::Closure& task,
137 ServiceWorkerStatusCode status) {
138 if (status != SERVICE_WORKER_OK) {
139 if (!error_callback.is_null())
140 error_callback.Run(status);
141 return;
143 if (version->running_status() != ServiceWorkerVersion::RUNNING) {
144 // We've tried to start the worker (and it has succeeded), but
145 // it looks it's not running yet.
146 NOTREACHED() << "The worker's not running after successful StartWorker";
147 if (!error_callback.is_null())
148 error_callback.Run(SERVICE_WORKER_ERROR_START_WORKER_FAILED);
149 return;
151 task.Run();
154 void RunErrorFetchCallback(const ServiceWorkerVersion::FetchCallback& callback,
155 ServiceWorkerStatusCode status) {
156 callback.Run(status,
157 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
158 ServiceWorkerResponse());
161 void RunErrorMessageCallback(
162 const std::vector<TransferredMessagePort>& sent_message_ports,
163 const ServiceWorkerVersion::StatusCallback& callback,
164 ServiceWorkerStatusCode status) {
165 // Transfering the message ports failed, so destroy the ports.
166 for (const TransferredMessagePort& port : sent_message_ports) {
167 MessagePortService::GetInstance()->ClosePort(port.id);
169 callback.Run(status);
172 void RunErrorServicePortConnectCallback(
173 const ServiceWorkerVersion::ServicePortConnectCallback& callback,
174 ServiceWorkerStatusCode status) {
175 callback.Run(status, false /* accept_connection */, base::string16(),
176 base::string16());
179 using WindowOpenedCallback = base::Callback<void(int, int)>;
181 // The WindowOpenedObserver class is a WebContentsObserver that will wait for a
182 // new Window's WebContents to be initialized, run the |callback| passed to its
183 // constructor then self destroy.
184 // The callback will receive the process and frame ids. If something went wrong
185 // those will be (kInvalidUniqueID, MSG_ROUTING_NONE).
186 // The callback will be called in the IO thread.
187 class WindowOpenedObserver : public WebContentsObserver {
188 public:
189 WindowOpenedObserver(WebContents* web_contents,
190 const WindowOpenedCallback& callback)
191 : WebContentsObserver(web_contents),
192 callback_(callback)
195 void DidCommitProvisionalLoadForFrame(
196 RenderFrameHost* render_frame_host,
197 const GURL& validated_url,
198 ui::PageTransition transition_type) override {
199 DCHECK(web_contents());
201 if (render_frame_host != web_contents()->GetMainFrame())
202 return;
204 RunCallback(render_frame_host->GetProcess()->GetID(),
205 render_frame_host->GetRoutingID());
208 void RenderProcessGone(base::TerminationStatus status) override {
209 RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
212 void WebContentsDestroyed() override {
213 RunCallback(ChildProcessHost::kInvalidUniqueID, MSG_ROUTING_NONE);
216 private:
217 void RunCallback(int render_process_id, int render_frame_id) {
218 // After running the callback, |this| will stop observing, thus
219 // web_contents() should return nullptr and |RunCallback| should no longer
220 // be called. Then, |this| will self destroy.
221 DCHECK(web_contents());
223 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
224 base::Bind(callback_,
225 render_process_id,
226 render_frame_id));
227 Observe(nullptr);
228 base::MessageLoop::current()->DeleteSoon(FROM_HERE, this);
231 const WindowOpenedCallback callback_;
233 DISALLOW_COPY_AND_ASSIGN(WindowOpenedObserver);
236 void DidOpenURL(const WindowOpenedCallback& callback,
237 WebContents* web_contents) {
238 DCHECK(web_contents);
240 new WindowOpenedObserver(web_contents, callback);
243 void OpenWindowOnUI(
244 const GURL& url,
245 const GURL& script_url,
246 int process_id,
247 const scoped_refptr<ServiceWorkerContextWrapper>& context_wrapper,
248 const WindowOpenedCallback& callback) {
249 DCHECK_CURRENTLY_ON(BrowserThread::UI);
251 BrowserContext* browser_context = context_wrapper->storage_partition()
252 ? context_wrapper->storage_partition()->browser_context()
253 : nullptr;
254 // We are shutting down.
255 if (!browser_context)
256 return;
258 RenderProcessHost* render_process_host =
259 RenderProcessHost::FromID(process_id);
260 if (render_process_host->IsForGuestsOnly()) {
261 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
262 base::Bind(callback,
263 ChildProcessHost::kInvalidUniqueID,
264 MSG_ROUTING_NONE));
265 return;
268 OpenURLParams params(
269 url, Referrer::SanitizeForRequest(
270 url, Referrer(script_url, blink::WebReferrerPolicyDefault)),
271 NEW_FOREGROUND_TAB, ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
272 true /* is_renderer_initiated */);
274 GetContentClient()->browser()->OpenURL(
275 browser_context, params,
276 base::Bind(&DidOpenURL, callback));
279 void KillEmbeddedWorkerProcess(int process_id, ResultCode code) {
280 DCHECK_CURRENTLY_ON(BrowserThread::UI);
281 RenderProcessHost* render_process_host =
282 RenderProcessHost::FromID(process_id);
283 if (render_process_host->GetHandle() != base::kNullProcessHandle) {
284 bad_message::ReceivedBadMessage(render_process_host,
285 bad_message::SERVICE_WORKER_BAD_URL);
289 void ClearTick(base::TimeTicks* time) {
290 *time = base::TimeTicks();
293 void RestartTick(base::TimeTicks* time) {
294 *time = base::TimeTicks().Now();
297 base::TimeDelta GetTickDuration(const base::TimeTicks& time) {
298 if (time.is_null())
299 return base::TimeDelta();
300 return base::TimeTicks().Now() - time;
303 void OnGetWindowClientsFromUI(
304 // The tuple contains process_id, frame_id, client_uuid.
305 const std::vector<base::Tuple<int, int, std::string>>& clients_info,
306 const GURL& script_url,
307 const GetClientsCallback& callback) {
308 scoped_ptr<ServiceWorkerClients> clients(new ServiceWorkerClients);
310 for (const auto& it : clients_info) {
311 ServiceWorkerClientInfo info =
312 ServiceWorkerProviderHost::GetWindowClientInfoOnUI(base::get<0>(it),
313 base::get<1>(it));
315 // If the request to the provider_host returned an empty
316 // ServiceWorkerClientInfo, that means that it wasn't possible to associate
317 // it with a valid RenderFrameHost. It might be because the frame was killed
318 // or navigated in between.
319 if (info.IsEmpty())
320 continue;
322 // We can get info for a frame that was navigating end ended up with a
323 // different URL than expected. In such case, we should make sure to not
324 // expose cross-origin WindowClient.
325 if (info.url.GetOrigin() != script_url.GetOrigin())
326 continue;
328 info.client_uuid = base::get<2>(it);
329 clients->push_back(info);
332 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
333 base::Bind(callback, base::Passed(&clients)));
336 void AddWindowClient(
337 ServiceWorkerProviderHost* host,
338 std::vector<base::Tuple<int, int, std::string>>* client_info) {
339 if (host->client_type() != blink::WebServiceWorkerClientTypeWindow)
340 return;
341 client_info->push_back(base::MakeTuple(host->process_id(), host->frame_id(),
342 host->client_uuid()));
345 void AddNonWindowClient(ServiceWorkerProviderHost* host,
346 const ServiceWorkerClientQueryOptions& options,
347 ServiceWorkerClients* clients) {
348 blink::WebServiceWorkerClientType host_client_type = host->client_type();
349 if (host_client_type == blink::WebServiceWorkerClientTypeWindow)
350 return;
351 if (options.client_type != blink::WebServiceWorkerClientTypeAll &&
352 options.client_type != host_client_type)
353 return;
355 ServiceWorkerClientInfo client_info(
356 blink::WebPageVisibilityStateHidden,
357 false, // is_focused
358 host->document_url(), REQUEST_CONTEXT_FRAME_TYPE_NONE, host_client_type);
359 client_info.client_uuid = host->client_uuid();
360 clients->push_back(client_info);
363 bool IsInstalled(ServiceWorkerVersion::Status status) {
364 switch (status) {
365 case ServiceWorkerVersion::NEW:
366 case ServiceWorkerVersion::INSTALLING:
367 case ServiceWorkerVersion::REDUNDANT:
368 return false;
369 case ServiceWorkerVersion::INSTALLED:
370 case ServiceWorkerVersion::ACTIVATING:
371 case ServiceWorkerVersion::ACTIVATED:
372 return true;
374 NOTREACHED() << "Unexpected status: " << status;
375 return false;
378 } // namespace
380 const int ServiceWorkerVersion::kStartWorkerTimeoutMinutes = 5;
381 const int ServiceWorkerVersion::kRequestTimeoutMinutes = 5;
383 class ServiceWorkerVersion::Metrics {
384 public:
385 using EventType = ServiceWorkerMetrics::EventType;
386 explicit Metrics(ServiceWorkerVersion* owner) : owner_(owner) {}
387 ~Metrics() {
388 for (const auto& ev : event_stats_) {
389 ServiceWorkerMetrics::RecordEventHandledRatio(owner_->scope(), ev.first,
390 ev.second.handled_events,
391 ev.second.fired_events);
395 void RecordEventHandledStatus(EventType event, bool handled) {
396 event_stats_[event].fired_events++;
397 if (handled)
398 event_stats_[event].handled_events++;
401 void NotifyStopping() {
402 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STOPPING;
405 void NotifyStopped() {
406 switch (stop_status_) {
407 case ServiceWorkerMetrics::STOP_STATUS_STOPPED:
408 case ServiceWorkerMetrics::STOP_STATUS_STALLED_THEN_STOPPED:
409 return;
410 case ServiceWorkerMetrics::STOP_STATUS_STOPPING:
411 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STOPPED;
412 break;
413 case ServiceWorkerMetrics::STOP_STATUS_STALLED:
414 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STALLED_THEN_STOPPED;
415 break;
416 case ServiceWorkerMetrics::NUM_STOP_STATUS_TYPES:
417 NOTREACHED();
418 return;
420 if (IsInstalled(owner_->status()))
421 ServiceWorkerMetrics::RecordStopWorkerStatus(stop_status_);
424 void NotifyStalledInStopping() {
425 if (stop_status_ != ServiceWorkerMetrics::STOP_STATUS_STOPPING)
426 return;
427 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STALLED;
428 if (IsInstalled(owner_->status()))
429 ServiceWorkerMetrics::RecordStopWorkerStatus(stop_status_);
432 private:
433 struct EventStat {
434 size_t fired_events = 0;
435 size_t handled_events = 0;
438 ServiceWorkerVersion* owner_;
439 std::map<EventType, EventStat> event_stats_;
440 ServiceWorkerMetrics::StopWorkerStatus stop_status_ =
441 ServiceWorkerMetrics::STOP_STATUS_STOPPING;
443 DISALLOW_COPY_AND_ASSIGN(Metrics);
446 // A controller for periodically sending a ping to the worker to see
447 // if the worker is not stalling.
448 class ServiceWorkerVersion::PingController {
449 public:
450 explicit PingController(ServiceWorkerVersion* version) : version_(version) {}
451 ~PingController() {}
453 void Activate() { ping_state_ = PINGING; }
455 void Deactivate() {
456 ClearTick(&ping_time_);
457 ping_state_ = NOT_PINGING;
460 void OnPongReceived() { ClearTick(&ping_time_); }
462 bool IsTimedOut() { return ping_state_ == PING_TIMED_OUT; }
464 // Checks ping status. This is supposed to be called periodically.
465 // This may call:
466 // - OnPingTimeout() if the worker hasn't reponded within a certain period.
467 // - PingWorker() if we're running ping timer and can send next ping.
468 void CheckPingStatus() {
469 if (GetTickDuration(ping_time_) >
470 base::TimeDelta::FromSeconds(kPingTimeoutSeconds)) {
471 ping_state_ = PING_TIMED_OUT;
472 version_->OnPingTimeout();
473 return;
476 // Check if we want to send a next ping.
477 if (ping_state_ != PINGING || !ping_time_.is_null())
478 return;
480 if (version_->PingWorker() != SERVICE_WORKER_OK) {
481 // TODO(falken): Maybe try resending Ping a few times first?
482 ping_state_ = PING_TIMED_OUT;
483 version_->OnPingTimeout();
484 return;
486 RestartTick(&ping_time_);
489 void SimulateTimeoutForTesting() {
490 version_->PingWorker();
491 ping_state_ = PING_TIMED_OUT;
492 version_->OnPingTimeout();
495 private:
496 enum PingState { NOT_PINGING, PINGING, PING_TIMED_OUT };
497 ServiceWorkerVersion* version_; // Not owned.
498 base::TimeTicks ping_time_;
499 PingState ping_state_ = NOT_PINGING;
500 DISALLOW_COPY_AND_ASSIGN(PingController);
503 ServiceWorkerVersion::ServiceWorkerVersion(
504 ServiceWorkerRegistration* registration,
505 const GURL& script_url,
506 int64 version_id,
507 base::WeakPtr<ServiceWorkerContextCore> context)
508 : version_id_(version_id),
509 registration_id_(registration->id()),
510 script_url_(script_url),
511 scope_(registration->pattern()),
512 context_(context),
513 script_cache_map_(this, context),
514 ping_controller_(new PingController(this)),
515 weak_factory_(this) {
516 DCHECK(context_);
517 DCHECK(registration);
518 context_->AddLiveVersion(this);
519 embedded_worker_ = context_->embedded_worker_registry()->CreateWorker();
520 embedded_worker_->AddListener(this);
523 ServiceWorkerVersion::~ServiceWorkerVersion() {
524 // The user may have closed the tab waiting for SW to start up.
525 if (GetTickDuration(start_time_) >
526 base::TimeDelta::FromSeconds(
527 kDestructedStartingWorkerTimeoutThresholdSeconds)) {
528 DCHECK(timeout_timer_.IsRunning());
529 DCHECK(!embedded_worker_->devtools_attached());
530 RecordStartWorkerResult(SERVICE_WORKER_ERROR_TIMEOUT);
533 // Same with stopping.
534 if (GetTickDuration(stop_time_) >
535 base::TimeDelta::FromSeconds(kStopWorkerTimeoutSeconds)) {
536 metrics_->NotifyStalledInStopping();
539 if (context_)
540 context_->RemoveLiveVersion(version_id_);
542 if (running_status() == STARTING || running_status() == RUNNING)
543 embedded_worker_->Stop();
544 embedded_worker_->RemoveListener(this);
547 void ServiceWorkerVersion::SetStatus(Status status) {
548 if (status_ == status)
549 return;
551 status_ = status;
553 if (skip_waiting_ && status_ == ACTIVATED) {
554 for (int request_id : pending_skip_waiting_requests_)
555 DidSkipWaiting(request_id);
556 pending_skip_waiting_requests_.clear();
559 std::vector<base::Closure> callbacks;
560 callbacks.swap(status_change_callbacks_);
561 for (const auto& callback : callbacks)
562 callback.Run();
564 FOR_EACH_OBSERVER(Listener, listeners_, OnVersionStateChanged(this));
567 void ServiceWorkerVersion::RegisterStatusChangeCallback(
568 const base::Closure& callback) {
569 status_change_callbacks_.push_back(callback);
572 ServiceWorkerVersionInfo ServiceWorkerVersion::GetInfo() {
573 DCHECK_CURRENTLY_ON(BrowserThread::IO);
574 ServiceWorkerVersionInfo info(
575 running_status(), status(), script_url(), registration_id(), version_id(),
576 embedded_worker()->process_id(), embedded_worker()->thread_id(),
577 embedded_worker()->worker_devtools_agent_route_id());
578 for (const auto& controllee : controllee_map_) {
579 const ServiceWorkerProviderHost* host = controllee.second;
580 info.clients.insert(std::make_pair(
581 host->client_uuid(),
582 ServiceWorkerVersionInfo::ClientInfo(
583 host->process_id(), host->route_id(), host->provider_type())));
585 if (!main_script_http_info_)
586 return info;
587 info.script_response_time = main_script_http_info_->response_time;
588 if (main_script_http_info_->headers)
589 main_script_http_info_->headers->GetLastModifiedValue(
590 &info.script_last_modified);
591 return info;
594 void ServiceWorkerVersion::StartWorker(const StatusCallback& callback) {
595 if (!context_) {
596 RecordStartWorkerResult(SERVICE_WORKER_ERROR_ABORT);
597 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_ABORT));
598 return;
600 if (is_redundant()) {
601 RecordStartWorkerResult(SERVICE_WORKER_ERROR_REDUNDANT);
602 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_REDUNDANT));
603 return;
605 prestart_status_ = status_;
607 // Ensure the live registration during starting worker so that the worker can
608 // get associated with it in SWDispatcherHost::OnSetHostedVersionId().
609 context_->storage()->FindRegistrationForId(
610 registration_id_,
611 scope_.GetOrigin(),
612 base::Bind(&ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker,
613 weak_factory_.GetWeakPtr(),
614 callback));
617 void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) {
618 if (running_status() == STOPPED) {
619 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
620 return;
622 if (stop_callbacks_.empty()) {
623 ServiceWorkerStatusCode status = embedded_worker_->Stop();
624 if (status != SERVICE_WORKER_OK) {
625 RunSoon(base::Bind(callback, status));
626 return;
629 stop_callbacks_.push_back(callback);
632 void ServiceWorkerVersion::ScheduleUpdate() {
633 if (!context_)
634 return;
635 if (update_timer_.IsRunning()) {
636 update_timer_.Reset();
637 return;
639 if (is_update_scheduled_)
640 return;
641 is_update_scheduled_ = true;
643 // Protect |this| until the timer fires, since we may be stopping
644 // and soon no one might hold a reference to us.
645 context_->ProtectVersion(make_scoped_refptr(this));
646 update_timer_.Start(FROM_HERE,
647 base::TimeDelta::FromSeconds(kUpdateDelaySeconds),
648 base::Bind(&ServiceWorkerVersion::StartUpdate,
649 weak_factory_.GetWeakPtr()));
652 void ServiceWorkerVersion::StartUpdate() {
653 if (!context_)
654 return;
655 context_->storage()->FindRegistrationForId(
656 registration_id_, scope_.GetOrigin(),
657 base::Bind(&ServiceWorkerVersion::FoundRegistrationForUpdate,
658 weak_factory_.GetWeakPtr()));
661 void ServiceWorkerVersion::DeferScheduledUpdate() {
662 if (update_timer_.IsRunning())
663 update_timer_.Reset();
666 void ServiceWorkerVersion::DispatchMessageEvent(
667 const base::string16& message,
668 const std::vector<TransferredMessagePort>& sent_message_ports,
669 const StatusCallback& callback) {
670 for (const TransferredMessagePort& port : sent_message_ports) {
671 MessagePortService::GetInstance()->HoldMessages(port.id);
674 DispatchMessageEventInternal(message, sent_message_ports, callback);
677 void ServiceWorkerVersion::DispatchMessageEventInternal(
678 const base::string16& message,
679 const std::vector<TransferredMessagePort>& sent_message_ports,
680 const StatusCallback& callback) {
681 if (running_status() != RUNNING) {
682 // Schedule calling this method after starting the worker.
683 StartWorker(base::Bind(
684 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
685 base::Bind(&RunErrorMessageCallback, sent_message_ports, callback),
686 base::Bind(&self::DispatchMessageEventInternal,
687 weak_factory_.GetWeakPtr(), message, sent_message_ports,
688 callback)));
689 return;
692 // TODO(kinuko): Cleanup this (and corresponding unit test) when message
693 // event becomes extendable, round-trip event. (crbug.com/498596)
694 RestartTick(&idle_time_);
696 MessagePortMessageFilter* filter =
697 embedded_worker_->message_port_message_filter();
698 std::vector<int> new_routing_ids;
699 filter->UpdateMessagePortsWithNewRoutes(sent_message_ports, &new_routing_ids);
700 ServiceWorkerStatusCode status =
701 embedded_worker_->SendMessage(ServiceWorkerMsg_MessageToWorker(
702 message, sent_message_ports, new_routing_ids));
703 RunSoon(base::Bind(callback, status));
706 void ServiceWorkerVersion::DispatchInstallEvent(
707 const StatusCallback& callback) {
708 DCHECK_EQ(INSTALLING, status()) << status();
710 if (running_status() != RUNNING) {
711 // Schedule calling this method after starting the worker.
712 StartWorker(
713 base::Bind(&RunTaskAfterStartWorker,
714 weak_factory_.GetWeakPtr(),
715 callback,
716 base::Bind(&self::DispatchInstallEventAfterStartWorker,
717 weak_factory_.GetWeakPtr(),
718 callback)));
719 } else {
720 DispatchInstallEventAfterStartWorker(callback);
724 void ServiceWorkerVersion::DispatchActivateEvent(
725 const StatusCallback& callback) {
726 DCHECK_EQ(ACTIVATING, status()) << status();
728 if (running_status() != RUNNING) {
729 // Schedule calling this method after starting the worker.
730 StartWorker(
731 base::Bind(&RunTaskAfterStartWorker,
732 weak_factory_.GetWeakPtr(),
733 callback,
734 base::Bind(&self::DispatchActivateEventAfterStartWorker,
735 weak_factory_.GetWeakPtr(),
736 callback)));
737 } else {
738 DispatchActivateEventAfterStartWorker(callback);
742 void ServiceWorkerVersion::DispatchFetchEvent(
743 const ServiceWorkerFetchRequest& request,
744 const base::Closure& prepare_callback,
745 const FetchCallback& fetch_callback) {
746 DCHECK_EQ(ACTIVATED, status()) << status();
748 if (running_status() != RUNNING) {
749 // Schedule calling this method after starting the worker.
750 StartWorker(base::Bind(&RunTaskAfterStartWorker,
751 weak_factory_.GetWeakPtr(),
752 base::Bind(&RunErrorFetchCallback, fetch_callback),
753 base::Bind(&self::DispatchFetchEvent,
754 weak_factory_.GetWeakPtr(),
755 request,
756 prepare_callback,
757 fetch_callback)));
758 return;
761 prepare_callback.Run();
763 int request_id = AddRequest(fetch_callback, &fetch_requests_, REQUEST_FETCH);
764 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
765 ServiceWorkerMsg_FetchEvent(request_id, request));
766 if (status != SERVICE_WORKER_OK) {
767 fetch_requests_.Remove(request_id);
768 RunSoon(base::Bind(&RunErrorFetchCallback,
769 fetch_callback,
770 SERVICE_WORKER_ERROR_FAILED));
774 void ServiceWorkerVersion::DispatchSyncEvent(SyncRegistrationPtr registration,
775 const StatusCallback& callback) {
776 DCHECK_EQ(ACTIVATED, status()) << status();
777 if (running_status() != RUNNING) {
778 // Schedule calling this method after starting the worker.
779 StartWorker(base::Bind(
780 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(), callback,
781 base::Bind(&self::DispatchSyncEvent, weak_factory_.GetWeakPtr(),
782 base::Passed(registration.Pass()), callback)));
783 return;
786 int request_id = AddRequest(callback, &sync_requests_, REQUEST_SYNC);
787 if (!background_sync_dispatcher_) {
788 embedded_worker_->GetServiceRegistry()->ConnectToRemoteService(
789 mojo::GetProxy(&background_sync_dispatcher_));
790 background_sync_dispatcher_.set_connection_error_handler(base::Bind(
791 &ServiceWorkerVersion::OnBackgroundSyncDispatcherConnectionError,
792 weak_factory_.GetWeakPtr()));
795 background_sync_dispatcher_->Sync(
796 registration.Pass(), base::Bind(&self::OnSyncEventFinished,
797 weak_factory_.GetWeakPtr(), request_id));
800 void ServiceWorkerVersion::DispatchNotificationClickEvent(
801 const StatusCallback& callback,
802 int64_t persistent_notification_id,
803 const PlatformNotificationData& notification_data) {
804 DCHECK_EQ(ACTIVATED, status()) << status();
805 if (running_status() != RUNNING) {
806 // Schedule calling this method after starting the worker.
807 StartWorker(base::Bind(
808 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(), callback,
809 base::Bind(&self::DispatchNotificationClickEvent,
810 weak_factory_.GetWeakPtr(), callback,
811 persistent_notification_id, notification_data)));
812 return;
815 int request_id = AddRequest(callback, &notification_click_requests_,
816 REQUEST_NOTIFICATION_CLICK);
817 ServiceWorkerStatusCode status =
818 embedded_worker_->SendMessage(ServiceWorkerMsg_NotificationClickEvent(
819 request_id, persistent_notification_id, notification_data));
820 if (status != SERVICE_WORKER_OK) {
821 notification_click_requests_.Remove(request_id);
822 RunSoon(base::Bind(callback, status));
826 void ServiceWorkerVersion::DispatchPushEvent(const StatusCallback& callback,
827 const std::string& data) {
828 DCHECK_EQ(ACTIVATED, status()) << status();
829 if (running_status() != RUNNING) {
830 // Schedule calling this method after starting the worker.
831 StartWorker(base::Bind(&RunTaskAfterStartWorker,
832 weak_factory_.GetWeakPtr(), callback,
833 base::Bind(&self::DispatchPushEvent,
834 weak_factory_.GetWeakPtr(),
835 callback, data)));
836 return;
839 int request_id = AddRequest(callback, &push_requests_, REQUEST_PUSH);
840 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
841 ServiceWorkerMsg_PushEvent(request_id, data));
842 if (status != SERVICE_WORKER_OK) {
843 push_requests_.Remove(request_id);
844 RunSoon(base::Bind(callback, status));
848 void ServiceWorkerVersion::DispatchGeofencingEvent(
849 const StatusCallback& callback,
850 blink::WebGeofencingEventType event_type,
851 const std::string& region_id,
852 const blink::WebCircularGeofencingRegion& region) {
853 DCHECK_EQ(ACTIVATED, status()) << status();
855 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
856 switches::kEnableExperimentalWebPlatformFeatures)) {
857 callback.Run(SERVICE_WORKER_ERROR_ABORT);
858 return;
861 if (running_status() != RUNNING) {
862 // Schedule calling this method after starting the worker.
863 StartWorker(base::Bind(&RunTaskAfterStartWorker,
864 weak_factory_.GetWeakPtr(),
865 callback,
866 base::Bind(&self::DispatchGeofencingEvent,
867 weak_factory_.GetWeakPtr(),
868 callback,
869 event_type,
870 region_id,
871 region)));
872 return;
875 int request_id =
876 AddRequest(callback, &geofencing_requests_, REQUEST_GEOFENCING);
877 ServiceWorkerStatusCode status =
878 embedded_worker_->SendMessage(ServiceWorkerMsg_GeofencingEvent(
879 request_id, event_type, region_id, region));
880 if (status != SERVICE_WORKER_OK) {
881 geofencing_requests_.Remove(request_id);
882 RunSoon(base::Bind(callback, status));
886 void ServiceWorkerVersion::DispatchServicePortConnectEvent(
887 const ServicePortConnectCallback& callback,
888 const GURL& target_url,
889 const GURL& origin,
890 int port_id) {
891 DCHECK_EQ(ACTIVATED, status()) << status();
893 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
894 switches::kEnableExperimentalWebPlatformFeatures)) {
895 callback.Run(SERVICE_WORKER_ERROR_ABORT, false, base::string16(),
896 base::string16());
897 return;
900 if (running_status() != RUNNING) {
901 // Schedule calling this method after starting the worker.
902 StartWorker(
903 base::Bind(&RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
904 base::Bind(&RunErrorServicePortConnectCallback, callback),
905 base::Bind(&self::DispatchServicePortConnectEvent,
906 weak_factory_.GetWeakPtr(), callback, target_url,
907 origin, port_id)));
908 return;
911 int request_id = AddRequest(callback, &service_port_connect_requests_,
912 REQUEST_SERVICE_PORT_CONNECT);
913 if (!service_port_dispatcher_) {
914 embedded_worker_->GetServiceRegistry()->ConnectToRemoteService(
915 mojo::GetProxy(&service_port_dispatcher_));
916 service_port_dispatcher_.set_connection_error_handler(base::Bind(
917 &ServiceWorkerVersion::OnServicePortDispatcherConnectionError,
918 weak_factory_.GetWeakPtr()));
920 service_port_dispatcher_->Connect(
921 mojo::String::From(target_url), mojo::String::From(origin), port_id,
922 base::Bind(&ServiceWorkerVersion::OnServicePortConnectEventFinished,
923 weak_factory_.GetWeakPtr(), request_id));
926 void ServiceWorkerVersion::DispatchCrossOriginMessageEvent(
927 const NavigatorConnectClient& client,
928 const base::string16& message,
929 const std::vector<TransferredMessagePort>& sent_message_ports,
930 const StatusCallback& callback) {
931 // Unlike in the case of DispatchMessageEvent, here the caller is assumed to
932 // have already put all the sent message ports on hold. So no need to do that
933 // here again.
935 if (running_status() != RUNNING) {
936 // Schedule calling this method after starting the worker.
937 StartWorker(base::Bind(
938 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
939 base::Bind(&RunErrorMessageCallback, sent_message_ports, callback),
940 base::Bind(&self::DispatchCrossOriginMessageEvent,
941 weak_factory_.GetWeakPtr(), client, message,
942 sent_message_ports, callback)));
943 return;
946 MessagePortMessageFilter* filter =
947 embedded_worker_->message_port_message_filter();
948 std::vector<int> new_routing_ids;
949 filter->UpdateMessagePortsWithNewRoutes(sent_message_ports, &new_routing_ids);
950 ServiceWorkerStatusCode status =
951 embedded_worker_->SendMessage(ServiceWorkerMsg_CrossOriginMessageToWorker(
952 client, message, sent_message_ports, new_routing_ids));
953 RunSoon(base::Bind(callback, status));
956 void ServiceWorkerVersion::AddControllee(
957 ServiceWorkerProviderHost* provider_host) {
958 const std::string& uuid = provider_host->client_uuid();
959 CHECK(!provider_host->client_uuid().empty());
960 DCHECK(!ContainsKey(controllee_map_, uuid));
961 controllee_map_[uuid] = provider_host;
962 // Keep the worker alive a bit longer right after a new controllee is added.
963 RestartTick(&idle_time_);
964 FOR_EACH_OBSERVER(Listener, listeners_,
965 OnControlleeAdded(this, provider_host));
968 void ServiceWorkerVersion::RemoveControllee(
969 ServiceWorkerProviderHost* provider_host) {
970 const std::string& uuid = provider_host->client_uuid();
971 DCHECK(ContainsKey(controllee_map_, uuid));
972 controllee_map_.erase(uuid);
973 FOR_EACH_OBSERVER(Listener, listeners_,
974 OnControlleeRemoved(this, provider_host));
975 if (HasControllee())
976 return;
977 FOR_EACH_OBSERVER(Listener, listeners_, OnNoControllees(this));
980 bool ServiceWorkerVersion::HasWindowClients() {
981 return !GetWindowClientsInternal(false /* include_uncontrolled */).empty();
984 void ServiceWorkerVersion::AddStreamingURLRequestJob(
985 const ServiceWorkerURLRequestJob* request_job) {
986 DCHECK(streaming_url_request_jobs_.find(request_job) ==
987 streaming_url_request_jobs_.end());
988 streaming_url_request_jobs_.insert(request_job);
991 void ServiceWorkerVersion::RemoveStreamingURLRequestJob(
992 const ServiceWorkerURLRequestJob* request_job) {
993 streaming_url_request_jobs_.erase(request_job);
994 if (is_redundant())
995 StopWorkerIfIdle();
998 void ServiceWorkerVersion::AddListener(Listener* listener) {
999 listeners_.AddObserver(listener);
1002 void ServiceWorkerVersion::RemoveListener(Listener* listener) {
1003 listeners_.RemoveObserver(listener);
1006 void ServiceWorkerVersion::ReportError(ServiceWorkerStatusCode status,
1007 const std::string& status_message) {
1008 if (status_message.empty()) {
1009 OnReportException(base::UTF8ToUTF16(ServiceWorkerStatusToString(status)),
1010 -1, -1, GURL());
1011 } else {
1012 OnReportException(base::UTF8ToUTF16(status_message), -1, -1, GURL());
1016 void ServiceWorkerVersion::SetStartWorkerStatusCode(
1017 ServiceWorkerStatusCode status) {
1018 start_worker_status_ = status;
1021 void ServiceWorkerVersion::Doom() {
1022 DCHECK(!HasControllee());
1023 SetStatus(REDUNDANT);
1024 if (running_status() == STARTING || running_status() == RUNNING)
1025 embedded_worker_->Stop();
1026 if (!context_)
1027 return;
1028 std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
1029 script_cache_map_.GetResources(&resources);
1030 context_->storage()->PurgeResources(resources);
1033 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
1034 embedded_worker()->set_devtools_attached(attached);
1035 if (attached) {
1036 // TODO(falken): Canceling the timeouts when debugging could cause
1037 // heisenbugs; we should instead run them as normal show an educational
1038 // message in DevTools when they occur. crbug.com/470419
1040 // Don't record the startup time metric once DevTools is attached.
1041 ClearTick(&start_time_);
1042 skip_recording_startup_time_ = true;
1044 // Cancel request timeouts.
1045 SetAllRequestTimes(base::TimeTicks());
1046 return;
1048 if (!start_callbacks_.empty()) {
1049 // Reactivate the timer for start timeout.
1050 DCHECK(timeout_timer_.IsRunning());
1051 DCHECK(running_status() == STARTING || running_status() == STOPPING)
1052 << running_status();
1053 RestartTick(&start_time_);
1056 // Reactivate request timeouts.
1057 SetAllRequestTimes(base::TimeTicks::Now());
1060 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
1061 const net::HttpResponseInfo& http_info) {
1062 main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
1063 FOR_EACH_OBSERVER(Listener, listeners_,
1064 OnMainScriptHttpResponseInfoSet(this));
1067 void ServiceWorkerVersion::SimulatePingTimeoutForTesting() {
1068 ping_controller_->SimulateTimeoutForTesting();
1071 const net::HttpResponseInfo*
1072 ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
1073 return main_script_http_info_.get();
1076 ServiceWorkerVersion::RequestInfo::RequestInfo(int id,
1077 RequestType type,
1078 const base::TimeTicks& now)
1079 : id(id), type(type), time(now) {
1082 ServiceWorkerVersion::RequestInfo::~RequestInfo() {
1085 template <typename CallbackType>
1086 ServiceWorkerVersion::PendingRequest<CallbackType>::PendingRequest(
1087 const CallbackType& callback,
1088 const base::TimeTicks& time)
1089 : callback(callback), start_time(time) {
1092 template <typename CallbackType>
1093 ServiceWorkerVersion::PendingRequest<CallbackType>::~PendingRequest() {
1096 void ServiceWorkerVersion::OnScriptLoaded() {
1097 DCHECK_EQ(STARTING, running_status());
1098 // Activate ping/pong now that JavaScript execution will start.
1099 ping_controller_->Activate();
1102 void ServiceWorkerVersion::OnStarting() {
1103 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1106 void ServiceWorkerVersion::OnStarted() {
1107 DCHECK_EQ(RUNNING, running_status());
1108 RestartTick(&idle_time_);
1110 // Fire all start callbacks.
1111 scoped_refptr<ServiceWorkerVersion> protect(this);
1112 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_OK);
1113 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1116 void ServiceWorkerVersion::OnStopping() {
1117 metrics_->NotifyStopping();
1118 RestartTick(&stop_time_);
1119 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1122 void ServiceWorkerVersion::OnStopped(
1123 EmbeddedWorkerInstance::Status old_status) {
1124 metrics_->NotifyStopped();
1125 if (!stop_time_.is_null())
1126 ServiceWorkerMetrics::RecordStopWorkerTime(GetTickDuration(stop_time_));
1128 OnStoppedInternal(old_status);
1131 void ServiceWorkerVersion::OnDetached(
1132 EmbeddedWorkerInstance::Status old_status) {
1133 OnStoppedInternal(old_status);
1136 void ServiceWorkerVersion::OnReportException(
1137 const base::string16& error_message,
1138 int line_number,
1139 int column_number,
1140 const GURL& source_url) {
1141 FOR_EACH_OBSERVER(
1142 Listener,
1143 listeners_,
1144 OnErrorReported(
1145 this, error_message, line_number, column_number, source_url));
1148 void ServiceWorkerVersion::OnReportConsoleMessage(int source_identifier,
1149 int message_level,
1150 const base::string16& message,
1151 int line_number,
1152 const GURL& source_url) {
1153 FOR_EACH_OBSERVER(Listener,
1154 listeners_,
1155 OnReportConsoleMessage(this,
1156 source_identifier,
1157 message_level,
1158 message,
1159 line_number,
1160 source_url));
1163 bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message& message) {
1164 bool handled = true;
1165 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerVersion, message)
1166 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetClients,
1167 OnGetClients)
1168 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ActivateEventFinished,
1169 OnActivateEventFinished)
1170 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_InstallEventFinished,
1171 OnInstallEventFinished)
1172 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FetchEventFinished,
1173 OnFetchEventFinished)
1174 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_NotificationClickEventFinished,
1175 OnNotificationClickEventFinished)
1176 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PushEventFinished,
1177 OnPushEventFinished)
1178 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GeofencingEventFinished,
1179 OnGeofencingEventFinished)
1180 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_OpenWindow,
1181 OnOpenWindow)
1182 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SetCachedMetadata,
1183 OnSetCachedMetadata)
1184 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClearCachedMetadata,
1185 OnClearCachedMetadata)
1186 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToClient,
1187 OnPostMessageToClient)
1188 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FocusClient,
1189 OnFocusClient)
1190 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting,
1191 OnSkipWaiting)
1192 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients,
1193 OnClaimClients)
1194 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_Pong, OnPongFromWorker)
1195 IPC_MESSAGE_UNHANDLED(handled = false)
1196 IPC_END_MESSAGE_MAP()
1197 return handled;
1200 void ServiceWorkerVersion::OnStartSentAndScriptEvaluated(
1201 ServiceWorkerStatusCode status) {
1202 if (status != SERVICE_WORKER_OK) {
1203 RunCallbacks(this, &start_callbacks_,
1204 DeduceStartWorkerFailureReason(status));
1208 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
1209 const StatusCallback& callback) {
1210 DCHECK_EQ(RUNNING, running_status())
1211 << "Worker stopped too soon after it was started.";
1213 int request_id = AddRequest(callback, &install_requests_, REQUEST_INSTALL);
1214 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
1215 ServiceWorkerMsg_InstallEvent(request_id));
1216 if (status != SERVICE_WORKER_OK) {
1217 install_requests_.Remove(request_id);
1218 RunSoon(base::Bind(callback, status));
1222 void ServiceWorkerVersion::DispatchActivateEventAfterStartWorker(
1223 const StatusCallback& callback) {
1224 DCHECK_EQ(RUNNING, running_status())
1225 << "Worker stopped too soon after it was started.";
1227 int request_id = AddRequest(callback, &activate_requests_, REQUEST_ACTIVATE);
1228 ServiceWorkerStatusCode status =
1229 embedded_worker_->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id));
1230 if (status != SERVICE_WORKER_OK) {
1231 activate_requests_.Remove(request_id);
1232 RunSoon(base::Bind(callback, status));
1236 void ServiceWorkerVersion::OnGetClients(
1237 int request_id,
1238 const ServiceWorkerClientQueryOptions& options) {
1239 TRACE_EVENT_ASYNC_BEGIN2(
1240 "ServiceWorker", "ServiceWorkerVersion::OnGetClients", request_id,
1241 "client_type", options.client_type, "include_uncontrolled",
1242 options.include_uncontrolled);
1244 if (controllee_map_.empty() && !options.include_uncontrolled) {
1245 OnGetClientsFinished(request_id, std::vector<ServiceWorkerClientInfo>());
1246 return;
1249 // For Window clients we want to query the info on the UI thread first.
1250 if (options.client_type == blink::WebServiceWorkerClientTypeWindow ||
1251 options.client_type == blink::WebServiceWorkerClientTypeAll) {
1252 GetWindowClients(request_id, options);
1253 return;
1256 ServiceWorkerClients clients;
1257 GetNonWindowClients(request_id, options, &clients);
1258 OnGetClientsFinished(request_id, clients);
1261 void ServiceWorkerVersion::OnGetClientsFinished(
1262 int request_id,
1263 const ServiceWorkerClients& clients) {
1264 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1265 TRACE_EVENT_ASYNC_END1("ServiceWorker", "ServiceWorkerVersion::OnGetClients",
1266 request_id, "The number of clients", clients.size());
1268 if (running_status() != RUNNING)
1269 return;
1270 embedded_worker_->SendMessage(
1271 ServiceWorkerMsg_DidGetClients(request_id, clients));
1274 void ServiceWorkerVersion::OnActivateEventFinished(
1275 int request_id,
1276 blink::WebServiceWorkerEventResult result) {
1277 DCHECK(ACTIVATING == status() ||
1278 REDUNDANT == status()) << status();
1279 TRACE_EVENT0("ServiceWorker",
1280 "ServiceWorkerVersion::OnActivateEventFinished");
1282 PendingRequest<StatusCallback>* request =
1283 activate_requests_.Lookup(request_id);
1284 if (!request) {
1285 NOTREACHED() << "Got unexpected message: " << request_id;
1286 return;
1288 ServiceWorkerStatusCode rv = SERVICE_WORKER_OK;
1289 if (result == blink::WebServiceWorkerEventResultRejected)
1290 rv = SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED;
1292 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ActivateEvent.Time",
1293 base::TimeTicks::Now() - request->start_time);
1295 scoped_refptr<ServiceWorkerVersion> protect(this);
1296 request->callback.Run(rv);
1297 RemoveCallbackAndStopIfRedundant(&activate_requests_, request_id);
1300 void ServiceWorkerVersion::OnInstallEventFinished(
1301 int request_id,
1302 blink::WebServiceWorkerEventResult result) {
1303 // Status is REDUNDANT if the worker was doomed while handling the install
1304 // event, and finished handling before being terminated.
1305 DCHECK(status() == INSTALLING || status() == REDUNDANT) << status();
1306 TRACE_EVENT0("ServiceWorker",
1307 "ServiceWorkerVersion::OnInstallEventFinished");
1309 PendingRequest<StatusCallback>* request =
1310 install_requests_.Lookup(request_id);
1311 if (!request) {
1312 NOTREACHED() << "Got unexpected message: " << request_id;
1313 return;
1315 ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
1316 if (result == blink::WebServiceWorkerEventResultRejected)
1317 status = SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED;
1319 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.InstallEvent.Time",
1320 base::TimeTicks::Now() - request->start_time);
1322 scoped_refptr<ServiceWorkerVersion> protect(this);
1323 request->callback.Run(status);
1324 RemoveCallbackAndStopIfRedundant(&install_requests_, request_id);
1327 void ServiceWorkerVersion::OnFetchEventFinished(
1328 int request_id,
1329 ServiceWorkerFetchEventResult result,
1330 const ServiceWorkerResponse& response) {
1331 TRACE_EVENT1("ServiceWorker",
1332 "ServiceWorkerVersion::OnFetchEventFinished",
1333 "Request id", request_id);
1334 PendingRequest<FetchCallback>* request = fetch_requests_.Lookup(request_id);
1335 if (!request) {
1336 NOTREACHED() << "Got unexpected message: " << request_id;
1337 return;
1340 // TODO(kinuko): Record other event statuses too.
1341 const bool handled = (result == SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE);
1342 metrics_->RecordEventHandledStatus(ServiceWorkerMetrics::EVENT_TYPE_FETCH,
1343 handled);
1345 ServiceWorkerMetrics::RecordFetchEventTime(
1346 result, base::TimeTicks::Now() - request->start_time);
1348 scoped_refptr<ServiceWorkerVersion> protect(this);
1349 request->callback.Run(SERVICE_WORKER_OK, result, response);
1350 RemoveCallbackAndStopIfRedundant(&fetch_requests_, request_id);
1353 void ServiceWorkerVersion::OnSyncEventFinished(
1354 int request_id,
1355 ServiceWorkerEventStatus status) {
1356 TRACE_EVENT1("ServiceWorker",
1357 "ServiceWorkerVersion::OnSyncEventFinished",
1358 "Request id", request_id);
1359 PendingRequest<StatusCallback>* request = sync_requests_.Lookup(request_id);
1360 if (!request) {
1361 NOTREACHED() << "Got unexpected message: " << request_id;
1362 return;
1365 scoped_refptr<ServiceWorkerVersion> protect(this);
1366 request->callback.Run(mojo::ConvertTo<ServiceWorkerStatusCode>(status));
1367 RemoveCallbackAndStopIfRedundant(&sync_requests_, request_id);
1370 void ServiceWorkerVersion::OnNotificationClickEventFinished(
1371 int request_id) {
1372 TRACE_EVENT1("ServiceWorker",
1373 "ServiceWorkerVersion::OnNotificationClickEventFinished",
1374 "Request id", request_id);
1375 PendingRequest<StatusCallback>* request =
1376 notification_click_requests_.Lookup(request_id);
1377 if (!request) {
1378 NOTREACHED() << "Got unexpected message: " << request_id;
1379 return;
1382 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.NotificationClickEvent.Time",
1383 base::TimeTicks::Now() - request->start_time);
1385 scoped_refptr<ServiceWorkerVersion> protect(this);
1386 request->callback.Run(SERVICE_WORKER_OK);
1387 RemoveCallbackAndStopIfRedundant(&notification_click_requests_, request_id);
1390 void ServiceWorkerVersion::OnPushEventFinished(
1391 int request_id,
1392 blink::WebServiceWorkerEventResult result) {
1393 TRACE_EVENT1("ServiceWorker",
1394 "ServiceWorkerVersion::OnPushEventFinished",
1395 "Request id", request_id);
1396 PendingRequest<StatusCallback>* request = push_requests_.Lookup(request_id);
1397 if (!request) {
1398 NOTREACHED() << "Got unexpected message: " << request_id;
1399 return;
1401 ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
1402 if (result == blink::WebServiceWorkerEventResultRejected)
1403 status = SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED;
1405 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.PushEvent.Time",
1406 base::TimeTicks::Now() - request->start_time);
1408 scoped_refptr<ServiceWorkerVersion> protect(this);
1409 request->callback.Run(status);
1410 RemoveCallbackAndStopIfRedundant(&push_requests_, request_id);
1413 void ServiceWorkerVersion::OnGeofencingEventFinished(int request_id) {
1414 TRACE_EVENT1("ServiceWorker",
1415 "ServiceWorkerVersion::OnGeofencingEventFinished",
1416 "Request id",
1417 request_id);
1418 PendingRequest<StatusCallback>* request =
1419 geofencing_requests_.Lookup(request_id);
1420 if (!request) {
1421 NOTREACHED() << "Got unexpected message: " << request_id;
1422 return;
1425 scoped_refptr<ServiceWorkerVersion> protect(this);
1426 request->callback.Run(SERVICE_WORKER_OK);
1427 RemoveCallbackAndStopIfRedundant(&geofencing_requests_, request_id);
1430 void ServiceWorkerVersion::OnServicePortConnectEventFinished(
1431 int request_id,
1432 ServicePortConnectResult result,
1433 const mojo::String& name,
1434 const mojo::String& data) {
1435 TRACE_EVENT1("ServiceWorker",
1436 "ServiceWorkerVersion::OnServicePortConnectEventFinished",
1437 "Request id", request_id);
1438 PendingRequest<ServicePortConnectCallback>* request =
1439 service_port_connect_requests_.Lookup(request_id);
1440 if (!request) {
1441 NOTREACHED() << "Got unexpected message: " << request_id;
1442 return;
1445 scoped_refptr<ServiceWorkerVersion> protect(this);
1446 request->callback.Run(SERVICE_WORKER_OK,
1447 result == SERVICE_PORT_CONNECT_RESULT_ACCEPT,
1448 name.To<base::string16>(), data.To<base::string16>());
1449 RemoveCallbackAndStopIfRedundant(&service_port_connect_requests_, request_id);
1452 void ServiceWorkerVersion::OnOpenWindow(int request_id, GURL url) {
1453 // Just abort if we are shutting down.
1454 if (!context_)
1455 return;
1457 if (!url.is_valid()) {
1458 DVLOG(1) << "Received unexpected invalid URL from renderer process.";
1459 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
1460 base::Bind(&KillEmbeddedWorkerProcess,
1461 embedded_worker_->process_id(),
1462 RESULT_CODE_KILLED_BAD_MESSAGE));
1463 return;
1466 // The renderer treats all URLs in the about: scheme as being about:blank.
1467 // Canonicalize about: URLs to about:blank.
1468 if (url.SchemeIs(url::kAboutScheme))
1469 url = GURL(url::kAboutBlankURL);
1471 // Reject requests for URLs that the process is not allowed to access. It's
1472 // possible to receive such requests since the renderer-side checks are
1473 // slightly different. For example, the view-source scheme will not be
1474 // filtered out by Blink.
1475 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
1476 embedded_worker_->process_id(), url)) {
1477 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowError(
1478 request_id, url.spec() + " cannot be opened."));
1479 return;
1482 BrowserThread::PostTask(
1483 BrowserThread::UI, FROM_HERE,
1484 base::Bind(&OpenWindowOnUI,
1485 url,
1486 script_url_,
1487 embedded_worker_->process_id(),
1488 make_scoped_refptr(context_->wrapper()),
1489 base::Bind(&ServiceWorkerVersion::DidOpenWindow,
1490 weak_factory_.GetWeakPtr(),
1491 request_id)));
1494 void ServiceWorkerVersion::DidOpenWindow(int request_id,
1495 int render_process_id,
1496 int render_frame_id) {
1497 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1499 if (running_status() != RUNNING)
1500 return;
1502 if (render_process_id == ChildProcessHost::kInvalidUniqueID &&
1503 render_frame_id == MSG_ROUTING_NONE) {
1504 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowError(
1505 request_id, "Something went wrong while trying to open the window."));
1506 return;
1509 for (auto it =
1510 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1511 !it->IsAtEnd(); it->Advance()) {
1512 ServiceWorkerProviderHost* provider_host = it->GetProviderHost();
1513 if (provider_host->process_id() != render_process_id ||
1514 provider_host->frame_id() != render_frame_id) {
1515 continue;
1517 provider_host->GetWindowClientInfo(base::Bind(
1518 &ServiceWorkerVersion::OnOpenWindowFinished, weak_factory_.GetWeakPtr(),
1519 request_id, provider_host->client_uuid()));
1520 return;
1523 // If here, it means that no provider_host was found, in which case, the
1524 // renderer should still be informed that the window was opened.
1525 OnOpenWindowFinished(request_id, std::string(), ServiceWorkerClientInfo());
1528 void ServiceWorkerVersion::OnOpenWindowFinished(
1529 int request_id,
1530 const std::string& client_uuid,
1531 const ServiceWorkerClientInfo& client_info) {
1532 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1534 if (running_status() != RUNNING)
1535 return;
1537 ServiceWorkerClientInfo client(client_info);
1539 // If the |client_info| is empty, it means that the opened window wasn't
1540 // controlled but the action still succeeded. The renderer process is
1541 // expecting an empty client in such case.
1542 if (!client.IsEmpty())
1543 client.client_uuid = client_uuid;
1545 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowResponse(
1546 request_id, client));
1549 void ServiceWorkerVersion::OnSetCachedMetadata(const GURL& url,
1550 const std::vector<char>& data) {
1551 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
1552 TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
1553 "ServiceWorkerVersion::OnSetCachedMetadata",
1554 callback_id, "URL", url.spec());
1555 script_cache_map_.WriteMetadata(
1556 url, data, base::Bind(&ServiceWorkerVersion::OnSetCachedMetadataFinished,
1557 weak_factory_.GetWeakPtr(), callback_id));
1560 void ServiceWorkerVersion::OnSetCachedMetadataFinished(int64 callback_id,
1561 int result) {
1562 TRACE_EVENT_ASYNC_END1("ServiceWorker",
1563 "ServiceWorkerVersion::OnSetCachedMetadata",
1564 callback_id, "result", result);
1565 FOR_EACH_OBSERVER(Listener, listeners_, OnCachedMetadataUpdated(this));
1568 void ServiceWorkerVersion::OnClearCachedMetadata(const GURL& url) {
1569 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
1570 TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
1571 "ServiceWorkerVersion::OnClearCachedMetadata",
1572 callback_id, "URL", url.spec());
1573 script_cache_map_.ClearMetadata(
1574 url, base::Bind(&ServiceWorkerVersion::OnClearCachedMetadataFinished,
1575 weak_factory_.GetWeakPtr(), callback_id));
1578 void ServiceWorkerVersion::OnClearCachedMetadataFinished(int64 callback_id,
1579 int result) {
1580 TRACE_EVENT_ASYNC_END1("ServiceWorker",
1581 "ServiceWorkerVersion::OnClearCachedMetadata",
1582 callback_id, "result", result);
1583 FOR_EACH_OBSERVER(Listener, listeners_, OnCachedMetadataUpdated(this));
1586 void ServiceWorkerVersion::OnPostMessageToClient(
1587 const std::string& client_uuid,
1588 const base::string16& message,
1589 const std::vector<TransferredMessagePort>& sent_message_ports) {
1590 if (!context_)
1591 return;
1592 TRACE_EVENT1("ServiceWorker",
1593 "ServiceWorkerVersion::OnPostMessageToDocument",
1594 "Client id", client_uuid);
1595 ServiceWorkerProviderHost* provider_host =
1596 context_->GetProviderHostByClientID(client_uuid);
1597 if (!provider_host) {
1598 // The client may already have been closed, just ignore.
1599 return;
1601 if (provider_host->document_url().GetOrigin() != script_url_.GetOrigin()) {
1602 // The client does not belong to the same origin as this ServiceWorker,
1603 // possibly due to timing issue or bad message.
1604 return;
1606 provider_host->PostMessage(this, message, sent_message_ports);
1609 void ServiceWorkerVersion::OnFocusClient(int request_id,
1610 const std::string& client_uuid) {
1611 if (!context_)
1612 return;
1613 TRACE_EVENT2("ServiceWorker",
1614 "ServiceWorkerVersion::OnFocusClient",
1615 "Request id", request_id,
1616 "Client id", client_uuid);
1617 ServiceWorkerProviderHost* provider_host =
1618 context_->GetProviderHostByClientID(client_uuid);
1619 if (!provider_host) {
1620 // The client may already have been closed, just ignore.
1621 return;
1623 if (provider_host->document_url().GetOrigin() != script_url_.GetOrigin()) {
1624 // The client does not belong to the same origin as this ServiceWorker,
1625 // possibly due to timing issue or bad message.
1626 return;
1628 provider_host->Focus(base::Bind(&ServiceWorkerVersion::OnFocusClientFinished,
1629 weak_factory_.GetWeakPtr(), request_id,
1630 client_uuid));
1633 void ServiceWorkerVersion::OnFocusClientFinished(
1634 int request_id,
1635 const std::string& client_uuid,
1636 const ServiceWorkerClientInfo& client) {
1637 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1639 if (running_status() != RUNNING)
1640 return;
1642 ServiceWorkerClientInfo client_info(client);
1643 client_info.client_uuid = client_uuid;
1645 embedded_worker_->SendMessage(ServiceWorkerMsg_FocusClientResponse(
1646 request_id, client_info));
1649 void ServiceWorkerVersion::OnSkipWaiting(int request_id) {
1650 skip_waiting_ = true;
1651 if (status_ != INSTALLED)
1652 return DidSkipWaiting(request_id);
1654 if (!context_)
1655 return;
1656 ServiceWorkerRegistration* registration =
1657 context_->GetLiveRegistration(registration_id_);
1658 if (!registration)
1659 return;
1660 pending_skip_waiting_requests_.push_back(request_id);
1661 if (pending_skip_waiting_requests_.size() == 1)
1662 registration->ActivateWaitingVersionWhenReady();
1665 void ServiceWorkerVersion::DidSkipWaiting(int request_id) {
1666 if (running_status() == STARTING || running_status() == RUNNING)
1667 embedded_worker_->SendMessage(ServiceWorkerMsg_DidSkipWaiting(request_id));
1670 void ServiceWorkerVersion::OnClaimClients(int request_id) {
1671 if (status_ != ACTIVATING && status_ != ACTIVATED) {
1672 embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
1673 request_id, blink::WebServiceWorkerError::ErrorTypeState,
1674 base::ASCIIToUTF16(kClaimClientsStateErrorMesage)));
1675 return;
1677 if (context_) {
1678 if (ServiceWorkerRegistration* registration =
1679 context_->GetLiveRegistration(registration_id_)) {
1680 registration->ClaimClients();
1681 embedded_worker_->SendMessage(
1682 ServiceWorkerMsg_DidClaimClients(request_id));
1683 return;
1687 embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
1688 request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
1689 base::ASCIIToUTF16(kClaimClientsShutdownErrorMesage)));
1692 void ServiceWorkerVersion::OnPongFromWorker() {
1693 ping_controller_->OnPongReceived();
1696 void ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker(
1697 const StatusCallback& callback,
1698 ServiceWorkerStatusCode status,
1699 const scoped_refptr<ServiceWorkerRegistration>& protect) {
1700 if (status != SERVICE_WORKER_OK) {
1701 RecordStartWorkerResult(status);
1702 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED));
1703 return;
1705 if (is_redundant()) {
1706 RecordStartWorkerResult(SERVICE_WORKER_ERROR_REDUNDANT);
1707 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_REDUNDANT));
1708 return;
1711 MarkIfStale();
1713 switch (running_status()) {
1714 case RUNNING:
1715 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
1716 return;
1717 case STOPPING:
1718 case STOPPED:
1719 case STARTING:
1720 if (start_callbacks_.empty()) {
1721 start_callbacks_.push_back(
1722 base::Bind(&ServiceWorkerVersion::RecordStartWorkerResult,
1723 weak_factory_.GetWeakPtr()));
1725 // Keep the live registration while starting the worker.
1726 start_callbacks_.push_back(
1727 base::Bind(&RunStartWorkerCallback, callback, protect));
1728 StartWorkerInternal();
1729 return;
1733 void ServiceWorkerVersion::StartWorkerInternal() {
1734 if (!metrics_)
1735 metrics_.reset(new Metrics(this));
1737 if (!timeout_timer_.IsRunning())
1738 StartTimeoutTimer();
1739 if (running_status() == STOPPED) {
1740 embedded_worker_->Start(
1741 version_id_, scope_, script_url_,
1742 base::Bind(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated,
1743 weak_factory_.GetWeakPtr()));
1747 void ServiceWorkerVersion::GetWindowClients(
1748 int request_id,
1749 const ServiceWorkerClientQueryOptions& options) {
1750 DCHECK(options.client_type == blink::WebServiceWorkerClientTypeWindow ||
1751 options.client_type == blink::WebServiceWorkerClientTypeAll);
1752 const std::vector<base::Tuple<int, int, std::string>>& clients_info =
1753 GetWindowClientsInternal(options.include_uncontrolled);
1755 if (clients_info.empty()) {
1756 DidGetWindowClients(request_id, options,
1757 make_scoped_ptr(new ServiceWorkerClients));
1758 return;
1761 BrowserThread::PostTask(
1762 BrowserThread::UI, FROM_HERE,
1763 base::Bind(&OnGetWindowClientsFromUI, clients_info, script_url_,
1764 base::Bind(&ServiceWorkerVersion::DidGetWindowClients,
1765 weak_factory_.GetWeakPtr(), request_id, options)));
1768 const std::vector<base::Tuple<int, int, std::string>>
1769 ServiceWorkerVersion::GetWindowClientsInternal(bool include_uncontrolled) {
1770 std::vector<base::Tuple<int, int, std::string>> clients_info;
1771 if (!include_uncontrolled) {
1772 for (auto& controllee : controllee_map_)
1773 AddWindowClient(controllee.second, &clients_info);
1774 } else {
1775 for (auto it =
1776 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1777 !it->IsAtEnd(); it->Advance()) {
1778 AddWindowClient(it->GetProviderHost(), &clients_info);
1781 return clients_info;
1784 void ServiceWorkerVersion::DidGetWindowClients(
1785 int request_id,
1786 const ServiceWorkerClientQueryOptions& options,
1787 scoped_ptr<ServiceWorkerClients> clients) {
1788 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1789 if (options.client_type == blink::WebServiceWorkerClientTypeAll)
1790 GetNonWindowClients(request_id, options, clients.get());
1791 OnGetClientsFinished(request_id, *clients);
1794 void ServiceWorkerVersion::GetNonWindowClients(
1795 int request_id,
1796 const ServiceWorkerClientQueryOptions& options,
1797 ServiceWorkerClients* clients) {
1798 if (!options.include_uncontrolled) {
1799 for (auto& controllee : controllee_map_) {
1800 AddNonWindowClient(controllee.second, options, clients);
1802 } else {
1803 for (auto it =
1804 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1805 !it->IsAtEnd(); it->Advance()) {
1806 AddNonWindowClient(it->GetProviderHost(), options, clients);
1811 void ServiceWorkerVersion::StartTimeoutTimer() {
1812 DCHECK(!timeout_timer_.IsRunning());
1814 if (embedded_worker_->devtools_attached()) {
1815 // Don't record the startup time metric once DevTools is attached.
1816 ClearTick(&start_time_);
1817 skip_recording_startup_time_ = true;
1818 } else {
1819 RestartTick(&start_time_);
1820 skip_recording_startup_time_ = false;
1823 ClearTick(&idle_time_);
1825 // Ping will be activated in OnScriptLoaded.
1826 ping_controller_->Deactivate();
1828 timeout_timer_.Start(FROM_HERE,
1829 base::TimeDelta::FromSeconds(kTimeoutTimerDelaySeconds),
1830 this, &ServiceWorkerVersion::OnTimeoutTimer);
1833 void ServiceWorkerVersion::StopTimeoutTimer() {
1834 timeout_timer_.Stop();
1836 // Trigger update if worker is stale.
1837 if (!stale_time_.is_null()) {
1838 ClearTick(&stale_time_);
1839 if (!update_timer_.IsRunning())
1840 ScheduleUpdate();
1844 void ServiceWorkerVersion::OnTimeoutTimer() {
1845 DCHECK(running_status() == STARTING || running_status() == RUNNING ||
1846 running_status() == STOPPING)
1847 << running_status();
1849 MarkIfStale();
1851 if (GetTickDuration(stop_time_) >
1852 base::TimeDelta::FromSeconds(kStopWorkerTimeoutSeconds)) {
1853 metrics_->NotifyStalledInStopping();
1856 // Trigger update if worker is stale and we waited long enough for it to go
1857 // idle.
1858 if (GetTickDuration(stale_time_) >
1859 base::TimeDelta::FromMinutes(kRequestTimeoutMinutes)) {
1860 ClearTick(&stale_time_);
1861 if (!update_timer_.IsRunning())
1862 ScheduleUpdate();
1865 // Starting a worker hasn't finished within a certain period.
1866 if (GetTickDuration(start_time_) >
1867 base::TimeDelta::FromMinutes(kStartWorkerTimeoutMinutes)) {
1868 DCHECK(running_status() == STARTING || running_status() == STOPPING)
1869 << running_status();
1870 scoped_refptr<ServiceWorkerVersion> protect(this);
1871 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_ERROR_TIMEOUT);
1872 if (running_status() == STARTING)
1873 embedded_worker_->Stop();
1874 return;
1877 // Requests have not finished within a certain period.
1878 bool request_timed_out = false;
1879 while (!requests_.empty()) {
1880 RequestInfo info = requests_.front();
1881 if (GetTickDuration(info.time) <
1882 base::TimeDelta::FromMinutes(kRequestTimeoutMinutes))
1883 break;
1884 if (OnRequestTimeout(info))
1885 request_timed_out = true;
1886 requests_.pop();
1888 if (request_timed_out && running_status() != STOPPING)
1889 embedded_worker_->Stop();
1891 // For the timeouts below, there are no callbacks to timeout so there is
1892 // nothing more to do if the worker is already stopping.
1893 if (running_status() == STOPPING)
1894 return;
1896 // The worker has been idle for longer than a certain period.
1897 if (GetTickDuration(idle_time_) >
1898 base::TimeDelta::FromSeconds(kIdleWorkerTimeoutSeconds)) {
1899 StopWorkerIfIdle();
1900 return;
1903 // Check ping status.
1904 ping_controller_->CheckPingStatus();
1907 ServiceWorkerStatusCode ServiceWorkerVersion::PingWorker() {
1908 DCHECK(running_status() == STARTING || running_status() == RUNNING);
1909 return embedded_worker_->SendMessage(ServiceWorkerMsg_Ping());
1912 void ServiceWorkerVersion::OnPingTimeout() {
1913 DCHECK(running_status() == STARTING || running_status() == RUNNING);
1914 // TODO(falken): Show a message to the developer that the SW was stopped due
1915 // to timeout (crbug.com/457968). Also, change the error code to
1916 // SERVICE_WORKER_ERROR_TIMEOUT.
1917 StopWorkerIfIdle();
1920 void ServiceWorkerVersion::StopWorkerIfIdle() {
1921 if (HasInflightRequests() && !ping_controller_->IsTimedOut())
1922 return;
1923 if (running_status() == STOPPED || running_status() == STOPPING ||
1924 !stop_callbacks_.empty()) {
1925 return;
1928 // TODO(falken): We may need to handle StopIfIdle failure and
1929 // forcibly fail pending callbacks so no one is stuck waiting
1930 // for the worker.
1931 embedded_worker_->StopIfIdle();
1934 bool ServiceWorkerVersion::HasInflightRequests() const {
1935 return !activate_requests_.IsEmpty() || !install_requests_.IsEmpty() ||
1936 !fetch_requests_.IsEmpty() || !sync_requests_.IsEmpty() ||
1937 !notification_click_requests_.IsEmpty() || !push_requests_.IsEmpty() ||
1938 !geofencing_requests_.IsEmpty() ||
1939 !service_port_connect_requests_.IsEmpty() ||
1940 !streaming_url_request_jobs_.empty();
1943 void ServiceWorkerVersion::RecordStartWorkerResult(
1944 ServiceWorkerStatusCode status) {
1945 base::TimeTicks start_time = start_time_;
1946 ClearTick(&start_time_);
1948 ServiceWorkerMetrics::RecordStartWorkerStatus(status,
1949 IsInstalled(prestart_status_));
1951 if (status == SERVICE_WORKER_OK && !start_time.is_null() &&
1952 !skip_recording_startup_time_) {
1953 ServiceWorkerMetrics::RecordStartWorkerTime(GetTickDuration(start_time),
1954 IsInstalled(prestart_status_));
1957 if (status != SERVICE_WORKER_ERROR_TIMEOUT)
1958 return;
1959 EmbeddedWorkerInstance::StartingPhase phase =
1960 EmbeddedWorkerInstance::NOT_STARTING;
1961 EmbeddedWorkerInstance::Status running_status = embedded_worker_->status();
1962 // Build an artifical JavaScript exception to show in the ServiceWorker
1963 // log for developers; it's not user-facing so it's not a localized resource.
1964 std::string message = "ServiceWorker startup timed out. ";
1965 if (running_status != EmbeddedWorkerInstance::STARTING) {
1966 message.append("The worker had unexpected status: ");
1967 message.append(EmbeddedWorkerInstance::StatusToString(running_status));
1968 } else {
1969 phase = embedded_worker_->starting_phase();
1970 message.append("The worker was in startup phase: ");
1971 message.append(EmbeddedWorkerInstance::StartingPhaseToString(phase));
1973 message.append(".");
1974 OnReportException(base::UTF8ToUTF16(message), -1, -1, GURL());
1975 DVLOG(1) << message;
1976 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.TimeoutPhase",
1977 phase,
1978 EmbeddedWorkerInstance::STARTING_PHASE_MAX_VALUE);
1981 template <typename IDMAP>
1982 void ServiceWorkerVersion::RemoveCallbackAndStopIfRedundant(IDMAP* callbacks,
1983 int request_id) {
1984 RestartTick(&idle_time_);
1985 callbacks->Remove(request_id);
1986 if (is_redundant()) {
1987 // The stop should be already scheduled, but try to stop immediately, in
1988 // order to release worker resources soon.
1989 StopWorkerIfIdle();
1993 template <typename CallbackType>
1994 int ServiceWorkerVersion::AddRequest(
1995 const CallbackType& callback,
1996 IDMap<PendingRequest<CallbackType>, IDMapOwnPointer>* callback_map,
1997 RequestType request_type) {
1998 base::TimeTicks now = base::TimeTicks::Now();
1999 int request_id =
2000 callback_map->Add(new PendingRequest<CallbackType>(callback, now));
2001 requests_.push(RequestInfo(request_id, request_type, now));
2002 return request_id;
2005 bool ServiceWorkerVersion::OnRequestTimeout(const RequestInfo& info) {
2006 switch (info.type) {
2007 case REQUEST_ACTIVATE:
2008 return RunIDMapCallback(&activate_requests_, info.id,
2009 SERVICE_WORKER_ERROR_TIMEOUT);
2010 case REQUEST_INSTALL:
2011 return RunIDMapCallback(&install_requests_, info.id,
2012 SERVICE_WORKER_ERROR_TIMEOUT);
2013 case REQUEST_FETCH:
2014 return RunIDMapCallback(
2015 &fetch_requests_, info.id, SERVICE_WORKER_ERROR_TIMEOUT,
2016 /* The other args are ignored for non-OK status. */
2017 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK, ServiceWorkerResponse());
2018 case REQUEST_SYNC:
2019 return RunIDMapCallback(&sync_requests_, info.id,
2020 SERVICE_WORKER_ERROR_TIMEOUT);
2021 case REQUEST_NOTIFICATION_CLICK:
2022 return RunIDMapCallback(&notification_click_requests_, info.id,
2023 SERVICE_WORKER_ERROR_TIMEOUT);
2024 case REQUEST_PUSH:
2025 return RunIDMapCallback(&push_requests_, info.id,
2026 SERVICE_WORKER_ERROR_TIMEOUT);
2027 case REQUEST_GEOFENCING:
2028 return RunIDMapCallback(&geofencing_requests_, info.id,
2029 SERVICE_WORKER_ERROR_TIMEOUT);
2030 case REQUEST_SERVICE_PORT_CONNECT:
2031 return RunIDMapCallback(&service_port_connect_requests_, info.id,
2032 SERVICE_WORKER_ERROR_TIMEOUT,
2033 false /* accept_connection */, base::string16(),
2034 base::string16());
2036 NOTREACHED() << "Got unexpected request type: " << info.type;
2037 return false;
2040 void ServiceWorkerVersion::SetAllRequestTimes(const base::TimeTicks& ticks) {
2041 std::queue<RequestInfo> new_requests;
2042 while (!requests_.empty()) {
2043 RequestInfo info = requests_.front();
2044 info.time = ticks;
2045 new_requests.push(info);
2046 requests_.pop();
2048 requests_ = new_requests;
2051 ServiceWorkerStatusCode ServiceWorkerVersion::DeduceStartWorkerFailureReason(
2052 ServiceWorkerStatusCode default_code) {
2053 if (ping_controller_->IsTimedOut())
2054 return SERVICE_WORKER_ERROR_TIMEOUT;
2056 if (start_worker_status_ != SERVICE_WORKER_OK)
2057 return start_worker_status_;
2059 const net::URLRequestStatus& main_script_status =
2060 script_cache_map()->main_script_status();
2061 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
2062 switch (main_script_status.error()) {
2063 case net::ERR_INSECURE_RESPONSE:
2064 case net::ERR_UNSAFE_REDIRECT:
2065 return SERVICE_WORKER_ERROR_SECURITY;
2066 case net::ERR_ABORTED:
2067 return SERVICE_WORKER_ERROR_ABORT;
2068 default:
2069 return SERVICE_WORKER_ERROR_NETWORK;
2073 return default_code;
2076 void ServiceWorkerVersion::MarkIfStale() {
2077 if (!context_)
2078 return;
2079 if (update_timer_.IsRunning() || !stale_time_.is_null())
2080 return;
2081 ServiceWorkerRegistration* registration =
2082 context_->GetLiveRegistration(registration_id_);
2083 if (!registration || registration->active_version() != this)
2084 return;
2085 base::TimeDelta time_since_last_check =
2086 base::Time::Now() - registration->last_update_check();
2087 if (time_since_last_check >
2088 base::TimeDelta::FromHours(kServiceWorkerScriptMaxCacheAgeInHours))
2089 RestartTick(&stale_time_);
2092 void ServiceWorkerVersion::FoundRegistrationForUpdate(
2093 ServiceWorkerStatusCode status,
2094 const scoped_refptr<ServiceWorkerRegistration>& registration) {
2095 if (!context_)
2096 return;
2098 const scoped_refptr<ServiceWorkerVersion> protect = this;
2099 if (is_update_scheduled_) {
2100 context_->UnprotectVersion(version_id_);
2101 is_update_scheduled_ = false;
2104 if (status != SERVICE_WORKER_OK || registration->active_version() != this)
2105 return;
2106 context_->UpdateServiceWorker(registration.get(),
2107 false /* force_bypass_cache */);
2110 void ServiceWorkerVersion::OnStoppedInternal(
2111 EmbeddedWorkerInstance::Status old_status) {
2112 DCHECK_EQ(STOPPED, running_status());
2113 scoped_refptr<ServiceWorkerVersion> protect(this);
2115 DCHECK(metrics_);
2116 metrics_.reset();
2118 bool should_restart = !is_redundant() && !start_callbacks_.empty() &&
2119 (old_status != EmbeddedWorkerInstance::STARTING);
2121 ClearTick(&stop_time_);
2122 StopTimeoutTimer();
2124 if (ping_controller_->IsTimedOut())
2125 should_restart = false;
2127 // Fire all stop callbacks.
2128 RunCallbacks(this, &stop_callbacks_, SERVICE_WORKER_OK);
2130 if (!should_restart) {
2131 // Let all start callbacks fail.
2132 RunCallbacks(this, &start_callbacks_,
2133 DeduceStartWorkerFailureReason(
2134 SERVICE_WORKER_ERROR_START_WORKER_FAILED));
2137 // Let all message callbacks fail (this will also fire and clear all
2138 // callbacks for events).
2139 // TODO(kinuko): Consider if we want to add queue+resend mechanism here.
2140 RunIDMapCallbacks(&activate_requests_,
2141 SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED);
2142 RunIDMapCallbacks(&install_requests_,
2143 SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
2144 RunIDMapCallbacks(&fetch_requests_, SERVICE_WORKER_ERROR_FAILED,
2145 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
2146 ServiceWorkerResponse());
2147 RunIDMapCallbacks(&sync_requests_, SERVICE_WORKER_ERROR_FAILED);
2148 RunIDMapCallbacks(&notification_click_requests_, SERVICE_WORKER_ERROR_FAILED);
2149 RunIDMapCallbacks(&push_requests_, SERVICE_WORKER_ERROR_FAILED);
2150 RunIDMapCallbacks(&geofencing_requests_, SERVICE_WORKER_ERROR_FAILED);
2152 // Close all mojo services. This will also fire and clear all callbacks
2153 // for messages that are still outstanding for those services.
2154 OnServicePortDispatcherConnectionError();
2156 OnBackgroundSyncDispatcherConnectionError();
2158 streaming_url_request_jobs_.clear();
2160 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
2162 if (should_restart)
2163 StartWorkerInternal();
2166 void ServiceWorkerVersion::OnServicePortDispatcherConnectionError() {
2167 RunIDMapCallbacks(&service_port_connect_requests_,
2168 SERVICE_WORKER_ERROR_FAILED, false, base::string16(),
2169 base::string16());
2170 service_port_dispatcher_.reset();
2173 void ServiceWorkerVersion::OnBackgroundSyncDispatcherConnectionError() {
2174 RunIDMapCallbacks(&sync_requests_, SERVICE_WORKER_ERROR_FAILED);
2175 background_sync_dispatcher_.reset();
2178 } // namespace content