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.
4
5#include "content/browser/service_worker/service_worker_version.h"
6
7#include "base/command_line.h"
8#include "base/stl_util.h"
9#include "base/strings/string16.h"
10#include "content/browser/service_worker/embedded_worker_instance.h"
11#include "content/browser/service_worker/embedded_worker_registry.h"
12#include "content/browser/service_worker/service_worker_context_core.h"
13#include "content/browser/service_worker/service_worker_registration.h"
14#include "content/browser/service_worker/service_worker_utils.h"
15#include "content/common/service_worker/service_worker_messages.h"
16#include "content/public/browser/browser_thread.h"
17#include "content/public/common/content_switches.h"
18
19namespace content {
20
21typedef ServiceWorkerVersion::StatusCallback StatusCallback;
22typedef ServiceWorkerVersion::MessageCallback MessageCallback;
23
24namespace {
25
26// Default delay for scheduled stop.
27// (Note that if all references to the version is dropped the worker
28// is also stopped without delay)
29const int64 kStopWorkerDelay = 30;  // 30 secs.
30
31// Default delay for scheduled update.
32const int kUpdateDelaySeconds = 1;
33
34void RunSoon(const base::Closure& callback) {
35  if (!callback.is_null())
36    base::MessageLoop::current()->PostTask(FROM_HERE, callback);
37}
38
39template <typename CallbackArray, typename Arg>
40void RunCallbacks(ServiceWorkerVersion* version,
41                  CallbackArray* callbacks_ptr,
42                  const Arg& arg) {
43  CallbackArray callbacks;
44  callbacks.swap(*callbacks_ptr);
45  scoped_refptr<ServiceWorkerVersion> protect(version);
46  for (typename CallbackArray::const_iterator i = callbacks.begin();
47       i != callbacks.end(); ++i)
48    (*i).Run(arg);
49}
50
51template <typename IDMAP, typename Method, typename Params>
52void RunIDMapCallbacks(IDMAP* callbacks, Method method, const Params& params) {
53  typename IDMAP::iterator iter(callbacks);
54  while (!iter.IsAtEnd()) {
55    DispatchToMethod(iter.GetCurrentValue(), method, params);
56    iter.Advance();
57  }
58  callbacks->Clear();
59}
60
61// A callback adapter to start a |task| after StartWorker.
62void RunTaskAfterStartWorker(
63    base::WeakPtr<ServiceWorkerVersion> version,
64    const StatusCallback& error_callback,
65    const base::Closure& task,
66    ServiceWorkerStatusCode status) {
67  if (status != SERVICE_WORKER_OK) {
68    if (!error_callback.is_null())
69      error_callback.Run(status);
70    return;
71  }
72  if (version->running_status() != ServiceWorkerVersion::RUNNING) {
73    // We've tried to start the worker (and it has succeeded), but
74    // it looks it's not running yet.
75    NOTREACHED() << "The worker's not running after successful StartWorker";
76    if (!error_callback.is_null())
77      error_callback.Run(SERVICE_WORKER_ERROR_START_WORKER_FAILED);
78    return;
79  }
80  task.Run();
81}
82
83void RunErrorFetchCallback(const ServiceWorkerVersion::FetchCallback& callback,
84                           ServiceWorkerStatusCode status) {
85  callback.Run(status,
86               SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
87               ServiceWorkerResponse());
88}
89
90}  // namespace
91
92ServiceWorkerVersion::ServiceWorkerVersion(
93    ServiceWorkerRegistration* registration,
94    const GURL& script_url,
95    int64 version_id,
96    base::WeakPtr<ServiceWorkerContextCore> context)
97    : version_id_(version_id),
98      registration_id_(kInvalidServiceWorkerVersionId),
99      script_url_(script_url),
100      status_(NEW),
101      context_(context),
102      script_cache_map_(this, context),
103      is_doomed_(false),
104      weak_factory_(this) {
105  DCHECK(context_);
106  DCHECK(registration);
107  if (registration) {
108    registration_id_ = registration->id();
109    scope_ = registration->pattern();
110  }
111  context_->AddLiveVersion(this);
112  embedded_worker_ = context_->embedded_worker_registry()->CreateWorker();
113  embedded_worker_->AddListener(this);
114  cache_listener_.reset(new ServiceWorkerCacheListener(this, context));
115  embedded_worker_->AddListener(cache_listener_.get());
116}
117
118ServiceWorkerVersion::~ServiceWorkerVersion() {
119  embedded_worker_->RemoveListener(this);
120  if (context_)
121    context_->RemoveLiveVersion(version_id_);
122  // EmbeddedWorker's dtor sends StopWorker if it's still running.
123}
124
125void ServiceWorkerVersion::SetStatus(Status status) {
126  if (status_ == status)
127    return;
128
129  // Schedule to stop worker after registration successfully completed.
130  if (status_ == ACTIVATING && status == ACTIVATED && !HasControllee())
131    ScheduleStopWorker();
132
133  status_ = status;
134
135  std::vector<base::Closure> callbacks;
136  callbacks.swap(status_change_callbacks_);
137  for (std::vector<base::Closure>::const_iterator i = callbacks.begin();
138       i != callbacks.end(); ++i) {
139    (*i).Run();
140  }
141
142  FOR_EACH_OBSERVER(Listener, listeners_, OnVersionStateChanged(this));
143}
144
145void ServiceWorkerVersion::RegisterStatusChangeCallback(
146    const base::Closure& callback) {
147  status_change_callbacks_.push_back(callback);
148}
149
150ServiceWorkerVersionInfo ServiceWorkerVersion::GetInfo() {
151  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
152  return ServiceWorkerVersionInfo(
153      running_status(),
154      status(),
155      script_url(),
156      version_id(),
157      embedded_worker()->process_id(),
158      embedded_worker()->thread_id(),
159      embedded_worker()->worker_devtools_agent_route_id());
160}
161
162void ServiceWorkerVersion::StartWorker(const StatusCallback& callback) {
163  StartWorker(false, callback);
164}
165
166void ServiceWorkerVersion::StartWorker(
167    bool pause_after_download,
168    const StatusCallback& callback) {
169  switch (running_status()) {
170    case RUNNING:
171      RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
172      return;
173    case STOPPING:
174      RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED));
175      return;
176    case STOPPED:
177    case STARTING:
178      start_callbacks_.push_back(callback);
179      if (running_status() == STOPPED) {
180        embedded_worker_->Start(
181            version_id_,
182            scope_,
183            script_url_,
184            pause_after_download,
185            base::Bind(&ServiceWorkerVersion::RunStartWorkerCallbacksOnError,
186                       weak_factory_.GetWeakPtr()));
187      }
188      return;
189  }
190}
191
192void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) {
193  if (running_status() == STOPPED) {
194    RunSoon(base::Bind(callback, SERVICE_WORKER_OK));
195    return;
196  }
197  if (stop_callbacks_.empty()) {
198    ServiceWorkerStatusCode status = embedded_worker_->Stop();
199    if (status != SERVICE_WORKER_OK) {
200      RunSoon(base::Bind(callback, status));
201      return;
202    }
203  }
204  stop_callbacks_.push_back(callback);
205}
206
207void ServiceWorkerVersion::ScheduleUpdate() {
208  if (update_timer_.IsRunning()) {
209    update_timer_.Reset();
210    return;
211  }
212  update_timer_.Start(
213      FROM_HERE, base::TimeDelta::FromSeconds(kUpdateDelaySeconds),
214      base::Bind(&ServiceWorkerVersion::StartUpdate,
215                 weak_factory_.GetWeakPtr()));
216}
217
218void ServiceWorkerVersion::DeferScheduledUpdate() {
219  if (update_timer_.IsRunning())
220    update_timer_.Reset();
221}
222
223void ServiceWorkerVersion::StartUpdate() {
224  update_timer_.Stop();
225  if (!context_)
226    return;
227  ServiceWorkerRegistration* registration =
228      context_->GetLiveRegistration(registration_id_);
229  if (!registration || !registration->GetNewestVersion())
230    return;
231  context_->UpdateServiceWorker(registration);
232}
233
234void ServiceWorkerVersion::SendMessage(
235    const IPC::Message& message, const StatusCallback& callback) {
236  if (running_status() != RUNNING) {
237    // Schedule calling this method after starting the worker.
238    StartWorker(base::Bind(&RunTaskAfterStartWorker,
239                           weak_factory_.GetWeakPtr(), callback,
240                           base::Bind(&self::SendMessage,
241                                      weak_factory_.GetWeakPtr(),
242                                      message, callback)));
243    return;
244  }
245
246  ServiceWorkerStatusCode status = embedded_worker_->SendMessage(message);
247  RunSoon(base::Bind(callback, status));
248}
249
250void ServiceWorkerVersion::DispatchInstallEvent(
251    int active_version_id,
252    const StatusCallback& callback) {
253  DCHECK_EQ(INSTALLING, status()) << status();
254
255  if (running_status() != RUNNING) {
256    // Schedule calling this method after starting the worker.
257    StartWorker(
258        base::Bind(&RunTaskAfterStartWorker,
259                   weak_factory_.GetWeakPtr(),
260                   callback,
261                   base::Bind(&self::DispatchInstallEventAfterStartWorker,
262                              weak_factory_.GetWeakPtr(),
263                              active_version_id,
264                              callback)));
265  } else {
266    DispatchInstallEventAfterStartWorker(active_version_id, callback);
267  }
268}
269
270void ServiceWorkerVersion::DispatchActivateEvent(
271    const StatusCallback& callback) {
272  DCHECK_EQ(ACTIVATING, status()) << status();
273
274  if (running_status() != RUNNING) {
275    // Schedule calling this method after starting the worker.
276    StartWorker(
277        base::Bind(&RunTaskAfterStartWorker,
278                   weak_factory_.GetWeakPtr(),
279                   callback,
280                   base::Bind(&self::DispatchActivateEventAfterStartWorker,
281                              weak_factory_.GetWeakPtr(),
282                              callback)));
283  } else {
284    DispatchActivateEventAfterStartWorker(callback);
285  }
286}
287
288void ServiceWorkerVersion::DispatchFetchEvent(
289    const ServiceWorkerFetchRequest& request,
290    const base::Closure& prepare_callback,
291    const FetchCallback& fetch_callback) {
292  DCHECK_EQ(ACTIVATED, status()) << status();
293
294  if (running_status() != RUNNING) {
295    // Schedule calling this method after starting the worker.
296    StartWorker(base::Bind(&RunTaskAfterStartWorker,
297                           weak_factory_.GetWeakPtr(),
298                           base::Bind(&RunErrorFetchCallback, fetch_callback),
299                           base::Bind(&self::DispatchFetchEvent,
300                                      weak_factory_.GetWeakPtr(),
301                                      request,
302                                      prepare_callback,
303                                      fetch_callback)));
304    return;
305  }
306
307  prepare_callback.Run();
308
309  int request_id = fetch_callbacks_.Add(new FetchCallback(fetch_callback));
310  ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
311      ServiceWorkerMsg_FetchEvent(request_id, request));
312  if (status != SERVICE_WORKER_OK) {
313    fetch_callbacks_.Remove(request_id);
314    RunSoon(base::Bind(&RunErrorFetchCallback,
315                       fetch_callback,
316                       SERVICE_WORKER_ERROR_FAILED));
317  }
318}
319
320void ServiceWorkerVersion::DispatchSyncEvent(const StatusCallback& callback) {
321  DCHECK_EQ(ACTIVATED, status()) << status();
322
323  if (!CommandLine::ForCurrentProcess()->HasSwitch(
324          switches::kEnableServiceWorkerSync)) {
325    callback.Run(SERVICE_WORKER_ERROR_ABORT);
326    return;
327  }
328
329  if (running_status() != RUNNING) {
330    // Schedule calling this method after starting the worker.
331    StartWorker(base::Bind(&RunTaskAfterStartWorker,
332                           weak_factory_.GetWeakPtr(), callback,
333                           base::Bind(&self::DispatchSyncEvent,
334                                      weak_factory_.GetWeakPtr(),
335                                      callback)));
336    return;
337  }
338
339  int request_id = sync_callbacks_.Add(new StatusCallback(callback));
340  ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
341      ServiceWorkerMsg_SyncEvent(request_id));
342  if (status != SERVICE_WORKER_OK) {
343    sync_callbacks_.Remove(request_id);
344    RunSoon(base::Bind(callback, status));
345  }
346}
347
348void ServiceWorkerVersion::DispatchPushEvent(const StatusCallback& callback,
349                                             const std::string& data) {
350  DCHECK_EQ(ACTIVATED, status()) << status();
351
352  if (!CommandLine::ForCurrentProcess()->HasSwitch(
353          switches::kEnableExperimentalWebPlatformFeatures)) {
354    callback.Run(SERVICE_WORKER_ERROR_ABORT);
355    return;
356  }
357
358  if (running_status() != RUNNING) {
359    // Schedule calling this method after starting the worker.
360    StartWorker(base::Bind(&RunTaskAfterStartWorker,
361                           weak_factory_.GetWeakPtr(), callback,
362                           base::Bind(&self::DispatchPushEvent,
363                                      weak_factory_.GetWeakPtr(),
364                                      callback, data)));
365    return;
366  }
367
368  int request_id = push_callbacks_.Add(new StatusCallback(callback));
369  ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
370      ServiceWorkerMsg_PushEvent(request_id, data));
371  if (status != SERVICE_WORKER_OK) {
372    push_callbacks_.Remove(request_id);
373    RunSoon(base::Bind(callback, status));
374  }
375}
376
377void ServiceWorkerVersion::AddControllee(
378    ServiceWorkerProviderHost* provider_host) {
379  DCHECK(!ContainsKey(controllee_map_, provider_host));
380  int controllee_id = controllee_by_id_.Add(provider_host);
381  controllee_map_[provider_host] = controllee_id;
382  if (stop_worker_timer_.IsRunning())
383    stop_worker_timer_.Stop();
384}
385
386void ServiceWorkerVersion::RemoveControllee(
387    ServiceWorkerProviderHost* provider_host) {
388  ControlleeMap::iterator found = controllee_map_.find(provider_host);
389  DCHECK(found != controllee_map_.end());
390  controllee_by_id_.Remove(found->second);
391  controllee_map_.erase(found);
392  if (HasControllee())
393    return;
394  FOR_EACH_OBSERVER(Listener, listeners_, OnNoControllees(this));
395  if (is_doomed_) {
396    DoomInternal();
397    return;
398  }
399  ScheduleStopWorker();
400}
401
402void ServiceWorkerVersion::AddListener(Listener* listener) {
403  listeners_.AddObserver(listener);
404}
405
406void ServiceWorkerVersion::RemoveListener(Listener* listener) {
407  listeners_.RemoveObserver(listener);
408}
409
410void ServiceWorkerVersion::Doom() {
411  if (is_doomed_)
412    return;
413  is_doomed_ = true;
414  if (!HasControllee())
415    DoomInternal();
416}
417
418void ServiceWorkerVersion::OnStarted() {
419  DCHECK_EQ(RUNNING, running_status());
420  if (status() == ACTIVATED && !HasControllee())
421    ScheduleStopWorker();
422  // Fire all start callbacks.
423  RunCallbacks(this, &start_callbacks_, SERVICE_WORKER_OK);
424  FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStarted(this));
425}
426
427void ServiceWorkerVersion::OnStopped() {
428  DCHECK_EQ(STOPPED, running_status());
429  scoped_refptr<ServiceWorkerVersion> protect(this);
430
431  // Fire all stop callbacks.
432  RunCallbacks(this, &stop_callbacks_, SERVICE_WORKER_OK);
433
434  // Let all start callbacks fail.
435  RunCallbacks(
436      this, &start_callbacks_, SERVICE_WORKER_ERROR_START_WORKER_FAILED);
437
438  // Let all message callbacks fail (this will also fire and clear all
439  // callbacks for events).
440  // TODO(kinuko): Consider if we want to add queue+resend mechanism here.
441  RunIDMapCallbacks(&activate_callbacks_,
442                    &StatusCallback::Run,
443                    MakeTuple(SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED));
444  RunIDMapCallbacks(&install_callbacks_,
445                    &StatusCallback::Run,
446                    MakeTuple(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED));
447  RunIDMapCallbacks(&fetch_callbacks_,
448                    &FetchCallback::Run,
449                    MakeTuple(SERVICE_WORKER_ERROR_FAILED,
450                              SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK,
451                              ServiceWorkerResponse()));
452  RunIDMapCallbacks(&sync_callbacks_,
453                    &StatusCallback::Run,
454                    MakeTuple(SERVICE_WORKER_ERROR_FAILED));
455  RunIDMapCallbacks(&push_callbacks_,
456                    &StatusCallback::Run,
457                    MakeTuple(SERVICE_WORKER_ERROR_FAILED));
458
459  FOR_EACH_OBSERVER(Listener, listeners_, OnWorkerStopped(this));
460}
461
462void ServiceWorkerVersion::OnReportException(
463    const base::string16& error_message,
464    int line_number,
465    int column_number,
466    const GURL& source_url) {
467  FOR_EACH_OBSERVER(
468      Listener,
469      listeners_,
470      OnErrorReported(
471          this, error_message, line_number, column_number, source_url));
472}
473
474void ServiceWorkerVersion::OnReportConsoleMessage(int source_identifier,
475                                                  int message_level,
476                                                  const base::string16& message,
477                                                  int line_number,
478                                                  const GURL& source_url) {
479  FOR_EACH_OBSERVER(Listener,
480                    listeners_,
481                    OnReportConsoleMessage(this,
482                                           source_identifier,
483                                           message_level,
484                                           message,
485                                           line_number,
486                                           source_url));
487}
488
489bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message& message) {
490  bool handled = true;
491  IPC_BEGIN_MESSAGE_MAP(ServiceWorkerVersion, message)
492    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetClientDocuments,
493                        OnGetClientDocuments)
494    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ActivateEventFinished,
495                        OnActivateEventFinished)
496    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_InstallEventFinished,
497                        OnInstallEventFinished)
498    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FetchEventFinished,
499                        OnFetchEventFinished)
500    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SyncEventFinished,
501                        OnSyncEventFinished)
502    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PushEventFinished,
503                        OnPushEventFinished)
504    IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToDocument,
505                        OnPostMessageToDocument)
506    IPC_MESSAGE_UNHANDLED(handled = false)
507  IPC_END_MESSAGE_MAP()
508  return handled;
509}
510
511void ServiceWorkerVersion::RunStartWorkerCallbacksOnError(
512    ServiceWorkerStatusCode status) {
513  if (status != SERVICE_WORKER_OK)
514    RunCallbacks(this, &start_callbacks_, status);
515}
516
517void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
518    int active_version_id,
519    const StatusCallback& callback) {
520  DCHECK_EQ(RUNNING, running_status())
521      << "Worker stopped too soon after it was started.";
522
523  int request_id = install_callbacks_.Add(new StatusCallback(callback));
524  ServiceWorkerStatusCode status = embedded_worker_->SendMessage(
525      ServiceWorkerMsg_InstallEvent(request_id, active_version_id));
526  if (status != SERVICE_WORKER_OK) {
527    install_callbacks_.Remove(request_id);
528    RunSoon(base::Bind(callback, status));
529  }
530}
531
532void ServiceWorkerVersion::DispatchActivateEventAfterStartWorker(
533    const StatusCallback& callback) {
534  DCHECK_EQ(RUNNING, running_status())
535      << "Worker stopped too soon after it was started.";
536
537  int request_id = activate_callbacks_.Add(new StatusCallback(callback));
538  ServiceWorkerStatusCode status =
539      embedded_worker_->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id));
540  if (status != SERVICE_WORKER_OK) {
541    activate_callbacks_.Remove(request_id);
542    RunSoon(base::Bind(callback, status));
543  }
544}
545
546void ServiceWorkerVersion::OnGetClientDocuments(int request_id) {
547  std::vector<int> client_ids;
548  ControlleeByIDMap::iterator it(&controllee_by_id_);
549  TRACE_EVENT0("ServiceWorker",
550               "ServiceWorkerVersion::OnGetClientDocuments");
551  while (!it.IsAtEnd()) {
552    client_ids.push_back(it.GetCurrentKey());
553    it.Advance();
554  }
555  // Don't bother if it's no longer running.
556  if (running_status() == RUNNING) {
557    embedded_worker_->SendMessage(
558        ServiceWorkerMsg_DidGetClientDocuments(request_id, client_ids));
559  }
560}
561
562void ServiceWorkerVersion::OnActivateEventFinished(
563    int request_id,
564    blink::WebServiceWorkerEventResult result) {
565  DCHECK(ACTIVATING == status() ||
566         REDUNDANT == status()) << status();
567  TRACE_EVENT0("ServiceWorker",
568               "ServiceWorkerVersion::OnActivateEventFinished");
569
570  StatusCallback* callback = activate_callbacks_.Lookup(request_id);
571  if (!callback) {
572    NOTREACHED() << "Got unexpected message: " << request_id;
573    return;
574  }
575  ServiceWorkerStatusCode rv = SERVICE_WORKER_OK;
576  if (result == blink::WebServiceWorkerEventResultRejected ||
577      status() != ACTIVATING) {
578    rv = SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED;
579  }
580
581  scoped_refptr<ServiceWorkerVersion> protect(this);
582  callback->Run(rv);
583  activate_callbacks_.Remove(request_id);
584}
585
586void ServiceWorkerVersion::OnInstallEventFinished(
587    int request_id,
588    blink::WebServiceWorkerEventResult result) {
589  DCHECK_EQ(INSTALLING, status()) << status();
590  TRACE_EVENT0("ServiceWorker",
591               "ServiceWorkerVersion::OnInstallEventFinished");
592
593  StatusCallback* callback = install_callbacks_.Lookup(request_id);
594  if (!callback) {
595    NOTREACHED() << "Got unexpected message: " << request_id;
596    return;
597  }
598  ServiceWorkerStatusCode status = SERVICE_WORKER_OK;
599  if (result == blink::WebServiceWorkerEventResultRejected)
600    status = SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED;
601
602  scoped_refptr<ServiceWorkerVersion> protect(this);
603  callback->Run(status);
604  install_callbacks_.Remove(request_id);
605}
606
607void ServiceWorkerVersion::OnFetchEventFinished(
608    int request_id,
609    ServiceWorkerFetchEventResult result,
610    const ServiceWorkerResponse& response) {
611  TRACE_EVENT1("ServiceWorker",
612               "ServiceWorkerVersion::OnFetchEventFinished",
613               "Request id", request_id);
614  FetchCallback* callback = fetch_callbacks_.Lookup(request_id);
615  if (!callback) {
616    NOTREACHED() << "Got unexpected message: " << request_id;
617    return;
618  }
619
620  scoped_refptr<ServiceWorkerVersion> protect(this);
621  callback->Run(SERVICE_WORKER_OK, result, response);
622  fetch_callbacks_.Remove(request_id);
623}
624
625void ServiceWorkerVersion::OnSyncEventFinished(
626    int request_id) {
627  TRACE_EVENT1("ServiceWorker",
628               "ServiceWorkerVersion::OnSyncEventFinished",
629               "Request id", request_id);
630  StatusCallback* callback = sync_callbacks_.Lookup(request_id);
631  if (!callback) {
632    NOTREACHED() << "Got unexpected message: " << request_id;
633    return;
634  }
635
636  scoped_refptr<ServiceWorkerVersion> protect(this);
637  callback->Run(SERVICE_WORKER_OK);
638  sync_callbacks_.Remove(request_id);
639}
640
641void ServiceWorkerVersion::OnPushEventFinished(
642    int request_id) {
643  TRACE_EVENT1("ServiceWorker",
644               "ServiceWorkerVersion::OnPushEventFinished",
645               "Request id", request_id);
646  StatusCallback* callback = push_callbacks_.Lookup(request_id);
647  if (!callback) {
648    NOTREACHED() << "Got unexpected message: " << request_id;
649    return;
650  }
651
652  scoped_refptr<ServiceWorkerVersion> protect(this);
653  callback->Run(SERVICE_WORKER_OK);
654  push_callbacks_.Remove(request_id);
655}
656
657void ServiceWorkerVersion::OnPostMessageToDocument(
658    int client_id,
659    const base::string16& message,
660    const std::vector<int>& sent_message_port_ids) {
661  TRACE_EVENT1("ServiceWorker",
662               "ServiceWorkerVersion::OnPostMessageToDocument",
663               "Client id", client_id);
664  ServiceWorkerProviderHost* provider_host =
665      controllee_by_id_.Lookup(client_id);
666  if (!provider_host) {
667    // The client may already have been closed, just ignore.
668    return;
669  }
670  provider_host->PostMessage(message, sent_message_port_ids);
671}
672
673void ServiceWorkerVersion::ScheduleStopWorker() {
674  if (running_status() != RUNNING)
675    return;
676  if (stop_worker_timer_.IsRunning()) {
677    stop_worker_timer_.Reset();
678    return;
679  }
680  stop_worker_timer_.Start(
681      FROM_HERE, base::TimeDelta::FromSeconds(kStopWorkerDelay),
682      base::Bind(&ServiceWorkerVersion::StopWorker,
683                 weak_factory_.GetWeakPtr(),
684                 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)));
685}
686
687void ServiceWorkerVersion::DoomInternal() {
688  DCHECK(!HasControllee());
689  SetStatus(REDUNDANT);
690  StopWorker(base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
691  if (!context_)
692    return;
693  std::vector<ServiceWorkerDatabase::ResourceRecord> resources;
694  script_cache_map_.GetResources(&resources);
695  context_->storage()->PurgeResources(resources);
696}
697
698}  // namespace content
699