Move Service Worker %2f path validation logic from browser into Blink (1)
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_version.cc
blob5415d33b9df232571a4495951aee5fd81e5ec295
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/guid.h"
12 #include "base/location.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string16.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/time/time.h"
21 #include "content/browser/bad_message.h"
22 #include "content/browser/child_process_security_policy_impl.h"
23 #include "content/browser/message_port_message_filter.h"
24 #include "content/browser/message_port_service.h"
25 #include "content/browser/service_worker/embedded_worker_instance.h"
26 #include "content/browser/service_worker/embedded_worker_registry.h"
27 #include "content/browser/service_worker/service_worker_context_core.h"
28 #include "content/browser/service_worker/service_worker_context_wrapper.h"
29 #include "content/browser/service_worker/service_worker_metrics.h"
30 #include "content/browser/service_worker/service_worker_registration.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/common/service_worker/service_worker_utils.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/content_browser_client.h"
37 #include "content/public/browser/page_navigator.h"
38 #include "content/public/browser/render_frame_host.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "content/public/browser/web_contents.h"
41 #include "content/public/browser/web_contents_observer.h"
42 #include "content/public/common/background_sync.mojom.h"
43 #include "content/public/common/child_process_host.h"
44 #include "content/public/common/content_client.h"
45 #include "content/public/common/content_switches.h"
46 #include "content/public/common/result_codes.h"
47 #include "content/public/common/service_registry.h"
48 #include "mojo/common/common_type_converters.h"
49 #include "mojo/common/url_type_converters.h"
50 #include "net/http/http_response_headers.h"
51 #include "net/http/http_response_info.h"
53 namespace content {
55 using StatusCallback = ServiceWorkerVersion::StatusCallback;
56 using ServiceWorkerClients = std::vector<ServiceWorkerClientInfo>;
57 using GetClientsCallback =
58 base::Callback<void(scoped_ptr<ServiceWorkerClients>)>;
60 namespace {
62 // Delay between the timeout timer firing.
63 const int kTimeoutTimerDelaySeconds = 30;
65 // Time to wait until stopping an idle worker.
66 const int kIdleWorkerTimeoutSeconds = 30;
68 // Time until a stopping worker is considered stalled.
69 const int kStopWorkerTimeoutSeconds = 30;
71 // Default delay for scheduled update.
72 const int kUpdateDelaySeconds = 1;
74 // Timeout for waiting for a response to a ping.
75 const int kPingTimeoutSeconds = 30;
77 // If the SW was destructed while starting up, how many seconds it
78 // had to start up for this to be considered a timeout occurrence.
79 const int kDestructedStartingWorkerTimeoutThresholdSeconds = 5;
81 const char kClaimClientsStateErrorMesage[] =
82 "Only the active worker can claim clients.";
84 const char kClaimClientsShutdownErrorMesage[] =
85 "Failed to claim clients due to Service Worker system shutdown.";
87 void RunSoon(const base::Closure& callback) {
88 if (!callback.is_null())
89 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, callback);
92 template <typename CallbackArray, typename Arg>
93 void RunCallbacks(ServiceWorkerVersion* version,
94 CallbackArray* callbacks_ptr,
95 const Arg& arg) {
96 CallbackArray callbacks;
97 callbacks.swap(*callbacks_ptr);
98 scoped_refptr<ServiceWorkerVersion> protect(version);
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(
382 blink::WebPageVisibilityStateHidden,
383 false, // is_focused
384 host->document_url(), REQUEST_CONTEXT_FRAME_TYPE_NONE, host_client_type);
385 client_info.client_uuid = host->client_uuid();
386 clients->push_back(client_info);
389 bool IsInstalled(ServiceWorkerVersion::Status status) {
390 switch (status) {
391 case ServiceWorkerVersion::NEW:
392 case ServiceWorkerVersion::INSTALLING:
393 case ServiceWorkerVersion::REDUNDANT:
394 return false;
395 case ServiceWorkerVersion::INSTALLED:
396 case ServiceWorkerVersion::ACTIVATING:
397 case ServiceWorkerVersion::ACTIVATED:
398 return true;
400 NOTREACHED() << "Unexpected status: " << status;
401 return false;
404 } // namespace
406 const int ServiceWorkerVersion::kStartWorkerTimeoutMinutes = 5;
407 const int ServiceWorkerVersion::kRequestTimeoutMinutes = 5;
409 class ServiceWorkerVersion::Metrics {
410 public:
411 using EventType = ServiceWorkerMetrics::EventType;
412 explicit Metrics(ServiceWorkerVersion* owner) : owner_(owner) {}
413 ~Metrics() {
414 for (const auto& ev : event_stats_) {
415 ServiceWorkerMetrics::RecordEventHandledRatio(owner_->scope(), ev.first,
416 ev.second.handled_events,
417 ev.second.fired_events);
421 void RecordEventHandledStatus(EventType event, bool handled) {
422 event_stats_[event].fired_events++;
423 if (handled)
424 event_stats_[event].handled_events++;
427 void NotifyStopping() {
428 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STOPPING;
431 void NotifyStopped() {
432 switch (stop_status_) {
433 case ServiceWorkerMetrics::STOP_STATUS_STOPPED:
434 case ServiceWorkerMetrics::STOP_STATUS_STALLED_THEN_STOPPED:
435 return;
436 case ServiceWorkerMetrics::STOP_STATUS_STOPPING:
437 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STOPPED;
438 break;
439 case ServiceWorkerMetrics::STOP_STATUS_STALLED:
440 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STALLED_THEN_STOPPED;
441 break;
442 case ServiceWorkerMetrics::NUM_STOP_STATUS_TYPES:
443 NOTREACHED();
444 return;
446 if (IsInstalled(owner_->status()))
447 ServiceWorkerMetrics::RecordStopWorkerStatus(stop_status_);
450 void NotifyStalledInStopping() {
451 if (stop_status_ != ServiceWorkerMetrics::STOP_STATUS_STOPPING)
452 return;
453 stop_status_ = ServiceWorkerMetrics::STOP_STATUS_STALLED;
454 if (IsInstalled(owner_->status()))
455 ServiceWorkerMetrics::RecordStopWorkerStatus(stop_status_);
458 private:
459 struct EventStat {
460 size_t fired_events = 0;
461 size_t handled_events = 0;
464 ServiceWorkerVersion* owner_;
465 std::map<EventType, EventStat> event_stats_;
466 ServiceWorkerMetrics::StopWorkerStatus stop_status_ =
467 ServiceWorkerMetrics::STOP_STATUS_STOPPING;
469 DISALLOW_COPY_AND_ASSIGN(Metrics);
472 // A controller for periodically sending a ping to the worker to see
473 // if the worker is not stalling.
474 class ServiceWorkerVersion::PingController {
475 public:
476 explicit PingController(ServiceWorkerVersion* version) : version_(version) {}
477 ~PingController() {}
479 void Activate() { ping_state_ = PINGING; }
481 void Deactivate() {
482 ClearTick(&ping_time_);
483 ping_state_ = NOT_PINGING;
486 void OnPongReceived() { ClearTick(&ping_time_); }
488 bool IsTimedOut() { return ping_state_ == PING_TIMED_OUT; }
490 // Checks ping status. This is supposed to be called periodically.
491 // This may call:
492 // - OnPingTimeout() if the worker hasn't reponded within a certain period.
493 // - PingWorker() if we're running ping timer and can send next ping.
494 void CheckPingStatus() {
495 if (GetTickDuration(ping_time_) >
496 base::TimeDelta::FromSeconds(kPingTimeoutSeconds)) {
497 ping_state_ = PING_TIMED_OUT;
498 version_->OnPingTimeout();
499 return;
502 // Check if we want to send a next ping.
503 if (ping_state_ != PINGING || !ping_time_.is_null())
504 return;
506 if (version_->PingWorker() != SERVICE_WORKER_OK) {
507 // TODO(falken): Maybe try resending Ping a few times first?
508 ping_state_ = PING_TIMED_OUT;
509 version_->OnPingTimeout();
510 return;
512 RestartTick(&ping_time_);
515 void SimulateTimeoutForTesting() {
516 version_->PingWorker();
517 ping_state_ = PING_TIMED_OUT;
518 version_->OnPingTimeout();
521 private:
522 enum PingState { NOT_PINGING, PINGING, PING_TIMED_OUT };
523 ServiceWorkerVersion* version_; // Not owned.
524 base::TimeTicks ping_time_;
525 PingState ping_state_ = NOT_PINGING;
526 DISALLOW_COPY_AND_ASSIGN(PingController);
529 ServiceWorkerVersion::ServiceWorkerVersion(
530 ServiceWorkerRegistration* registration,
531 const GURL& script_url,
532 int64 version_id,
533 base::WeakPtr<ServiceWorkerContextCore> context)
534 : version_id_(version_id),
535 registration_id_(registration->id()),
536 script_url_(script_url),
537 scope_(registration->pattern()),
538 context_(context),
539 script_cache_map_(this, context),
540 ping_controller_(new PingController(this)),
541 weak_factory_(this) {
542 DCHECK(context_);
543 DCHECK(registration);
544 context_->AddLiveVersion(this);
545 embedded_worker_ = context_->embedded_worker_registry()->CreateWorker();
546 embedded_worker_->AddListener(this);
549 ServiceWorkerVersion::~ServiceWorkerVersion() {
550 // The user may have closed the tab waiting for SW to start up.
551 if (GetTickDuration(start_time_) >
552 base::TimeDelta::FromSeconds(
553 kDestructedStartingWorkerTimeoutThresholdSeconds)) {
554 DCHECK(timeout_timer_.IsRunning());
555 DCHECK(!embedded_worker_->devtools_attached());
556 RecordStartWorkerResult(SERVICE_WORKER_ERROR_TIMEOUT);
559 // Same with stopping.
560 if (GetTickDuration(stop_time_) >
561 base::TimeDelta::FromSeconds(kStopWorkerTimeoutSeconds)) {
562 metrics_->NotifyStalledInStopping();
565 if (context_)
566 context_->RemoveLiveVersion(version_id_);
568 if (running_status() == STARTING || running_status() == RUNNING)
569 embedded_worker_->Stop();
570 embedded_worker_->RemoveListener(this);
573 void ServiceWorkerVersion::SetStatus(Status status) {
574 if (status_ == status)
575 return;
577 status_ = status;
579 if (skip_waiting_ && status_ == ACTIVATED) {
580 for (int request_id : pending_skip_waiting_requests_)
581 DidSkipWaiting(request_id);
582 pending_skip_waiting_requests_.clear();
585 std::vector<base::Closure> callbacks;
586 callbacks.swap(status_change_callbacks_);
587 for (const auto& callback : callbacks)
588 callback.Run();
590 FOR_EACH_OBSERVER(Listener, listeners_, OnVersionStateChanged(this));
593 void ServiceWorkerVersion::RegisterStatusChangeCallback(
594 const base::Closure& callback) {
595 status_change_callbacks_.push_back(callback);
598 ServiceWorkerVersionInfo ServiceWorkerVersion::GetInfo() {
599 DCHECK_CURRENTLY_ON(BrowserThread::IO);
600 ServiceWorkerVersionInfo info(
601 running_status(), status(), script_url(), registration_id(), version_id(),
602 embedded_worker()->process_id(), embedded_worker()->thread_id(),
603 embedded_worker()->worker_devtools_agent_route_id());
604 for (const auto& controllee : controllee_map_) {
605 const ServiceWorkerProviderHost* host = controllee.second;
606 info.clients.insert(std::make_pair(
607 host->client_uuid(),
608 ServiceWorkerVersionInfo::ClientInfo(
609 host->process_id(), host->route_id(), host->provider_type())));
611 if (!main_script_http_info_)
612 return info;
613 info.script_response_time = main_script_http_info_->response_time;
614 if (main_script_http_info_->headers)
615 main_script_http_info_->headers->GetLastModifiedValue(
616 &info.script_last_modified);
617 return info;
620 void ServiceWorkerVersion::StartWorker(const StatusCallback& callback) {
621 if (!context_) {
622 RecordStartWorkerResult(SERVICE_WORKER_ERROR_ABORT);
623 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_ABORT));
624 return;
626 if (is_redundant()) {
627 RecordStartWorkerResult(SERVICE_WORKER_ERROR_REDUNDANT);
628 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_REDUNDANT));
629 return;
631 prestart_status_ = status_;
633 // Ensure the live registration during starting worker so that the worker can
634 // get associated with it in SWDispatcherHost::OnSetHostedVersionId().
635 context_->storage()->FindRegistrationForId(
636 registration_id_,
637 scope_.GetOrigin(),
638 base::Bind(&ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker,
639 weak_factory_.GetWeakPtr(),
640 callback));
643 void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) {
644 if (running_status() == STOPPED) {
645 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
646 return;
648 if (stop_callbacks_.empty()) {
649 ServiceWorkerStatusCode status = embedded_worker_->Stop();
650 if (status != SERVICE_WORKER_OK) {
651 RunSoon(base::Bind(callback, status));
652 return;
655 stop_callbacks_.push_back(callback);
658 void ServiceWorkerVersion::ScheduleUpdate() {
659 if (!context_)
660 return;
661 if (update_timer_.IsRunning()) {
662 update_timer_.Reset();
663 return;
665 if (is_update_scheduled_)
666 return;
667 is_update_scheduled_ = true;
669 // Protect |this| until the timer fires, since we may be stopping
670 // and soon no one might hold a reference to us.
671 context_->ProtectVersion(make_scoped_refptr(this));
672 update_timer_.Start(FROM_HERE,
673 base::TimeDelta::FromSeconds(kUpdateDelaySeconds),
674 base::Bind(&ServiceWorkerVersion::StartUpdate,
675 weak_factory_.GetWeakPtr()));
678 void ServiceWorkerVersion::StartUpdate() {
679 if (!context_)
680 return;
681 context_->storage()->FindRegistrationForId(
682 registration_id_, scope_.GetOrigin(),
683 base::Bind(&ServiceWorkerVersion::FoundRegistrationForUpdate,
684 weak_factory_.GetWeakPtr()));
687 void ServiceWorkerVersion::DeferScheduledUpdate() {
688 if (update_timer_.IsRunning())
689 update_timer_.Reset();
692 void ServiceWorkerVersion::DispatchMessageEvent(
693 const base::string16& message,
694 const std::vector<TransferredMessagePort>& sent_message_ports,
695 const StatusCallback& callback) {
696 for (const TransferredMessagePort& port : sent_message_ports) {
697 MessagePortService::GetInstance()->HoldMessages(port.id);
700 DispatchMessageEventInternal(message, sent_message_ports, callback);
703 void ServiceWorkerVersion::DispatchMessageEventInternal(
704 const base::string16& message,
705 const std::vector<TransferredMessagePort>& sent_message_ports,
706 const StatusCallback& callback) {
707 if (running_status() != RUNNING) {
708 // Schedule calling this method after starting the worker.
709 StartWorker(base::Bind(
710 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
711 base::Bind(&RunErrorMessageCallback, sent_message_ports, callback),
712 base::Bind(&self::DispatchMessageEventInternal,
713 weak_factory_.GetWeakPtr(), message, sent_message_ports,
714 callback)));
715 return;
718 // TODO(kinuko): Cleanup this (and corresponding unit test) when message
719 // event becomes extendable, round-trip event. (crbug.com/498596)
720 RestartTick(&idle_time_);
722 MessagePortMessageFilter* filter =
723 embedded_worker_->message_port_message_filter();
724 std::vector<int> new_routing_ids;
725 filter->UpdateMessagePortsWithNewRoutes(sent_message_ports, &new_routing_ids);
726 ServiceWorkerStatusCode status =
727 embedded_worker_->SendMessage(ServiceWorkerMsg_MessageToWorker(
728 message, sent_message_ports, new_routing_ids));
729 RunSoon(base::Bind(callback, status));
732 void ServiceWorkerVersion::DispatchInstallEvent(
733 const StatusCallback& callback) {
734 DCHECK_EQ(INSTALLING, status()) << status();
736 if (running_status() != RUNNING) {
737 // Schedule calling this method after starting the worker.
738 StartWorker(
739 base::Bind(&RunTaskAfterStartWorker,
740 weak_factory_.GetWeakPtr(),
741 callback,
742 base::Bind(&self::DispatchInstallEventAfterStartWorker,
743 weak_factory_.GetWeakPtr(),
744 callback)));
745 } else {
746 DispatchInstallEventAfterStartWorker(callback);
750 void ServiceWorkerVersion::DispatchActivateEvent(
751 const StatusCallback& callback) {
752 DCHECK_EQ(ACTIVATING, status()) << status();
754 if (running_status() != RUNNING) {
755 // Schedule calling this method after starting the worker.
756 StartWorker(
757 base::Bind(&RunTaskAfterStartWorker,
758 weak_factory_.GetWeakPtr(),
759 callback,
760 base::Bind(&self::DispatchActivateEventAfterStartWorker,
761 weak_factory_.GetWeakPtr(),
762 callback)));
763 } else {
764 DispatchActivateEventAfterStartWorker(callback);
768 void ServiceWorkerVersion::DispatchFetchEvent(
769 const ServiceWorkerFetchRequest& request,
770 const base::Closure& prepare_callback,
771 const FetchCallback& fetch_callback) {
772 DCHECK_EQ(ACTIVATED, status()) << status();
774 if (running_status() != RUNNING) {
775 // Schedule calling this method after starting the worker.
776 StartWorker(base::Bind(&RunTaskAfterStartWorker,
777 weak_factory_.GetWeakPtr(),
778 base::Bind(&RunErrorFetchCallback, fetch_callback),
779 base::Bind(&self::DispatchFetchEvent,
780 weak_factory_.GetWeakPtr(),
781 request,
782 prepare_callback,
783 fetch_callback)));
784 return;
787 prepare_callback.Run();
789 int request_id = AddRequest(fetch_callback, &fetch_requests_, REQUEST_FETCH);
790 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
791 ServiceWorkerMsg_FetchEvent(request_id, request));
792 if (status != SERVICE_WORKER_OK) {
793 fetch_requests_.Remove(request_id);
794 RunSoon(base::Bind(&RunErrorFetchCallback,
795 fetch_callback,
796 SERVICE_WORKER_ERROR_FAILED));
800 void ServiceWorkerVersion::DispatchSyncEvent(SyncRegistrationPtr registration,
801 const StatusCallback& callback) {
802 DCHECK_EQ(ACTIVATED, status()) << status();
803 if (running_status() != RUNNING) {
804 // Schedule calling this method after starting the worker.
805 StartWorker(base::Bind(
806 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(), callback,
807 base::Bind(&self::DispatchSyncEvent, weak_factory_.GetWeakPtr(),
808 base::Passed(registration.Pass()), callback)));
809 return;
812 int request_id = AddRequest(callback, &sync_requests_, REQUEST_SYNC);
813 if (!background_sync_dispatcher_) {
814 embedded_worker_->GetServiceRegistry()->ConnectToRemoteService(
815 mojo::GetProxy(&background_sync_dispatcher_));
816 background_sync_dispatcher_.set_connection_error_handler(base::Bind(
817 &ServiceWorkerVersion::OnBackgroundSyncDispatcherConnectionError,
818 weak_factory_.GetWeakPtr()));
821 background_sync_dispatcher_->Sync(
822 registration.Pass(), base::Bind(&self::OnSyncEventFinished,
823 weak_factory_.GetWeakPtr(), request_id));
826 void ServiceWorkerVersion::DispatchNotificationClickEvent(
827 const StatusCallback& callback,
828 int64_t persistent_notification_id,
829 const PlatformNotificationData& notification_data,
830 int action_index) {
831 DCHECK_EQ(ACTIVATED, status()) << status();
832 if (running_status() != RUNNING) {
833 // Schedule calling this method after starting the worker.
834 StartWorker(base::Bind(
835 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(), callback,
836 base::Bind(&self::DispatchNotificationClickEvent,
837 weak_factory_.GetWeakPtr(), callback,
838 persistent_notification_id, notification_data,
839 action_index)));
840 return;
843 int request_id = AddRequest(callback, &notification_click_requests_,
844 REQUEST_NOTIFICATION_CLICK);
845 ServiceWorkerStatusCode status =
846 embedded_worker_->SendMessage(ServiceWorkerMsg_NotificationClickEvent(
847 request_id, persistent_notification_id, notification_data,
848 action_index));
849 if (status != SERVICE_WORKER_OK) {
850 notification_click_requests_.Remove(request_id);
851 RunSoon(base::Bind(callback, status));
855 void ServiceWorkerVersion::DispatchPushEvent(const StatusCallback& callback,
856 const std::string& data) {
857 DCHECK_EQ(ACTIVATED, status()) << status();
858 if (running_status() != RUNNING) {
859 // Schedule calling this method after starting the worker.
860 StartWorker(base::Bind(&RunTaskAfterStartWorker,
861 weak_factory_.GetWeakPtr(), callback,
862 base::Bind(&self::DispatchPushEvent,
863 weak_factory_.GetWeakPtr(),
864 callback, data)));
865 return;
868 int request_id = AddRequest(callback, &push_requests_, REQUEST_PUSH);
869 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
870 ServiceWorkerMsg_PushEvent(request_id, data));
871 if (status != SERVICE_WORKER_OK) {
872 push_requests_.Remove(request_id);
873 RunSoon(base::Bind(callback, status));
877 void ServiceWorkerVersion::DispatchGeofencingEvent(
878 const StatusCallback& callback,
879 blink::WebGeofencingEventType event_type,
880 const std::string& region_id,
881 const blink::WebCircularGeofencingRegion& region) {
882 DCHECK_EQ(ACTIVATED, status()) << status();
884 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
885 switches::kEnableExperimentalWebPlatformFeatures)) {
886 callback.Run(SERVICE_WORKER_ERROR_ABORT);
887 return;
890 if (running_status() != RUNNING) {
891 // Schedule calling this method after starting the worker.
892 StartWorker(base::Bind(&RunTaskAfterStartWorker,
893 weak_factory_.GetWeakPtr(),
894 callback,
895 base::Bind(&self::DispatchGeofencingEvent,
896 weak_factory_.GetWeakPtr(),
897 callback,
898 event_type,
899 region_id,
900 region)));
901 return;
904 int request_id =
905 AddRequest(callback, &geofencing_requests_, REQUEST_GEOFENCING);
906 ServiceWorkerStatusCode status =
907 embedded_worker_->SendMessage(ServiceWorkerMsg_GeofencingEvent(
908 request_id, event_type, region_id, region));
909 if (status != SERVICE_WORKER_OK) {
910 geofencing_requests_.Remove(request_id);
911 RunSoon(base::Bind(callback, status));
915 void ServiceWorkerVersion::DispatchServicePortConnectEvent(
916 const ServicePortConnectCallback& callback,
917 const GURL& target_url,
918 const GURL& origin,
919 int port_id) {
920 DCHECK_EQ(ACTIVATED, status()) << status();
922 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
923 switches::kEnableExperimentalWebPlatformFeatures)) {
924 callback.Run(SERVICE_WORKER_ERROR_ABORT, false, base::string16(),
925 base::string16());
926 return;
929 if (running_status() != RUNNING) {
930 // Schedule calling this method after starting the worker.
931 StartWorker(
932 base::Bind(&RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
933 base::Bind(&RunErrorServicePortConnectCallback, callback),
934 base::Bind(&self::DispatchServicePortConnectEvent,
935 weak_factory_.GetWeakPtr(), callback, target_url,
936 origin, port_id)));
937 return;
940 int request_id = AddRequest(callback, &service_port_connect_requests_,
941 REQUEST_SERVICE_PORT_CONNECT);
942 if (!service_port_dispatcher_) {
943 embedded_worker_->GetServiceRegistry()->ConnectToRemoteService(
944 mojo::GetProxy(&service_port_dispatcher_));
945 service_port_dispatcher_.set_connection_error_handler(base::Bind(
946 &ServiceWorkerVersion::OnServicePortDispatcherConnectionError,
947 weak_factory_.GetWeakPtr()));
949 service_port_dispatcher_->Connect(
950 mojo::String::From(target_url), mojo::String::From(origin), port_id,
951 base::Bind(&ServiceWorkerVersion::OnServicePortConnectEventFinished,
952 weak_factory_.GetWeakPtr(), request_id));
955 void ServiceWorkerVersion::DispatchCrossOriginMessageEvent(
956 const NavigatorConnectClient& client,
957 const base::string16& message,
958 const std::vector<TransferredMessagePort>& sent_message_ports,
959 const StatusCallback& callback) {
960 // Unlike in the case of DispatchMessageEvent, here the caller is assumed to
961 // have already put all the sent message ports on hold. So no need to do that
962 // here again.
964 if (running_status() != RUNNING) {
965 // Schedule calling this method after starting the worker.
966 StartWorker(base::Bind(
967 &RunTaskAfterStartWorker, weak_factory_.GetWeakPtr(),
968 base::Bind(&RunErrorMessageCallback, sent_message_ports, callback),
969 base::Bind(&self::DispatchCrossOriginMessageEvent,
970 weak_factory_.GetWeakPtr(), client, message,
971 sent_message_ports, callback)));
972 return;
975 MessagePortMessageFilter* filter =
976 embedded_worker_->message_port_message_filter();
977 std::vector<int> new_routing_ids;
978 filter->UpdateMessagePortsWithNewRoutes(sent_message_ports, &new_routing_ids);
979 ServiceWorkerStatusCode status =
980 embedded_worker_->SendMessage(ServiceWorkerMsg_CrossOriginMessageToWorker(
981 client, message, sent_message_ports, new_routing_ids));
982 RunSoon(base::Bind(callback, status));
985 void ServiceWorkerVersion::AddControllee(
986 ServiceWorkerProviderHost* provider_host) {
987 const std::string& uuid = provider_host->client_uuid();
988 CHECK(!provider_host->client_uuid().empty());
989 DCHECK(!ContainsKey(controllee_map_, uuid));
990 controllee_map_[uuid] = provider_host;
991 // Keep the worker alive a bit longer right after a new controllee is added.
992 RestartTick(&idle_time_);
993 FOR_EACH_OBSERVER(Listener, listeners_,
994 OnControlleeAdded(this, provider_host));
997 void ServiceWorkerVersion::RemoveControllee(
998 ServiceWorkerProviderHost* provider_host) {
999 const std::string& uuid = provider_host->client_uuid();
1000 DCHECK(ContainsKey(controllee_map_, uuid));
1001 controllee_map_.erase(uuid);
1002 FOR_EACH_OBSERVER(Listener, listeners_,
1003 OnControlleeRemoved(this, provider_host));
1004 if (HasControllee())
1005 return;
1006 FOR_EACH_OBSERVER(Listener, listeners_, OnNoControllees(this));
1009 bool ServiceWorkerVersion::HasWindowClients() {
1010 return !GetWindowClientsInternal(false /* include_uncontrolled */).empty();
1013 void ServiceWorkerVersion::AddStreamingURLRequestJob(
1014 const ServiceWorkerURLRequestJob* request_job) {
1015 DCHECK(streaming_url_request_jobs_.find(request_job) ==
1016 streaming_url_request_jobs_.end());
1017 streaming_url_request_jobs_.insert(request_job);
1020 void ServiceWorkerVersion::RemoveStreamingURLRequestJob(
1021 const ServiceWorkerURLRequestJob* request_job) {
1022 streaming_url_request_jobs_.erase(request_job);
1023 if (is_redundant())
1024 StopWorkerIfIdle();
1027 void ServiceWorkerVersion::AddListener(Listener* listener) {
1028 listeners_.AddObserver(listener);
1031 void ServiceWorkerVersion::RemoveListener(Listener* listener) {
1032 listeners_.RemoveObserver(listener);
1035 void ServiceWorkerVersion::ReportError(ServiceWorkerStatusCode status,
1036 const std::string& status_message) {
1037 if (status_message.empty()) {
1038 OnReportException(base::UTF8ToUTF16(ServiceWorkerStatusToString(status)),
1039 -1, -1, GURL());
1040 } else {
1041 OnReportException(base::UTF8ToUTF16(status_message), -1, -1, GURL());
1045 void ServiceWorkerVersion::SetStartWorkerStatusCode(
1046 ServiceWorkerStatusCode status) {
1047 start_worker_status_ = status;
1050 void ServiceWorkerVersion::Doom() {
1051 DCHECK(!HasControllee());
1052 SetStatus(REDUNDANT);
1053 if (running_status() == STARTING || running_status() == RUNNING)
1054 embedded_worker_->Stop();
1055 if (!context_)
1056 return;
1057 std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
1058 script_cache_map_.GetResources(&resources);
1059 context_->storage()->PurgeResources(resources);
1062 void ServiceWorkerVersion::SetDevToolsAttached(bool attached) {
1063 embedded_worker()->set_devtools_attached(attached);
1064 if (attached) {
1065 // TODO(falken): Canceling the timeouts when debugging could cause
1066 // heisenbugs; we should instead run them as normal show an educational
1067 // message in DevTools when they occur. crbug.com/470419
1069 // Don't record the startup time metric once DevTools is attached.
1070 ClearTick(&start_time_);
1071 skip_recording_startup_time_ = true;
1073 // Cancel request timeouts.
1074 SetAllRequestTimes(base::TimeTicks());
1075 return;
1077 if (!start_callbacks_.empty()) {
1078 // Reactivate the timer for start timeout.
1079 DCHECK(timeout_timer_.IsRunning());
1080 DCHECK(running_status() == STARTING || running_status() == STOPPING)
1081 << running_status();
1082 RestartTick(&start_time_);
1085 // Reactivate request timeouts.
1086 SetAllRequestTimes(base::TimeTicks::Now());
1089 void ServiceWorkerVersion::SetMainScriptHttpResponseInfo(
1090 const net::HttpResponseInfo& http_info) {
1091 main_script_http_info_.reset(new net::HttpResponseInfo(http_info));
1092 FOR_EACH_OBSERVER(Listener, listeners_,
1093 OnMainScriptHttpResponseInfoSet(this));
1096 void ServiceWorkerVersion::SimulatePingTimeoutForTesting() {
1097 ping_controller_->SimulateTimeoutForTesting();
1100 const net::HttpResponseInfo*
1101 ServiceWorkerVersion::GetMainScriptHttpResponseInfo() {
1102 return main_script_http_info_.get();
1105 ServiceWorkerVersion::RequestInfo::RequestInfo(int id,
1106 RequestType type,
1107 const base::TimeTicks& now)
1108 : id(id), type(type), time(now) {
1111 ServiceWorkerVersion::RequestInfo::~RequestInfo() {
1114 template <typename CallbackType>
1115 ServiceWorkerVersion::PendingRequest<CallbackType>::PendingRequest(
1116 const CallbackType& callback,
1117 const base::TimeTicks& time)
1118 : callback(callback), start_time(time) {
1121 template <typename CallbackType>
1122 ServiceWorkerVersion::PendingRequest<CallbackType>::~PendingRequest() {
1125 void ServiceWorkerVersion::OnScriptLoaded() {
1126 DCHECK_EQ(STARTING, running_status());
1127 // Activate ping/pong now that JavaScript execution will start.
1128 ping_controller_->Activate();
1131 void ServiceWorkerVersion::OnStarting() {
1132 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1135 void ServiceWorkerVersion::OnStarted() {
1136 DCHECK_EQ(RUNNING, running_status());
1137 RestartTick(&idle_time_);
1139 // Fire all start callbacks.
1140 scoped_refptr<ServiceWorkerVersion> protect(this);
1141 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_OK);
1142 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1145 void ServiceWorkerVersion::OnStopping() {
1146 metrics_->NotifyStopping();
1147 RestartTick(&stop_time_);
1148 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
1151 void ServiceWorkerVersion::OnStopped(
1152 EmbeddedWorkerInstance::Status old_status) {
1153 metrics_->NotifyStopped();
1154 if (!stop_time_.is_null())
1155 ServiceWorkerMetrics::RecordStopWorkerTime(GetTickDuration(stop_time_));
1157 OnStoppedInternal(old_status);
1160 void ServiceWorkerVersion::OnDetached(
1161 EmbeddedWorkerInstance::Status old_status) {
1162 OnStoppedInternal(old_status);
1165 void ServiceWorkerVersion::OnReportException(
1166 const base::string16& error_message,
1167 int line_number,
1168 int column_number,
1169 const GURL& source_url) {
1170 FOR_EACH_OBSERVER(
1171 Listener,
1172 listeners_,
1173 OnErrorReported(
1174 this, error_message, line_number, column_number, source_url));
1177 void ServiceWorkerVersion::OnReportConsoleMessage(int source_identifier,
1178 int message_level,
1179 const base::string16& message,
1180 int line_number,
1181 const GURL& source_url) {
1182 FOR_EACH_OBSERVER(Listener,
1183 listeners_,
1184 OnReportConsoleMessage(this,
1185 source_identifier,
1186 message_level,
1187 message,
1188 line_number,
1189 source_url));
1192 bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message& message) {
1193 bool handled = true;
1194 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerVersion, message)
1195 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetClients,
1196 OnGetClients)
1197 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ActivateEventFinished,
1198 OnActivateEventFinished)
1199 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_InstallEventFinished,
1200 OnInstallEventFinished)
1201 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FetchEventFinished,
1202 OnFetchEventFinished)
1203 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_NotificationClickEventFinished,
1204 OnNotificationClickEventFinished)
1205 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PushEventFinished,
1206 OnPushEventFinished)
1207 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GeofencingEventFinished,
1208 OnGeofencingEventFinished)
1209 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_OpenWindow,
1210 OnOpenWindow)
1211 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SetCachedMetadata,
1212 OnSetCachedMetadata)
1213 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClearCachedMetadata,
1214 OnClearCachedMetadata)
1215 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToClient,
1216 OnPostMessageToClient)
1217 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FocusClient,
1218 OnFocusClient)
1219 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_NavigateClient, OnNavigateClient)
1220 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SkipWaiting,
1221 OnSkipWaiting)
1222 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ClaimClients,
1223 OnClaimClients)
1224 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_Pong, OnPongFromWorker)
1225 IPC_MESSAGE_UNHANDLED(handled = false)
1226 IPC_END_MESSAGE_MAP()
1227 return handled;
1230 void ServiceWorkerVersion::OnStartSentAndScriptEvaluated(
1231 ServiceWorkerStatusCode status) {
1232 if (status != SERVICE_WORKER_OK) {
1233 RunCallbacks(this, &start_callbacks_,
1234 DeduceStartWorkerFailureReason(status));
1238 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
1239 const StatusCallback& callback) {
1240 DCHECK_EQ(RUNNING, running_status())
1241 << "Worker stopped too soon after it was started.";
1243 int request_id = AddRequest(callback, &install_requests_, REQUEST_INSTALL);
1244 ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
1245 ServiceWorkerMsg_InstallEvent(request_id));
1246 if (status != SERVICE_WORKER_OK) {
1247 install_requests_.Remove(request_id);
1248 RunSoon(base::Bind(callback, status));
1252 void ServiceWorkerVersion::DispatchActivateEventAfterStartWorker(
1253 const StatusCallback& callback) {
1254 DCHECK_EQ(RUNNING, running_status())
1255 << "Worker stopped too soon after it was started.";
1257 int request_id = AddRequest(callback, &activate_requests_, REQUEST_ACTIVATE);
1258 ServiceWorkerStatusCode status =
1259 embedded_worker_->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id));
1260 if (status != SERVICE_WORKER_OK) {
1261 activate_requests_.Remove(request_id);
1262 RunSoon(base::Bind(callback, status));
1266 void ServiceWorkerVersion::OnGetClients(
1267 int request_id,
1268 const ServiceWorkerClientQueryOptions& options) {
1269 TRACE_EVENT_ASYNC_BEGIN2(
1270 "ServiceWorker", "ServiceWorkerVersion::OnGetClients", request_id,
1271 "client_type", options.client_type, "include_uncontrolled",
1272 options.include_uncontrolled);
1274 if (controllee_map_.empty() && !options.include_uncontrolled) {
1275 OnGetClientsFinished(request_id, std::vector<ServiceWorkerClientInfo>());
1276 return;
1279 // For Window clients we want to query the info on the UI thread first.
1280 if (options.client_type == blink::WebServiceWorkerClientTypeWindow ||
1281 options.client_type == blink::WebServiceWorkerClientTypeAll) {
1282 GetWindowClients(request_id, options);
1283 return;
1286 ServiceWorkerClients clients;
1287 GetNonWindowClients(request_id, options, &clients);
1288 OnGetClientsFinished(request_id, clients);
1291 void ServiceWorkerVersion::OnGetClientsFinished(
1292 int request_id,
1293 const ServiceWorkerClients& clients) {
1294 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1295 TRACE_EVENT_ASYNC_END1("ServiceWorker", "ServiceWorkerVersion::OnGetClients",
1296 request_id, "The number of clients", clients.size());
1298 if (running_status() != RUNNING)
1299 return;
1300 embedded_worker_->SendMessage(
1301 ServiceWorkerMsg_DidGetClients(request_id, clients));
1304 void ServiceWorkerVersion::OnActivateEventFinished(
1305 int request_id,
1306 blink::WebServiceWorkerEventResult result) {
1307 DCHECK(ACTIVATING == status() ||
1308 REDUNDANT == status()) << status();
1309 TRACE_EVENT0("ServiceWorker",
1310 "ServiceWorkerVersion::OnActivateEventFinished");
1312 PendingRequest<StatusCallback>* request =
1313 activate_requests_.Lookup(request_id);
1314 if (!request) {
1315 NOTREACHED() << "Got unexpected message: " << request_id;
1316 return;
1318 ServiceWorkerStatusCode rv = SERVICE_WORKER_OK;
1319 if (result == blink::WebServiceWorkerEventResultRejected)
1320 rv = SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED;
1322 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ActivateEvent.Time",
1323 base::TimeTicks::Now() - request->start_time);
1325 scoped_refptr<ServiceWorkerVersion> protect(this);
1326 request->callback.Run(rv);
1327 RemoveCallbackAndStopIfRedundant(&activate_requests_, request_id);
1330 void ServiceWorkerVersion::OnInstallEventFinished(
1331 int request_id,
1332 blink::WebServiceWorkerEventResult result) {
1333 // Status is REDUNDANT if the worker was doomed while handling the install
1334 // event, and finished handling before being terminated.
1335 DCHECK(status() == INSTALLING || status() == REDUNDANT) << status();
1336 TRACE_EVENT0("ServiceWorker",
1337 "ServiceWorkerVersion::OnInstallEventFinished");
1339 PendingRequest<StatusCallback>* request =
1340 install_requests_.Lookup(request_id);
1341 if (!request) {
1342 NOTREACHED() << "Got unexpected message: " << request_id;
1343 return;
1345 ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
1346 if (result == blink::WebServiceWorkerEventResultRejected)
1347 status = SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED;
1349 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.InstallEvent.Time",
1350 base::TimeTicks::Now() - request->start_time);
1352 scoped_refptr<ServiceWorkerVersion> protect(this);
1353 request->callback.Run(status);
1354 RemoveCallbackAndStopIfRedundant(&install_requests_, request_id);
1357 void ServiceWorkerVersion::OnFetchEventFinished(
1358 int request_id,
1359 ServiceWorkerFetchEventResult result,
1360 const ServiceWorkerResponse& response) {
1361 TRACE_EVENT1("ServiceWorker",
1362 "ServiceWorkerVersion::OnFetchEventFinished",
1363 "Request id", request_id);
1364 PendingRequest<FetchCallback>* request = fetch_requests_.Lookup(request_id);
1365 if (!request) {
1366 NOTREACHED() << "Got unexpected message: " << request_id;
1367 return;
1370 // TODO(kinuko): Record other event statuses too.
1371 const bool handled = (result == SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE);
1372 metrics_->RecordEventHandledStatus(ServiceWorkerMetrics::EVENT_TYPE_FETCH,
1373 handled);
1375 ServiceWorkerMetrics::RecordFetchEventTime(
1376 result, base::TimeTicks::Now() - request->start_time);
1378 scoped_refptr<ServiceWorkerVersion> protect(this);
1379 request->callback.Run(SERVICE_WORKER_OK, result, response);
1380 RemoveCallbackAndStopIfRedundant(&fetch_requests_, request_id);
1383 void ServiceWorkerVersion::OnSyncEventFinished(
1384 int request_id,
1385 ServiceWorkerEventStatus status) {
1386 TRACE_EVENT1("ServiceWorker",
1387 "ServiceWorkerVersion::OnSyncEventFinished",
1388 "Request id", request_id);
1389 PendingRequest<StatusCallback>* request = sync_requests_.Lookup(request_id);
1390 if (!request) {
1391 NOTREACHED() << "Got unexpected message: " << request_id;
1392 return;
1395 scoped_refptr<ServiceWorkerVersion> protect(this);
1396 request->callback.Run(mojo::ConvertTo<ServiceWorkerStatusCode>(status));
1397 RemoveCallbackAndStopIfRedundant(&sync_requests_, request_id);
1400 void ServiceWorkerVersion::OnNotificationClickEventFinished(
1401 int request_id) {
1402 TRACE_EVENT1("ServiceWorker",
1403 "ServiceWorkerVersion::OnNotificationClickEventFinished",
1404 "Request id", request_id);
1405 PendingRequest<StatusCallback>* request =
1406 notification_click_requests_.Lookup(request_id);
1407 if (!request) {
1408 NOTREACHED() << "Got unexpected message: " << request_id;
1409 return;
1412 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.NotificationClickEvent.Time",
1413 base::TimeTicks::Now() - request->start_time);
1415 scoped_refptr<ServiceWorkerVersion> protect(this);
1416 request->callback.Run(SERVICE_WORKER_OK);
1417 RemoveCallbackAndStopIfRedundant(&notification_click_requests_, request_id);
1420 void ServiceWorkerVersion::OnPushEventFinished(
1421 int request_id,
1422 blink::WebServiceWorkerEventResult result) {
1423 TRACE_EVENT1("ServiceWorker",
1424 "ServiceWorkerVersion::OnPushEventFinished",
1425 "Request id", request_id);
1426 PendingRequest<StatusCallback>* request = push_requests_.Lookup(request_id);
1427 if (!request) {
1428 NOTREACHED() << "Got unexpected message: " << request_id;
1429 return;
1431 ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
1432 if (result == blink::WebServiceWorkerEventResultRejected)
1433 status = SERVICE_WORKER_ERROR_EVENT_WAITUNTIL_REJECTED;
1435 UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.PushEvent.Time",
1436 base::TimeTicks::Now() - request->start_time);
1438 scoped_refptr<ServiceWorkerVersion> protect(this);
1439 request->callback.Run(status);
1440 RemoveCallbackAndStopIfRedundant(&push_requests_, request_id);
1443 void ServiceWorkerVersion::OnGeofencingEventFinished(int request_id) {
1444 TRACE_EVENT1("ServiceWorker",
1445 "ServiceWorkerVersion::OnGeofencingEventFinished",
1446 "Request id",
1447 request_id);
1448 PendingRequest<StatusCallback>* request =
1449 geofencing_requests_.Lookup(request_id);
1450 if (!request) {
1451 NOTREACHED() << "Got unexpected message: " << request_id;
1452 return;
1455 scoped_refptr<ServiceWorkerVersion> protect(this);
1456 request->callback.Run(SERVICE_WORKER_OK);
1457 RemoveCallbackAndStopIfRedundant(&geofencing_requests_, request_id);
1460 void ServiceWorkerVersion::OnServicePortConnectEventFinished(
1461 int request_id,
1462 ServicePortConnectResult result,
1463 const mojo::String& name,
1464 const mojo::String& data) {
1465 TRACE_EVENT1("ServiceWorker",
1466 "ServiceWorkerVersion::OnServicePortConnectEventFinished",
1467 "Request id", request_id);
1468 PendingRequest<ServicePortConnectCallback>* request =
1469 service_port_connect_requests_.Lookup(request_id);
1470 if (!request) {
1471 NOTREACHED() << "Got unexpected message: " << request_id;
1472 return;
1475 scoped_refptr<ServiceWorkerVersion> protect(this);
1476 request->callback.Run(SERVICE_WORKER_OK,
1477 result == SERVICE_PORT_CONNECT_RESULT_ACCEPT,
1478 name.To<base::string16>(), data.To<base::string16>());
1479 RemoveCallbackAndStopIfRedundant(&service_port_connect_requests_, request_id);
1482 void ServiceWorkerVersion::OnOpenWindow(int request_id, GURL url) {
1483 // Just abort if we are shutting down.
1484 if (!context_)
1485 return;
1487 if (!url.is_valid()) {
1488 DVLOG(1) << "Received unexpected invalid URL from renderer process.";
1489 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
1490 base::Bind(&KillEmbeddedWorkerProcess,
1491 embedded_worker_->process_id(),
1492 RESULT_CODE_KILLED_BAD_MESSAGE));
1493 return;
1496 // The renderer treats all URLs in the about: scheme as being about:blank.
1497 // Canonicalize about: URLs to about:blank.
1498 if (url.SchemeIs(url::kAboutScheme))
1499 url = GURL(url::kAboutBlankURL);
1501 // Reject requests for URLs that the process is not allowed to access. It's
1502 // possible to receive such requests since the renderer-side checks are
1503 // slightly different. For example, the view-source scheme will not be
1504 // filtered out by Blink.
1505 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
1506 embedded_worker_->process_id(), url)) {
1507 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowError(
1508 request_id, url.spec() + " cannot be opened."));
1509 return;
1512 BrowserThread::PostTask(
1513 BrowserThread::UI, FROM_HERE,
1514 base::Bind(&OpenWindowOnUI,
1515 url,
1516 script_url_,
1517 embedded_worker_->process_id(),
1518 make_scoped_refptr(context_->wrapper()),
1519 base::Bind(&ServiceWorkerVersion::DidOpenWindow,
1520 weak_factory_.GetWeakPtr(),
1521 request_id)));
1524 void ServiceWorkerVersion::DidOpenWindow(int request_id,
1525 int render_process_id,
1526 int render_frame_id) {
1527 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1529 if (running_status() != RUNNING)
1530 return;
1532 if (render_process_id == ChildProcessHost::kInvalidUniqueID &&
1533 render_frame_id == MSG_ROUTING_NONE) {
1534 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowError(
1535 request_id, "Something went wrong while trying to open the window."));
1536 return;
1539 for (auto it =
1540 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1541 !it->IsAtEnd(); it->Advance()) {
1542 ServiceWorkerProviderHost* provider_host = it->GetProviderHost();
1543 if (provider_host->process_id() != render_process_id ||
1544 provider_host->frame_id() != render_frame_id) {
1545 continue;
1547 provider_host->GetWindowClientInfo(base::Bind(
1548 &ServiceWorkerVersion::OnOpenWindowFinished, weak_factory_.GetWeakPtr(),
1549 request_id, provider_host->client_uuid()));
1550 return;
1553 // If here, it means that no provider_host was found, in which case, the
1554 // renderer should still be informed that the window was opened.
1555 OnOpenWindowFinished(request_id, std::string(), ServiceWorkerClientInfo());
1558 void ServiceWorkerVersion::OnOpenWindowFinished(
1559 int request_id,
1560 const std::string& client_uuid,
1561 const ServiceWorkerClientInfo& client_info) {
1562 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1564 if (running_status() != RUNNING)
1565 return;
1567 ServiceWorkerClientInfo client(client_info);
1569 // If the |client_info| is empty, it means that the opened window wasn't
1570 // controlled but the action still succeeded. The renderer process is
1571 // expecting an empty client in such case.
1572 if (!client.IsEmpty())
1573 client.client_uuid = client_uuid;
1575 embedded_worker_->SendMessage(ServiceWorkerMsg_OpenWindowResponse(
1576 request_id, client));
1579 void ServiceWorkerVersion::OnSetCachedMetadata(const GURL& url,
1580 const std::vector<char>& data) {
1581 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
1582 TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
1583 "ServiceWorkerVersion::OnSetCachedMetadata",
1584 callback_id, "URL", url.spec());
1585 script_cache_map_.WriteMetadata(
1586 url, data, base::Bind(&ServiceWorkerVersion::OnSetCachedMetadataFinished,
1587 weak_factory_.GetWeakPtr(), callback_id));
1590 void ServiceWorkerVersion::OnSetCachedMetadataFinished(int64 callback_id,
1591 int result) {
1592 TRACE_EVENT_ASYNC_END1("ServiceWorker",
1593 "ServiceWorkerVersion::OnSetCachedMetadata",
1594 callback_id, "result", result);
1595 FOR_EACH_OBSERVER(Listener, listeners_, OnCachedMetadataUpdated(this));
1598 void ServiceWorkerVersion::OnClearCachedMetadata(const GURL& url) {
1599 int64 callback_id = base::TimeTicks::Now().ToInternalValue();
1600 TRACE_EVENT_ASYNC_BEGIN1("ServiceWorker",
1601 "ServiceWorkerVersion::OnClearCachedMetadata",
1602 callback_id, "URL", url.spec());
1603 script_cache_map_.ClearMetadata(
1604 url, base::Bind(&ServiceWorkerVersion::OnClearCachedMetadataFinished,
1605 weak_factory_.GetWeakPtr(), callback_id));
1608 void ServiceWorkerVersion::OnClearCachedMetadataFinished(int64 callback_id,
1609 int result) {
1610 TRACE_EVENT_ASYNC_END1("ServiceWorker",
1611 "ServiceWorkerVersion::OnClearCachedMetadata",
1612 callback_id, "result", result);
1613 FOR_EACH_OBSERVER(Listener, listeners_, OnCachedMetadataUpdated(this));
1616 void ServiceWorkerVersion::OnPostMessageToClient(
1617 const std::string& client_uuid,
1618 const base::string16& message,
1619 const std::vector<TransferredMessagePort>& sent_message_ports) {
1620 if (!context_)
1621 return;
1622 TRACE_EVENT1("ServiceWorker",
1623 "ServiceWorkerVersion::OnPostMessageToDocument",
1624 "Client id", client_uuid);
1625 ServiceWorkerProviderHost* provider_host =
1626 context_->GetProviderHostByClientID(client_uuid);
1627 if (!provider_host) {
1628 // The client may already have been closed, just ignore.
1629 return;
1631 if (provider_host->document_url().GetOrigin() != script_url_.GetOrigin()) {
1632 // The client does not belong to the same origin as this ServiceWorker,
1633 // possibly due to timing issue or bad message.
1634 return;
1636 provider_host->PostMessage(this, message, sent_message_ports);
1639 void ServiceWorkerVersion::OnFocusClient(int request_id,
1640 const std::string& client_uuid) {
1641 if (!context_)
1642 return;
1643 TRACE_EVENT2("ServiceWorker",
1644 "ServiceWorkerVersion::OnFocusClient",
1645 "Request id", request_id,
1646 "Client id", client_uuid);
1647 ServiceWorkerProviderHost* provider_host =
1648 context_->GetProviderHostByClientID(client_uuid);
1649 if (!provider_host) {
1650 // The client may already have been closed, just ignore.
1651 return;
1653 if (provider_host->document_url().GetOrigin() != script_url_.GetOrigin()) {
1654 // The client does not belong to the same origin as this ServiceWorker,
1655 // possibly due to timing issue or bad message.
1656 return;
1658 provider_host->Focus(base::Bind(&ServiceWorkerVersion::OnFocusClientFinished,
1659 weak_factory_.GetWeakPtr(), request_id,
1660 client_uuid));
1663 void ServiceWorkerVersion::OnFocusClientFinished(
1664 int request_id,
1665 const std::string& client_uuid,
1666 const ServiceWorkerClientInfo& client) {
1667 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1669 if (running_status() != RUNNING)
1670 return;
1672 ServiceWorkerClientInfo client_info(client);
1673 client_info.client_uuid = client_uuid;
1675 embedded_worker_->SendMessage(ServiceWorkerMsg_FocusClientResponse(
1676 request_id, client_info));
1679 void ServiceWorkerVersion::OnNavigateClient(int request_id,
1680 const std::string& client_uuid,
1681 const GURL& url) {
1682 if (!context_)
1683 return;
1685 TRACE_EVENT2("ServiceWorker", "ServiceWorkerVersion::OnNavigateClient",
1686 "Request id", request_id, "Client id", client_uuid);
1688 if (!url.is_valid() || !base::IsValidGUID(client_uuid)) {
1689 DVLOG(1) << "Received unexpected invalid URL/UUID from renderer process.";
1690 BrowserThread::PostTask(
1691 BrowserThread::UI, FROM_HERE,
1692 base::Bind(&KillEmbeddedWorkerProcess, embedded_worker_->process_id(),
1693 RESULT_CODE_KILLED_BAD_MESSAGE));
1694 return;
1697 // Reject requests for URLs that the process is not allowed to access. It's
1698 // possible to receive such requests since the renderer-side checks are
1699 // slightly different. For example, the view-source scheme will not be
1700 // filtered out by Blink.
1701 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
1702 embedded_worker_->process_id(), url)) {
1703 embedded_worker_->SendMessage(
1704 ServiceWorkerMsg_NavigateClientError(request_id, url));
1705 return;
1708 ServiceWorkerProviderHost* provider_host =
1709 context_->GetProviderHostByClientID(client_uuid);
1710 if (!provider_host || provider_host->active_version() != this) {
1711 embedded_worker_->SendMessage(
1712 ServiceWorkerMsg_NavigateClientError(request_id, url));
1713 return;
1716 BrowserThread::PostTask(
1717 BrowserThread::UI, FROM_HERE,
1718 base::Bind(&NavigateClientOnUI, url, script_url_,
1719 provider_host->process_id(), provider_host->frame_id(),
1720 base::Bind(&ServiceWorkerVersion::DidNavigateClient,
1721 weak_factory_.GetWeakPtr(), request_id)));
1724 void ServiceWorkerVersion::DidNavigateClient(int request_id,
1725 int render_process_id,
1726 int render_frame_id) {
1727 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1729 if (running_status() != RUNNING)
1730 return;
1732 if (render_process_id == ChildProcessHost::kInvalidUniqueID &&
1733 render_frame_id == MSG_ROUTING_NONE) {
1734 embedded_worker_->SendMessage(
1735 ServiceWorkerMsg_NavigateClientError(request_id, GURL()));
1736 return;
1739 for (auto it =
1740 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1741 !it->IsAtEnd(); it->Advance()) {
1742 ServiceWorkerProviderHost* provider_host = it->GetProviderHost();
1743 if (provider_host->process_id() != render_process_id ||
1744 provider_host->frame_id() != render_frame_id) {
1745 continue;
1747 provider_host->GetWindowClientInfo(base::Bind(
1748 &ServiceWorkerVersion::OnNavigateClientFinished,
1749 weak_factory_.GetWeakPtr(), request_id, provider_host->client_uuid()));
1750 return;
1753 OnNavigateClientFinished(request_id, std::string(),
1754 ServiceWorkerClientInfo());
1757 void ServiceWorkerVersion::OnNavigateClientFinished(
1758 int request_id,
1759 const std::string& client_uuid,
1760 const ServiceWorkerClientInfo& client_info) {
1761 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1763 if (running_status() != RUNNING)
1764 return;
1766 ServiceWorkerClientInfo client(client_info);
1768 // If the |client_info| is empty, it means that the navigated client wasn't
1769 // controlled but the action still succeeded. The renderer process is
1770 // expecting an empty client in such case.
1771 if (!client.IsEmpty())
1772 client.client_uuid = client_uuid;
1774 embedded_worker_->SendMessage(
1775 ServiceWorkerMsg_NavigateClientResponse(request_id, client));
1778 void ServiceWorkerVersion::OnSkipWaiting(int request_id) {
1779 skip_waiting_ = true;
1780 if (status_ != INSTALLED)
1781 return DidSkipWaiting(request_id);
1783 if (!context_)
1784 return;
1785 ServiceWorkerRegistration* registration =
1786 context_->GetLiveRegistration(registration_id_);
1787 if (!registration)
1788 return;
1789 pending_skip_waiting_requests_.push_back(request_id);
1790 if (pending_skip_waiting_requests_.size() == 1)
1791 registration->ActivateWaitingVersionWhenReady();
1794 void ServiceWorkerVersion::DidSkipWaiting(int request_id) {
1795 if (running_status() == STARTING || running_status() == RUNNING)
1796 embedded_worker_->SendMessage(ServiceWorkerMsg_DidSkipWaiting(request_id));
1799 void ServiceWorkerVersion::OnClaimClients(int request_id) {
1800 if (status_ != ACTIVATING && status_ != ACTIVATED) {
1801 embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
1802 request_id, blink::WebServiceWorkerError::ErrorTypeState,
1803 base::ASCIIToUTF16(kClaimClientsStateErrorMesage)));
1804 return;
1806 if (context_) {
1807 if (ServiceWorkerRegistration* registration =
1808 context_->GetLiveRegistration(registration_id_)) {
1809 registration->ClaimClients();
1810 embedded_worker_->SendMessage(
1811 ServiceWorkerMsg_DidClaimClients(request_id));
1812 return;
1816 embedded_worker_->SendMessage(ServiceWorkerMsg_ClaimClientsError(
1817 request_id, blink::WebServiceWorkerError::ErrorTypeAbort,
1818 base::ASCIIToUTF16(kClaimClientsShutdownErrorMesage)));
1821 void ServiceWorkerVersion::OnPongFromWorker() {
1822 ping_controller_->OnPongReceived();
1825 void ServiceWorkerVersion::DidEnsureLiveRegistrationForStartWorker(
1826 const StatusCallback& callback,
1827 ServiceWorkerStatusCode status,
1828 const scoped_refptr<ServiceWorkerRegistration>& protect) {
1829 if (status != SERVICE_WORKER_OK) {
1830 RecordStartWorkerResult(status);
1831 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED));
1832 return;
1834 if (is_redundant()) {
1835 RecordStartWorkerResult(SERVICE_WORKER_ERROR_REDUNDANT);
1836 RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_REDUNDANT));
1837 return;
1840 MarkIfStale();
1842 switch (running_status()) {
1843 case RUNNING:
1844 RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
1845 return;
1846 case STOPPING:
1847 case STOPPED:
1848 case STARTING:
1849 if (start_callbacks_.empty()) {
1850 start_callbacks_.push_back(
1851 base::Bind(&ServiceWorkerVersion::RecordStartWorkerResult,
1852 weak_factory_.GetWeakPtr()));
1854 // Keep the live registration while starting the worker.
1855 start_callbacks_.push_back(
1856 base::Bind(&RunStartWorkerCallback, callback, protect));
1857 StartWorkerInternal();
1858 return;
1862 void ServiceWorkerVersion::StartWorkerInternal() {
1863 if (!metrics_)
1864 metrics_.reset(new Metrics(this));
1866 if (!timeout_timer_.IsRunning())
1867 StartTimeoutTimer();
1868 if (running_status() == STOPPED) {
1869 embedded_worker_->Start(
1870 version_id_, scope_, script_url_,
1871 base::Bind(&ServiceWorkerVersion::OnStartSentAndScriptEvaluated,
1872 weak_factory_.GetWeakPtr()));
1876 void ServiceWorkerVersion::GetWindowClients(
1877 int request_id,
1878 const ServiceWorkerClientQueryOptions& options) {
1879 DCHECK(options.client_type == blink::WebServiceWorkerClientTypeWindow ||
1880 options.client_type == blink::WebServiceWorkerClientTypeAll);
1881 const std::vector<base::Tuple<int, int, std::string>>& clients_info =
1882 GetWindowClientsInternal(options.include_uncontrolled);
1884 if (clients_info.empty()) {
1885 DidGetWindowClients(request_id, options,
1886 make_scoped_ptr(new ServiceWorkerClients));
1887 return;
1890 BrowserThread::PostTask(
1891 BrowserThread::UI, FROM_HERE,
1892 base::Bind(&OnGetWindowClientsFromUI, clients_info, script_url_,
1893 base::Bind(&ServiceWorkerVersion::DidGetWindowClients,
1894 weak_factory_.GetWeakPtr(), request_id, options)));
1897 const std::vector<base::Tuple<int, int, std::string>>
1898 ServiceWorkerVersion::GetWindowClientsInternal(bool include_uncontrolled) {
1899 std::vector<base::Tuple<int, int, std::string>> clients_info;
1900 if (!include_uncontrolled) {
1901 for (auto& controllee : controllee_map_)
1902 AddWindowClient(controllee.second, &clients_info);
1903 } else {
1904 for (auto it =
1905 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1906 !it->IsAtEnd(); it->Advance()) {
1907 AddWindowClient(it->GetProviderHost(), &clients_info);
1910 return clients_info;
1913 void ServiceWorkerVersion::DidGetWindowClients(
1914 int request_id,
1915 const ServiceWorkerClientQueryOptions& options,
1916 scoped_ptr<ServiceWorkerClients> clients) {
1917 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1918 if (options.client_type == blink::WebServiceWorkerClientTypeAll)
1919 GetNonWindowClients(request_id, options, clients.get());
1920 OnGetClientsFinished(request_id, *clients);
1923 void ServiceWorkerVersion::GetNonWindowClients(
1924 int request_id,
1925 const ServiceWorkerClientQueryOptions& options,
1926 ServiceWorkerClients* clients) {
1927 if (!options.include_uncontrolled) {
1928 for (auto& controllee : controllee_map_) {
1929 AddNonWindowClient(controllee.second, options, clients);
1931 } else {
1932 for (auto it =
1933 context_->GetClientProviderHostIterator(script_url_.GetOrigin());
1934 !it->IsAtEnd(); it->Advance()) {
1935 AddNonWindowClient(it->GetProviderHost(), options, clients);
1940 void ServiceWorkerVersion::StartTimeoutTimer() {
1941 DCHECK(!timeout_timer_.IsRunning());
1943 if (embedded_worker_->devtools_attached()) {
1944 // Don't record the startup time metric once DevTools is attached.
1945 ClearTick(&start_time_);
1946 skip_recording_startup_time_ = true;
1947 } else {
1948 RestartTick(&start_time_);
1949 skip_recording_startup_time_ = false;
1952 ClearTick(&idle_time_);
1954 // Ping will be activated in OnScriptLoaded.
1955 ping_controller_->Deactivate();
1957 timeout_timer_.Start(FROM_HERE,
1958 base::TimeDelta::FromSeconds(kTimeoutTimerDelaySeconds),
1959 this, &ServiceWorkerVersion::OnTimeoutTimer);
1962 void ServiceWorkerVersion::StopTimeoutTimer() {
1963 timeout_timer_.Stop();
1965 // Trigger update if worker is stale.
1966 if (!stale_time_.is_null()) {
1967 ClearTick(&stale_time_);
1968 if (!update_timer_.IsRunning())
1969 ScheduleUpdate();
1973 void ServiceWorkerVersion::OnTimeoutTimer() {
1974 DCHECK(running_status() == STARTING || running_status() == RUNNING ||
1975 running_status() == STOPPING)
1976 << running_status();
1978 MarkIfStale();
1980 if (GetTickDuration(stop_time_) >
1981 base::TimeDelta::FromSeconds(kStopWorkerTimeoutSeconds)) {
1982 metrics_->NotifyStalledInStopping();
1985 // Trigger update if worker is stale and we waited long enough for it to go
1986 // idle.
1987 if (GetTickDuration(stale_time_) >
1988 base::TimeDelta::FromMinutes(kRequestTimeoutMinutes)) {
1989 ClearTick(&stale_time_);
1990 if (!update_timer_.IsRunning())
1991 ScheduleUpdate();
1994 // Starting a worker hasn't finished within a certain period.
1995 if (GetTickDuration(start_time_) >
1996 base::TimeDelta::FromMinutes(kStartWorkerTimeoutMinutes)) {
1997 DCHECK(running_status() == STARTING || running_status() == STOPPING)
1998 << running_status();
1999 scoped_refptr<ServiceWorkerVersion> protect(this);
2000 RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_ERROR_TIMEOUT);
2001 if (running_status() == STARTING)
2002 embedded_worker_->Stop();
2003 return;
2006 // Requests have not finished within a certain period.
2007 bool request_timed_out = false;
2008 while (!requests_.empty()) {
2009 RequestInfo info = requests_.front();
2010 if (GetTickDuration(info.time) <
2011 base::TimeDelta::FromMinutes(kRequestTimeoutMinutes))
2012 break;
2013 if (OnRequestTimeout(info))
2014 request_timed_out = true;
2015 requests_.pop();
2017 if (request_timed_out && running_status() != STOPPING)
2018 embedded_worker_->Stop();
2020 // For the timeouts below, there are no callbacks to timeout so there is
2021 // nothing more to do if the worker is already stopping.
2022 if (running_status() == STOPPING)
2023 return;
2025 // The worker has been idle for longer than a certain period.
2026 if (GetTickDuration(idle_time_) >
2027 base::TimeDelta::FromSeconds(kIdleWorkerTimeoutSeconds)) {
2028 StopWorkerIfIdle();
2029 return;
2032 // Check ping status.
2033 ping_controller_->CheckPingStatus();
2036 ServiceWorkerStatusCode ServiceWorkerVersion::PingWorker() {
2037 DCHECK(running_status() == STARTING || running_status() == RUNNING);
2038 return embedded_worker_->SendMessage(ServiceWorkerMsg_Ping());
2041 void ServiceWorkerVersion::OnPingTimeout() {
2042 DCHECK(running_status() == STARTING || running_status() == RUNNING);
2043 // TODO(falken): Show a message to the developer that the SW was stopped due
2044 // to timeout (crbug.com/457968). Also, change the error code to
2045 // SERVICE_WORKER_ERROR_TIMEOUT.
2046 StopWorkerIfIdle();
2049 void ServiceWorkerVersion::StopWorkerIfIdle() {
2050 if (HasInflightRequests() && !ping_controller_->IsTimedOut())
2051 return;
2052 if (running_status() == STOPPED || running_status() == STOPPING ||
2053 !stop_callbacks_.empty()) {
2054 return;
2057 // TODO(falken): We may need to handle StopIfIdle failure and
2058 // forcibly fail pending callbacks so no one is stuck waiting
2059 // for the worker.
2060 embedded_worker_->StopIfIdle();
2063 bool ServiceWorkerVersion::HasInflightRequests() const {
2064 return !activate_requests_.IsEmpty() || !install_requests_.IsEmpty() ||
2065 !fetch_requests_.IsEmpty() || !sync_requests_.IsEmpty() ||
2066 !notification_click_requests_.IsEmpty() || !push_requests_.IsEmpty() ||
2067 !geofencing_requests_.IsEmpty() ||
2068 !service_port_connect_requests_.IsEmpty() ||
2069 !streaming_url_request_jobs_.empty();
2072 void ServiceWorkerVersion::RecordStartWorkerResult(
2073 ServiceWorkerStatusCode status) {
2074 base::TimeTicks start_time = start_time_;
2075 ClearTick(&start_time_);
2077 ServiceWorkerMetrics::RecordStartWorkerStatus(status,
2078 IsInstalled(prestart_status_));
2080 if (status == SERVICE_WORKER_OK && !start_time.is_null() &&
2081 !skip_recording_startup_time_) {
2082 ServiceWorkerMetrics::RecordStartWorkerTime(GetTickDuration(start_time),
2083 IsInstalled(prestart_status_));
2086 if (status != SERVICE_WORKER_ERROR_TIMEOUT)
2087 return;
2088 EmbeddedWorkerInstance::StartingPhase phase =
2089 EmbeddedWorkerInstance::NOT_STARTING;
2090 EmbeddedWorkerInstance::Status running_status = embedded_worker_->status();
2091 // Build an artifical JavaScript exception to show in the ServiceWorker
2092 // log for developers; it's not user-facing so it's not a localized resource.
2093 std::string message = "ServiceWorker startup timed out. ";
2094 if (running_status != EmbeddedWorkerInstance::STARTING) {
2095 message.append("The worker had unexpected status: ");
2096 message.append(EmbeddedWorkerInstance::StatusToString(running_status));
2097 } else {
2098 phase = embedded_worker_->starting_phase();
2099 message.append("The worker was in startup phase: ");
2100 message.append(EmbeddedWorkerInstance::StartingPhaseToString(phase));
2102 message.append(".");
2103 OnReportException(base::UTF8ToUTF16(message), -1, -1, GURL());
2104 DVLOG(1) << message;
2105 UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.TimeoutPhase",
2106 phase,
2107 EmbeddedWorkerInstance::STARTING_PHASE_MAX_VALUE);
2110 template <typename IDMAP>
2111 void ServiceWorkerVersion::RemoveCallbackAndStopIfRedundant(IDMAP* callbacks,
2112 int request_id) {
2113 RestartTick(&idle_time_);
2114 callbacks->Remove(request_id);
2115 if (is_redundant()) {
2116 // The stop should be already scheduled, but try to stop immediately, in
2117 // order to release worker resources soon.
2118 StopWorkerIfIdle();
2122 template <typename CallbackType>
2123 int ServiceWorkerVersion::AddRequest(
2124 const CallbackType& callback,
2125 IDMap<PendingRequest<CallbackType>, IDMapOwnPointer>* callback_map,
2126 RequestType request_type) {
2127 base::TimeTicks now = base::TimeTicks::Now();
2128 int request_id =
2129 callback_map->Add(new PendingRequest<CallbackType>(callback, now));
2130 requests_.push(RequestInfo(request_id, request_type, now));
2131 return request_id;
2134 bool ServiceWorkerVersion::OnRequestTimeout(const RequestInfo& info) {
2135 switch (info.type) {
2136 case REQUEST_ACTIVATE:
2137 return RunIDMapCallback(&activate_requests_, info.id,
2138 SERVICE_WORKER_ERROR_TIMEOUT);
2139 case REQUEST_INSTALL:
2140 return RunIDMapCallback(&install_requests_, info.id,
2141 SERVICE_WORKER_ERROR_TIMEOUT);
2142 case REQUEST_FETCH:
2143 return RunIDMapCallback(
2144 &fetch_requests_, info.id, SERVICE_WORKER_ERROR_TIMEOUT,
2145 /* The other args are ignored for non-OK status. */
2146 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK, ServiceWorkerResponse());
2147 case REQUEST_SYNC:
2148 return RunIDMapCallback(&sync_requests_, info.id,
2149 SERVICE_WORKER_ERROR_TIMEOUT);
2150 case REQUEST_NOTIFICATION_CLICK:
2151 return RunIDMapCallback(&notification_click_requests_, info.id,
2152 SERVICE_WORKER_ERROR_TIMEOUT);
2153 case REQUEST_PUSH:
2154 return RunIDMapCallback(&push_requests_, info.id,
2155 SERVICE_WORKER_ERROR_TIMEOUT);
2156 case REQUEST_GEOFENCING:
2157 return RunIDMapCallback(&geofencing_requests_, info.id,
2158 SERVICE_WORKER_ERROR_TIMEOUT);
2159 case REQUEST_SERVICE_PORT_CONNECT:
2160 return RunIDMapCallback(&service_port_connect_requests_, info.id,
2161 SERVICE_WORKER_ERROR_TIMEOUT,
2162 false /* accept_connection */, base::string16(),
2163 base::string16());
2165 NOTREACHED() << "Got unexpected request type: " << info.type;
2166 return false;
2169 void ServiceWorkerVersion::SetAllRequestTimes(const base::TimeTicks& ticks) {
2170 std::queue<RequestInfo> new_requests;
2171 while (!requests_.empty()) {
2172 RequestInfo info = requests_.front();
2173 info.time = ticks;
2174 new_requests.push(info);
2175 requests_.pop();
2177 requests_ = new_requests;
2180 ServiceWorkerStatusCode ServiceWorkerVersion::DeduceStartWorkerFailureReason(
2181 ServiceWorkerStatusCode default_code) {
2182 if (ping_controller_->IsTimedOut())
2183 return SERVICE_WORKER_ERROR_TIMEOUT;
2185 if (start_worker_status_ != SERVICE_WORKER_OK)
2186 return start_worker_status_;
2188 const net::URLRequestStatus& main_script_status =
2189 script_cache_map()->main_script_status();
2190 if (main_script_status.status() != net::URLRequestStatus::SUCCESS) {
2191 switch (main_script_status.error()) {
2192 case net::ERR_INSECURE_RESPONSE:
2193 case net::ERR_UNSAFE_REDIRECT:
2194 return SERVICE_WORKER_ERROR_SECURITY;
2195 case net::ERR_ABORTED:
2196 return SERVICE_WORKER_ERROR_ABORT;
2197 default:
2198 return SERVICE_WORKER_ERROR_NETWORK;
2202 return default_code;
2205 void ServiceWorkerVersion::MarkIfStale() {
2206 if (!context_)
2207 return;
2208 if (update_timer_.IsRunning() || !stale_time_.is_null())
2209 return;
2210 ServiceWorkerRegistration* registration =
2211 context_->GetLiveRegistration(registration_id_);
2212 if (!registration || registration->active_version() != this)
2213 return;
2214 base::TimeDelta time_since_last_check =
2215 base::Time::Now() - registration->last_update_check();
2216 if (time_since_last_check >
2217 base::TimeDelta::FromHours(kServiceWorkerScriptMaxCacheAgeInHours))
2218 RestartTick(&stale_time_);
2221 void ServiceWorkerVersion::FoundRegistrationForUpdate(
2222 ServiceWorkerStatusCode status,
2223 const scoped_refptr<ServiceWorkerRegistration>& registration) {
2224 if (!context_)
2225 return;
2227 const scoped_refptr<ServiceWorkerVersion> protect = this;
2228 if (is_update_scheduled_) {
2229 context_->UnprotectVersion(version_id_);
2230 is_update_scheduled_ = false;
2233 if (status != SERVICE_WORKER_OK || registration->active_version() != this)
2234 return;
2235 context_->UpdateServiceWorker(registration.get(),
2236 false /* force_bypass_cache */);
2239 void ServiceWorkerVersion::OnStoppedInternal(
2240 EmbeddedWorkerInstance::Status old_status) {
2241 DCHECK_EQ(STOPPED, running_status());
2242 scoped_refptr<ServiceWorkerVersion> protect(this);
2244 DCHECK(metrics_);
2245 metrics_.reset();
2247 bool should_restart = !is_redundant() && !start_callbacks_.empty() &&
2248 (old_status != EmbeddedWorkerInstance::STARTING);
2250 ClearTick(&stop_time_);
2251 StopTimeoutTimer();
2253 if (ping_controller_->IsTimedOut())
2254 should_restart = false;
2256 // Fire all stop callbacks.
2257 RunCallbacks(this, &stop_callbacks_, SERVICE_WORKER_OK);
2259 if (!should_restart) {
2260 // Let all start callbacks fail.
2261 RunCallbacks(this, &start_callbacks_,
2262 DeduceStartWorkerFailureReason(
2263 SERVICE_WORKER_ERROR_START_WORKER_FAILED));
2266 // Let all message callbacks fail (this will also fire and clear all
2267 // callbacks for events).
2268 // TODO(kinuko): Consider if we want to add queue+resend mechanism here.
2269 RunIDMapCallbacks(&activate_requests_,
2270 SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED);
2271 RunIDMapCallbacks(&install_requests_,
2272 SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED);
2273 RunIDMapCallbacks(&fetch_requests_, SERVICE_WORKER_ERROR_FAILED,
2274 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
2275 ServiceWorkerResponse());
2276 RunIDMapCallbacks(&sync_requests_, SERVICE_WORKER_ERROR_FAILED);
2277 RunIDMapCallbacks(&notification_click_requests_, SERVICE_WORKER_ERROR_FAILED);
2278 RunIDMapCallbacks(&push_requests_, SERVICE_WORKER_ERROR_FAILED);
2279 RunIDMapCallbacks(&geofencing_requests_, SERVICE_WORKER_ERROR_FAILED);
2281 // Close all mojo services. This will also fire and clear all callbacks
2282 // for messages that are still outstanding for those services.
2283 OnServicePortDispatcherConnectionError();
2285 OnBackgroundSyncDispatcherConnectionError();
2287 streaming_url_request_jobs_.clear();
2289 FOR_EACH_OBSERVER(Listener, listeners_, OnRunningStateChanged(this));
2291 if (should_restart)
2292 StartWorkerInternal();
2295 void ServiceWorkerVersion::OnServicePortDispatcherConnectionError() {
2296 RunIDMapCallbacks(&service_port_connect_requests_,
2297 SERVICE_WORKER_ERROR_FAILED, false, base::string16(),
2298 base::string16());
2299 service_port_dispatcher_.reset();
2302 void ServiceWorkerVersion::OnBackgroundSyncDispatcherConnectionError() {
2303 RunIDMapCallbacks(&sync_requests_, SERVICE_WORKER_ERROR_FAILED);
2304 background_sync_dispatcher_.reset();
2307 } // namespace content