1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_version.h"
7 #include "base/command_line.h"
8 #include "base/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"
21 typedef ServiceWorkerVersion::StatusCallback StatusCallback
;
22 typedef ServiceWorkerVersion::MessageCallback MessageCallback
;
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)
29 const int64 kStopWorkerDelay
= 30; // 30 secs.
31 // Default delay for scheduled update.
32 const int kUpdateDelaySeconds
= 10;
34 void RunSoon(const base::Closure
& callback
) {
35 if (!callback
.is_null())
36 base::MessageLoop::current()->PostTask(FROM_HERE
, callback
);
39 template <typename CallbackArray
, typename Arg
>
40 void RunCallbacks(ServiceWorkerVersion
* version
,
41 CallbackArray
* callbacks_ptr
,
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
)
51 template <typename IDMAP
, typename Method
, typename Params
>
52 void RunIDMapCallbacks(IDMAP
* callbacks
, Method method
, const Params
& params
) {
53 typename
IDMAP::iterator
iter(callbacks
);
54 while (!iter
.IsAtEnd()) {
55 DispatchToMethod(iter
.GetCurrentValue(), method
, params
);
61 // A callback adapter to start a |task| after StartWorker.
62 void 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
);
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
);
83 void RunErrorFetchCallback(const ServiceWorkerVersion::FetchCallback
& callback
,
84 ServiceWorkerStatusCode status
) {
86 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK
,
87 ServiceWorkerResponse());
92 ServiceWorkerVersion::ServiceWorkerVersion(
93 ServiceWorkerRegistration
* registration
,
95 base::WeakPtr
<ServiceWorkerContextCore
> context
)
96 : version_id_(version_id
),
97 registration_id_(kInvalidServiceWorkerVersionId
),
100 script_cache_map_(this, context
),
102 weak_factory_(this) {
104 DCHECK(registration
);
106 registration_id_
= registration
->id();
107 script_url_
= registration
->script_url();
108 scope_
= registration
->pattern();
110 context_
->AddLiveVersion(this);
111 embedded_worker_
= context_
->embedded_worker_registry()->CreateWorker();
112 embedded_worker_
->AddListener(this);
113 stores_listener_
.reset(new ServiceWorkerStoresListener(this));
114 embedded_worker_
->AddListener(stores_listener_
.get());
117 ServiceWorkerVersion::~ServiceWorkerVersion() {
118 embedded_worker_
->RemoveListener(this);
120 context_
->RemoveLiveVersion(version_id_
);
121 // EmbeddedWorker's dtor sends StopWorker if it's still running.
124 void ServiceWorkerVersion::SetStatus(Status status
) {
125 if (status_
== status
)
128 // Schedule to stop worker after registration successfully completed.
129 if (status_
== ACTIVATING
&& status
== ACTIVATED
&& !HasControllee())
130 ScheduleStopWorker();
134 std::vector
<base::Closure
> callbacks
;
135 callbacks
.swap(status_change_callbacks_
);
136 for (std::vector
<base::Closure
>::const_iterator i
= callbacks
.begin();
137 i
!= callbacks
.end(); ++i
) {
141 FOR_EACH_OBSERVER(Listener
, listeners_
, OnVersionStateChanged(this));
144 void ServiceWorkerVersion::RegisterStatusChangeCallback(
145 const base::Closure
& callback
) {
146 status_change_callbacks_
.push_back(callback
);
149 ServiceWorkerVersionInfo
ServiceWorkerVersion::GetInfo() {
150 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
151 return ServiceWorkerVersionInfo(
155 embedded_worker()->process_id(),
156 embedded_worker()->thread_id(),
157 embedded_worker()->worker_devtools_agent_route_id());
160 void ServiceWorkerVersion::StartWorker(const StatusCallback
& callback
) {
161 StartWorkerWithCandidateProcesses(std::vector
<int>(), false, callback
);
164 void ServiceWorkerVersion::StartWorkerWithCandidateProcesses(
165 const std::vector
<int>& possible_process_ids
,
166 bool pause_after_download
,
167 const StatusCallback
& callback
) {
168 switch (running_status()) {
170 RunSoon(base::Bind(callback
, SERVICE_WORKER_OK
));
173 RunSoon(base::Bind(callback
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
));
177 start_callbacks_
.push_back(callback
);
178 if (running_status() == STOPPED
) {
179 embedded_worker_
->Start(
183 pause_after_download
,
184 possible_process_ids
,
185 base::Bind(&ServiceWorkerVersion::RunStartWorkerCallbacksOnError
,
186 weak_factory_
.GetWeakPtr()));
192 void ServiceWorkerVersion::StopWorker(const StatusCallback
& callback
) {
193 if (running_status() == STOPPED
) {
194 RunSoon(base::Bind(callback
, SERVICE_WORKER_OK
));
197 if (stop_callbacks_
.empty()) {
198 ServiceWorkerStatusCode status
= embedded_worker_
->Stop();
199 if (status
!= SERVICE_WORKER_OK
) {
200 RunSoon(base::Bind(callback
, status
));
204 stop_callbacks_
.push_back(callback
);
207 void ServiceWorkerVersion::ScheduleUpdate() {
208 if (update_timer_
.IsRunning()) {
209 update_timer_
.Reset();
213 FROM_HERE
, base::TimeDelta::FromSeconds(kUpdateDelaySeconds
),
214 base::Bind(&ServiceWorkerVersion::StartUpdate
,
215 weak_factory_
.GetWeakPtr()));
218 void ServiceWorkerVersion::DeferScheduledUpdate() {
219 if (update_timer_
.IsRunning())
220 update_timer_
.Reset();
223 void ServiceWorkerVersion::StartUpdate() {
224 update_timer_
.Stop();
227 ServiceWorkerRegistration
* registration
=
228 context_
->GetLiveRegistration(registration_id_
);
231 context_
->UpdateServiceWorker(registration
);
234 void 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
)));
246 ServiceWorkerStatusCode status
= embedded_worker_
->SendMessage(message
);
247 RunSoon(base::Bind(callback
, status
));
250 void ServiceWorkerVersion::DispatchInstallEvent(
251 int active_version_id
,
252 const StatusCallback
& callback
) {
253 DCHECK_EQ(INSTALLING
, status()) << status();
255 if (running_status() != RUNNING
) {
256 // Schedule calling this method after starting the worker.
258 base::Bind(&RunTaskAfterStartWorker
,
259 weak_factory_
.GetWeakPtr(),
261 base::Bind(&self::DispatchInstallEventAfterStartWorker
,
262 weak_factory_
.GetWeakPtr(),
266 DispatchInstallEventAfterStartWorker(active_version_id
, callback
);
270 void ServiceWorkerVersion::DispatchActivateEvent(
271 const StatusCallback
& callback
) {
272 DCHECK_EQ(ACTIVATING
, status()) << status();
274 if (running_status() != RUNNING
) {
275 // Schedule calling this method after starting the worker.
277 base::Bind(&RunTaskAfterStartWorker
,
278 weak_factory_
.GetWeakPtr(),
280 base::Bind(&self::DispatchActivateEventAfterStartWorker
,
281 weak_factory_
.GetWeakPtr(),
284 DispatchActivateEventAfterStartWorker(callback
);
288 void ServiceWorkerVersion::DispatchFetchEvent(
289 const ServiceWorkerFetchRequest
& request
,
290 const FetchCallback
& callback
) {
291 DCHECK_EQ(ACTIVATED
, status()) << status();
293 if (running_status() != RUNNING
) {
294 // Schedule calling this method after starting the worker.
295 StartWorker(base::Bind(&RunTaskAfterStartWorker
,
296 weak_factory_
.GetWeakPtr(),
297 base::Bind(&RunErrorFetchCallback
, callback
),
298 base::Bind(&self::DispatchFetchEvent
,
299 weak_factory_
.GetWeakPtr(),
300 request
, callback
)));
304 int request_id
= fetch_callbacks_
.Add(new FetchCallback(callback
));
305 ServiceWorkerStatusCode status
= embedded_worker_
->SendMessage(
306 ServiceWorkerMsg_FetchEvent(request_id
, request
));
307 if (status
!= SERVICE_WORKER_OK
) {
308 fetch_callbacks_
.Remove(request_id
);
309 RunSoon(base::Bind(&RunErrorFetchCallback
,
311 SERVICE_WORKER_ERROR_FAILED
));
315 void ServiceWorkerVersion::DispatchSyncEvent(const StatusCallback
& callback
) {
316 DCHECK_EQ(ACTIVATED
, status()) << status();
318 if (!CommandLine::ForCurrentProcess()->HasSwitch(
319 switches::kEnableServiceWorkerSync
)) {
320 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
324 if (running_status() != RUNNING
) {
325 // Schedule calling this method after starting the worker.
326 StartWorker(base::Bind(&RunTaskAfterStartWorker
,
327 weak_factory_
.GetWeakPtr(), callback
,
328 base::Bind(&self::DispatchSyncEvent
,
329 weak_factory_
.GetWeakPtr(),
334 int request_id
= sync_callbacks_
.Add(new StatusCallback(callback
));
335 ServiceWorkerStatusCode status
= embedded_worker_
->SendMessage(
336 ServiceWorkerMsg_SyncEvent(request_id
));
337 if (status
!= SERVICE_WORKER_OK
) {
338 sync_callbacks_
.Remove(request_id
);
339 RunSoon(base::Bind(callback
, status
));
343 void ServiceWorkerVersion::DispatchPushEvent(const StatusCallback
& callback
,
344 const std::string
& data
) {
345 DCHECK_EQ(ACTIVATED
, status()) << status();
347 if (!CommandLine::ForCurrentProcess()->HasSwitch(
348 switches::kEnableExperimentalWebPlatformFeatures
)) {
349 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
353 if (running_status() != RUNNING
) {
354 // Schedule calling this method after starting the worker.
355 StartWorker(base::Bind(&RunTaskAfterStartWorker
,
356 weak_factory_
.GetWeakPtr(), callback
,
357 base::Bind(&self::DispatchPushEvent
,
358 weak_factory_
.GetWeakPtr(),
363 int request_id
= push_callbacks_
.Add(new StatusCallback(callback
));
364 ServiceWorkerStatusCode status
= embedded_worker_
->SendMessage(
365 ServiceWorkerMsg_PushEvent(request_id
, data
));
366 if (status
!= SERVICE_WORKER_OK
) {
367 push_callbacks_
.Remove(request_id
);
368 RunSoon(base::Bind(callback
, status
));
372 void ServiceWorkerVersion::AddProcessToWorker(int process_id
) {
373 embedded_worker_
->AddProcessReference(process_id
);
376 void ServiceWorkerVersion::RemoveProcessFromWorker(int process_id
) {
377 embedded_worker_
->ReleaseProcessReference(process_id
);
380 bool ServiceWorkerVersion::HasProcessToRun() const {
381 return embedded_worker_
->HasProcessToRun();
384 void ServiceWorkerVersion::AddControllee(
385 ServiceWorkerProviderHost
* provider_host
) {
386 DCHECK(!ContainsKey(controllee_map_
, provider_host
));
387 int controllee_id
= controllee_by_id_
.Add(provider_host
);
388 controllee_map_
[provider_host
] = controllee_id
;
389 AddProcessToWorker(provider_host
->process_id());
390 if (stop_worker_timer_
.IsRunning())
391 stop_worker_timer_
.Stop();
394 void ServiceWorkerVersion::RemoveControllee(
395 ServiceWorkerProviderHost
* provider_host
) {
396 ControlleeMap::iterator found
= controllee_map_
.find(provider_host
);
397 DCHECK(found
!= controllee_map_
.end());
398 controllee_by_id_
.Remove(found
->second
);
399 controllee_map_
.erase(found
);
400 RemoveProcessFromWorker(provider_host
->process_id());
403 FOR_EACH_OBSERVER(Listener
, listeners_
, OnNoControllees(this));
408 ScheduleStopWorker();
411 void ServiceWorkerVersion::AddPotentialControllee(
412 ServiceWorkerProviderHost
* provider_host
) {
413 AddProcessToWorker(provider_host
->process_id());
416 void ServiceWorkerVersion::RemovePotentialControllee(
417 ServiceWorkerProviderHost
* provider_host
) {
418 RemoveProcessFromWorker(provider_host
->process_id());
421 void ServiceWorkerVersion::AddListener(Listener
* listener
) {
422 listeners_
.AddObserver(listener
);
425 void ServiceWorkerVersion::RemoveListener(Listener
* listener
) {
426 listeners_
.RemoveObserver(listener
);
429 void ServiceWorkerVersion::Doom() {
433 if (!HasControllee())
437 void ServiceWorkerVersion::OnStarted() {
438 DCHECK_EQ(RUNNING
, running_status());
439 if (status() == ACTIVATED
&& !HasControllee())
440 ScheduleStopWorker();
441 // Fire all start callbacks.
442 RunCallbacks(this, &start_callbacks_
, SERVICE_WORKER_OK
);
443 FOR_EACH_OBSERVER(Listener
, listeners_
, OnWorkerStarted(this));
446 void ServiceWorkerVersion::OnStopped() {
447 DCHECK_EQ(STOPPED
, running_status());
448 scoped_refptr
<ServiceWorkerVersion
> protect(this);
450 // Fire all stop callbacks.
451 RunCallbacks(this, &stop_callbacks_
, SERVICE_WORKER_OK
);
453 // Let all start callbacks fail.
455 this, &start_callbacks_
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
);
457 // Let all message callbacks fail (this will also fire and clear all
458 // callbacks for events).
459 // TODO(kinuko): Consider if we want to add queue+resend mechanism here.
460 RunIDMapCallbacks(&activate_callbacks_
,
461 &StatusCallback::Run
,
462 MakeTuple(SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED
));
463 RunIDMapCallbacks(&install_callbacks_
,
464 &StatusCallback::Run
,
465 MakeTuple(SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED
));
466 RunIDMapCallbacks(&fetch_callbacks_
,
468 MakeTuple(SERVICE_WORKER_ERROR_FAILED
,
469 SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK
,
470 ServiceWorkerResponse()));
471 RunIDMapCallbacks(&sync_callbacks_
,
472 &StatusCallback::Run
,
473 MakeTuple(SERVICE_WORKER_ERROR_FAILED
));
474 RunIDMapCallbacks(&push_callbacks_
,
475 &StatusCallback::Run
,
476 MakeTuple(SERVICE_WORKER_ERROR_FAILED
));
478 FOR_EACH_OBSERVER(Listener
, listeners_
, OnWorkerStopped(this));
481 void ServiceWorkerVersion::OnReportException(
482 const base::string16
& error_message
,
485 const GURL
& source_url
) {
490 this, error_message
, line_number
, column_number
, source_url
));
493 void ServiceWorkerVersion::OnReportConsoleMessage(int source_identifier
,
495 const base::string16
& message
,
497 const GURL
& source_url
) {
498 FOR_EACH_OBSERVER(Listener
,
500 OnReportConsoleMessage(this,
508 bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message
& message
) {
510 IPC_BEGIN_MESSAGE_MAP(ServiceWorkerVersion
, message
)
511 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_GetClientDocuments
,
512 OnGetClientDocuments
)
513 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_ActivateEventFinished
,
514 OnActivateEventFinished
)
515 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_InstallEventFinished
,
516 OnInstallEventFinished
)
517 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_FetchEventFinished
,
518 OnFetchEventFinished
)
519 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_SyncEventFinished
,
521 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PushEventFinished
,
523 IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_PostMessageToDocument
,
524 OnPostMessageToDocument
)
525 IPC_MESSAGE_UNHANDLED(handled
= false)
526 IPC_END_MESSAGE_MAP()
530 void ServiceWorkerVersion::RunStartWorkerCallbacksOnError(
531 ServiceWorkerStatusCode status
) {
532 if (status
!= SERVICE_WORKER_OK
)
533 RunCallbacks(this, &start_callbacks_
, status
);
536 void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker(
537 int active_version_id
,
538 const StatusCallback
& callback
) {
539 DCHECK_EQ(RUNNING
, running_status())
540 << "Worker stopped too soon after it was started.";
542 int request_id
= install_callbacks_
.Add(new StatusCallback(callback
));
543 ServiceWorkerStatusCode status
= embedded_worker_
->SendMessage(
544 ServiceWorkerMsg_InstallEvent(request_id
, active_version_id
));
545 if (status
!= SERVICE_WORKER_OK
) {
546 install_callbacks_
.Remove(request_id
);
547 RunSoon(base::Bind(callback
, status
));
551 void ServiceWorkerVersion::DispatchActivateEventAfterStartWorker(
552 const StatusCallback
& callback
) {
553 DCHECK_EQ(RUNNING
, running_status())
554 << "Worker stopped too soon after it was started.";
556 int request_id
= activate_callbacks_
.Add(new StatusCallback(callback
));
557 ServiceWorkerStatusCode status
=
558 embedded_worker_
->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id
));
559 if (status
!= SERVICE_WORKER_OK
) {
560 activate_callbacks_
.Remove(request_id
);
561 RunSoon(base::Bind(callback
, status
));
565 void ServiceWorkerVersion::OnGetClientDocuments(int request_id
) {
566 std::vector
<int> client_ids
;
567 ControlleeByIDMap::iterator
it(&controllee_by_id_
);
568 while (!it
.IsAtEnd()) {
569 client_ids
.push_back(it
.GetCurrentKey());
572 // Don't bother if it's no longer running.
573 if (running_status() == RUNNING
) {
574 embedded_worker_
->SendMessage(
575 ServiceWorkerMsg_DidGetClientDocuments(request_id
, client_ids
));
579 void ServiceWorkerVersion::OnActivateEventFinished(
581 blink::WebServiceWorkerEventResult result
) {
582 DCHECK(ACTIVATING
== status() ||
583 REDUNDANT
== status()) << status();
585 StatusCallback
* callback
= activate_callbacks_
.Lookup(request_id
);
587 NOTREACHED() << "Got unexpected message: " << request_id
;
590 ServiceWorkerStatusCode rv
= SERVICE_WORKER_OK
;
591 if (result
== blink::WebServiceWorkerEventResultRejected
||
592 status() != ACTIVATING
) {
593 rv
= SERVICE_WORKER_ERROR_ACTIVATE_WORKER_FAILED
;
596 scoped_refptr
<ServiceWorkerVersion
> protect(this);
598 activate_callbacks_
.Remove(request_id
);
601 void ServiceWorkerVersion::OnInstallEventFinished(
603 blink::WebServiceWorkerEventResult result
) {
604 DCHECK_EQ(INSTALLING
, status()) << status();
606 StatusCallback
* callback
= install_callbacks_
.Lookup(request_id
);
608 NOTREACHED() << "Got unexpected message: " << request_id
;
611 ServiceWorkerStatusCode status
= SERVICE_WORKER_OK
;
612 if (result
== blink::WebServiceWorkerEventResultRejected
)
613 status
= SERVICE_WORKER_ERROR_INSTALL_WORKER_FAILED
;
615 scoped_refptr
<ServiceWorkerVersion
> protect(this);
616 callback
->Run(status
);
617 install_callbacks_
.Remove(request_id
);
620 void ServiceWorkerVersion::OnFetchEventFinished(
622 ServiceWorkerFetchEventResult result
,
623 const ServiceWorkerResponse
& response
) {
624 FetchCallback
* callback
= fetch_callbacks_
.Lookup(request_id
);
626 NOTREACHED() << "Got unexpected message: " << request_id
;
630 scoped_refptr
<ServiceWorkerVersion
> protect(this);
631 callback
->Run(SERVICE_WORKER_OK
, result
, response
);
632 fetch_callbacks_
.Remove(request_id
);
635 void ServiceWorkerVersion::OnSyncEventFinished(
637 StatusCallback
* callback
= sync_callbacks_
.Lookup(request_id
);
639 NOTREACHED() << "Got unexpected message: " << request_id
;
643 scoped_refptr
<ServiceWorkerVersion
> protect(this);
644 callback
->Run(SERVICE_WORKER_OK
);
645 sync_callbacks_
.Remove(request_id
);
648 void ServiceWorkerVersion::OnPushEventFinished(
650 StatusCallback
* callback
= push_callbacks_
.Lookup(request_id
);
652 NOTREACHED() << "Got unexpected message: " << request_id
;
656 scoped_refptr
<ServiceWorkerVersion
> protect(this);
657 callback
->Run(SERVICE_WORKER_OK
);
658 push_callbacks_
.Remove(request_id
);
661 void ServiceWorkerVersion::OnPostMessageToDocument(
663 const base::string16
& message
,
664 const std::vector
<int>& sent_message_port_ids
) {
665 ServiceWorkerProviderHost
* provider_host
=
666 controllee_by_id_
.Lookup(client_id
);
667 if (!provider_host
) {
668 // The client may already have been closed, just ignore.
671 provider_host
->PostMessage(message
, sent_message_port_ids
);
674 void ServiceWorkerVersion::ScheduleStopWorker() {
675 if (running_status() != RUNNING
)
677 if (stop_worker_timer_
.IsRunning()) {
678 stop_worker_timer_
.Reset();
681 stop_worker_timer_
.Start(
682 FROM_HERE
, base::TimeDelta::FromSeconds(kStopWorkerDelay
),
683 base::Bind(&ServiceWorkerVersion::StopWorker
,
684 weak_factory_
.GetWeakPtr(),
685 base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
)));
688 void ServiceWorkerVersion::DoomInternal() {
689 DCHECK(!HasControllee());
690 SetStatus(REDUNDANT
);
691 StopWorker(base::Bind(&ServiceWorkerUtils::NoOpStatusCallback
));
694 std::vector
<ServiceWorkerDatabase::ResourceRecord
> resources
;
695 script_cache_map_
.GetResources(&resources
);
696 context_
->storage()->PurgeResources(resources
);
699 } // namespace content