1 // Copyright 2014 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_internals_ui.h"
10 #include "base/bind.h"
11 #include "base/memory/scoped_vector.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h"
14 #include "content/browser/devtools/devtools_agent_host_impl.h"
15 #include "content/browser/devtools/service_worker_devtools_manager.h"
16 #include "content/browser/service_worker/service_worker_context_observer.h"
17 #include "content/browser/service_worker/service_worker_context_wrapper.h"
18 #include "content/browser/service_worker/service_worker_registration.h"
19 #include "content/browser/service_worker/service_worker_version.h"
20 #include "content/grit/content_resources.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/storage_partition.h"
24 #include "content/public/browser/web_contents.h"
25 #include "content/public/browser/web_ui.h"
26 #include "content/public/browser/web_ui_data_source.h"
27 #include "content/public/common/url_constants.h"
29 using base::DictionaryValue
;
30 using base::FundamentalValue
;
31 using base::ListValue
;
32 using base::StringValue
;
40 using GetRegistrationsCallback
=
41 base::Callback
<void(const std::vector
<ServiceWorkerRegistrationInfo
>&,
42 const std::vector
<ServiceWorkerVersionInfo
>&,
43 const std::vector
<ServiceWorkerRegistrationInfo
>&)>;
45 void OperationCompleteCallback(WeakPtr
<ServiceWorkerInternalsUI
> internals
,
47 ServiceWorkerStatusCode status
) {
48 if (!BrowserThread::CurrentlyOn(BrowserThread::UI
)) {
49 BrowserThread::PostTask(
52 base::Bind(OperationCompleteCallback
, internals
, callback_id
, status
));
55 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
57 internals
->web_ui()->CallJavascriptFunction(
58 "serviceworker.onOperationComplete",
59 FundamentalValue(static_cast<int>(status
)),
60 FundamentalValue(callback_id
));
64 void CallServiceWorkerVersionMethodWithVersionID(
65 ServiceWorkerInternalsUI::ServiceWorkerVersionMethod method
,
66 scoped_refptr
<ServiceWorkerContextWrapper
> context
,
68 const ServiceWorkerInternalsUI::StatusCallback
& callback
) {
69 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
70 BrowserThread::PostTask(
73 base::Bind(CallServiceWorkerVersionMethodWithVersionID
,
81 scoped_refptr
<ServiceWorkerVersion
> version
=
82 context
->GetLiveVersion(version_id
);
84 callback
.Run(SERVICE_WORKER_ERROR_NOT_FOUND
);
87 (*version
.get().*method
)(callback
);
90 void DispatchPushEventWithVersionID(
91 scoped_refptr
<ServiceWorkerContextWrapper
> context
,
93 const ServiceWorkerInternalsUI::StatusCallback
& callback
) {
94 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
95 BrowserThread::PostTask(
98 base::Bind(DispatchPushEventWithVersionID
,
105 scoped_refptr
<ServiceWorkerVersion
> version
=
106 context
->GetLiveVersion(version_id
);
107 if (!version
.get()) {
108 callback
.Run(SERVICE_WORKER_ERROR_NOT_FOUND
);
111 std::string data
= "Test push message from ServiceWorkerInternals.";
112 version
->DispatchPushEvent(callback
, data
);
115 void UpdateVersionInfo(const ServiceWorkerVersionInfo
& version
,
116 DictionaryValue
* info
) {
117 switch (version
.running_status
) {
118 case ServiceWorkerVersion::STOPPED
:
119 info
->SetString("running_status", "STOPPED");
121 case ServiceWorkerVersion::STARTING
:
122 info
->SetString("running_status", "STARTING");
124 case ServiceWorkerVersion::RUNNING
:
125 info
->SetString("running_status", "RUNNING");
127 case ServiceWorkerVersion::STOPPING
:
128 info
->SetString("running_status", "STOPPING");
132 switch (version
.status
) {
133 case ServiceWorkerVersion::NEW
:
134 info
->SetString("status", "NEW");
136 case ServiceWorkerVersion::INSTALLING
:
137 info
->SetString("status", "INSTALLING");
139 case ServiceWorkerVersion::INSTALLED
:
140 info
->SetString("status", "INSTALLED");
142 case ServiceWorkerVersion::ACTIVATING
:
143 info
->SetString("status", "ACTIVATING");
145 case ServiceWorkerVersion::ACTIVATED
:
146 info
->SetString("status", "ACTIVATED");
148 case ServiceWorkerVersion::REDUNDANT
:
149 info
->SetString("status", "REDUNDANT");
152 info
->SetString("script_url", version
.script_url
.spec());
153 info
->SetString("version_id", base::Int64ToString(version
.version_id
));
154 info
->SetInteger("process_id", version
.process_id
);
155 info
->SetInteger("thread_id", version
.thread_id
);
156 info
->SetInteger("devtools_agent_route_id", version
.devtools_agent_route_id
);
159 ListValue
* GetRegistrationListValue(
160 const std::vector
<ServiceWorkerRegistrationInfo
>& registrations
) {
161 ListValue
* result
= new ListValue();
162 for (std::vector
<ServiceWorkerRegistrationInfo
>::const_iterator it
=
163 registrations
.begin();
164 it
!= registrations
.end();
166 const ServiceWorkerRegistrationInfo
& registration
= *it
;
167 DictionaryValue
* registration_info
= new DictionaryValue();
168 registration_info
->SetString("scope", registration
.pattern
.spec());
169 registration_info
->SetString(
170 "registration_id", base::Int64ToString(registration
.registration_id
));
172 if (registration
.active_version
.version_id
!=
173 kInvalidServiceWorkerVersionId
) {
174 DictionaryValue
* active_info
= new DictionaryValue();
175 UpdateVersionInfo(registration
.active_version
, active_info
);
176 registration_info
->Set("active", active_info
);
179 if (registration
.waiting_version
.version_id
!=
180 kInvalidServiceWorkerVersionId
) {
181 DictionaryValue
* waiting_info
= new DictionaryValue();
182 UpdateVersionInfo(registration
.waiting_version
, waiting_info
);
183 registration_info
->Set("waiting", waiting_info
);
186 result
->Append(registration_info
);
191 ListValue
* GetVersionListValue(
192 const std::vector
<ServiceWorkerVersionInfo
>& versions
) {
193 ListValue
* result
= new ListValue();
194 for (std::vector
<ServiceWorkerVersionInfo
>::const_iterator it
=
196 it
!= versions
.end();
198 DictionaryValue
* info
= new DictionaryValue();
199 UpdateVersionInfo(*it
, info
);
200 result
->Append(info
);
205 void DidGetStoredRegistrationsOnIOThread(
206 scoped_refptr
<ServiceWorkerContextWrapper
> context
,
207 const GetRegistrationsCallback
& callback
,
208 const std::vector
<ServiceWorkerRegistrationInfo
>& stored_registrations
) {
209 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
210 BrowserThread::PostTask(
211 BrowserThread::UI
, FROM_HERE
,
212 base::Bind(callback
, context
->GetAllLiveRegistrationInfo(),
213 context
->GetAllLiveVersionInfo(), stored_registrations
));
216 void GetRegistrationsOnIOThread(
217 scoped_refptr
<ServiceWorkerContextWrapper
> context
,
218 const GetRegistrationsCallback
& callback
) {
219 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
220 context
->GetAllRegistrations(
221 base::Bind(DidGetStoredRegistrationsOnIOThread
, context
, callback
));
224 void DidGetRegistrations(
225 WeakPtr
<ServiceWorkerInternalsUI
> internals
,
227 const base::FilePath
& context_path
,
228 const std::vector
<ServiceWorkerRegistrationInfo
>& live_registrations
,
229 const std::vector
<ServiceWorkerVersionInfo
>& live_versions
,
230 const std::vector
<ServiceWorkerRegistrationInfo
>& stored_registrations
) {
231 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
235 ScopedVector
<const Value
> args
;
236 args
.push_back(GetRegistrationListValue(live_registrations
));
237 args
.push_back(GetVersionListValue(live_versions
));
238 args
.push_back(GetRegistrationListValue(stored_registrations
));
239 args
.push_back(new FundamentalValue(partition_id
));
240 args
.push_back(new StringValue(context_path
.value()));
241 internals
->web_ui()->CallJavascriptFunction("serviceworker.onPartitionData",
247 class ServiceWorkerInternalsUI::PartitionObserver
248 : public ServiceWorkerContextObserver
{
250 PartitionObserver(int partition_id
, WebUI
* web_ui
)
251 : partition_id_(partition_id
), web_ui_(web_ui
) {}
252 ~PartitionObserver() override
{}
253 // ServiceWorkerContextObserver overrides:
254 void OnRunningStateChanged(int64 version_id
,
255 ServiceWorkerVersion::RunningStatus
) override
{
256 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
257 web_ui_
->CallJavascriptFunction(
258 "serviceworker.onRunningStateChanged", FundamentalValue(partition_id_
),
259 StringValue(base::Int64ToString(version_id
)));
261 void OnVersionStateChanged(int64 version_id
,
262 ServiceWorkerVersion::Status
) override
{
263 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
264 web_ui_
->CallJavascriptFunction(
265 "serviceworker.onVersionStateChanged",
266 FundamentalValue(partition_id_
),
267 StringValue(base::Int64ToString(version_id
)));
269 void OnErrorReported(int64 version_id
,
272 const ErrorInfo
& info
) override
{
273 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
274 ScopedVector
<const Value
> args
;
275 args
.push_back(new FundamentalValue(partition_id_
));
276 args
.push_back(new StringValue(base::Int64ToString(version_id
)));
277 args
.push_back(new FundamentalValue(process_id
));
278 args
.push_back(new FundamentalValue(thread_id
));
279 scoped_ptr
<DictionaryValue
> value(new DictionaryValue());
280 value
->SetString("message", info
.error_message
);
281 value
->SetInteger("lineNumber", info
.line_number
);
282 value
->SetInteger("columnNumber", info
.column_number
);
283 value
->SetString("sourceURL", info
.source_url
.spec());
284 args
.push_back(value
.release());
285 web_ui_
->CallJavascriptFunction("serviceworker.onErrorReported",
288 void OnReportConsoleMessage(int64 version_id
,
291 const ConsoleMessage
& message
) override
{
292 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
293 ScopedVector
<const Value
> args
;
294 args
.push_back(new FundamentalValue(partition_id_
));
295 args
.push_back(new StringValue(base::Int64ToString(version_id
)));
296 args
.push_back(new FundamentalValue(process_id
));
297 args
.push_back(new FundamentalValue(thread_id
));
298 scoped_ptr
<DictionaryValue
> value(new DictionaryValue());
299 value
->SetInteger("sourceIdentifier", message
.source_identifier
);
300 value
->SetInteger("message_level", message
.message_level
);
301 value
->SetString("message", message
.message
);
302 value
->SetInteger("lineNumber", message
.line_number
);
303 value
->SetString("sourceURL", message
.source_url
.spec());
304 args
.push_back(value
.release());
305 web_ui_
->CallJavascriptFunction("serviceworker.onConsoleMessageReported",
308 void OnRegistrationStored(int64 registration_id
,
309 const GURL
& pattern
) override
{
310 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
311 web_ui_
->CallJavascriptFunction("serviceworker.onRegistrationStored",
312 StringValue(pattern
.spec()));
314 void OnRegistrationDeleted(int64 registration_id
,
315 const GURL
& pattern
) override
{
316 web_ui_
->CallJavascriptFunction("serviceworker.onRegistrationDeleted",
317 StringValue(pattern
.spec()));
319 int partition_id() const { return partition_id_
; }
322 const int partition_id_
;
323 WebUI
* const web_ui_
;
326 ServiceWorkerInternalsUI::ServiceWorkerInternalsUI(WebUI
* web_ui
)
327 : WebUIController(web_ui
), next_partition_id_(0) {
328 WebUIDataSource
* source
=
329 WebUIDataSource::Create(kChromeUIServiceWorkerInternalsHost
);
330 source
->SetJsonPath("strings.js");
331 source
->AddResourcePath("serviceworker_internals.js",
332 IDR_SERVICE_WORKER_INTERNALS_JS
);
333 source
->AddResourcePath("serviceworker_internals.css",
334 IDR_SERVICE_WORKER_INTERNALS_CSS
);
335 source
->SetDefaultResource(IDR_SERVICE_WORKER_INTERNALS_HTML
);
336 source
->DisableDenyXFrameOptions();
338 BrowserContext
* browser_context
=
339 web_ui
->GetWebContents()->GetBrowserContext();
340 WebUIDataSource::Add(browser_context
, source
);
342 web_ui
->RegisterMessageCallback(
344 base::Bind(&ServiceWorkerInternalsUI::GetOptions
,
345 base::Unretained(this)));
346 web_ui
->RegisterMessageCallback(
348 base::Bind(&ServiceWorkerInternalsUI::SetOption
, base::Unretained(this)));
349 web_ui
->RegisterMessageCallback(
350 "getAllRegistrations",
351 base::Bind(&ServiceWorkerInternalsUI::GetAllRegistrations
,
352 base::Unretained(this)));
353 web_ui
->RegisterMessageCallback(
355 base::Bind(&ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod
,
356 base::Unretained(this),
357 &ServiceWorkerVersion::StopWorker
));
358 web_ui
->RegisterMessageCallback(
360 base::Bind(&ServiceWorkerInternalsUI::DispatchPushEvent
,
361 base::Unretained(this)));
362 web_ui
->RegisterMessageCallback(
364 base::Bind(&ServiceWorkerInternalsUI::InspectWorker
,
365 base::Unretained(this)));
366 web_ui
->RegisterMessageCallback(
368 base::Bind(&ServiceWorkerInternalsUI::Unregister
,
369 base::Unretained(this)));
370 web_ui
->RegisterMessageCallback(
372 base::Bind(&ServiceWorkerInternalsUI::StartWorker
,
373 base::Unretained(this)));
376 ServiceWorkerInternalsUI::~ServiceWorkerInternalsUI() {
377 BrowserContext
* browser_context
=
378 web_ui()->GetWebContents()->GetBrowserContext();
379 // Safe to use base::Unretained(this) because
380 // ForEachStoragePartition is synchronous.
381 BrowserContext::StoragePartitionCallback remove_observer_cb
=
382 base::Bind(&ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition
,
383 base::Unretained(this));
384 BrowserContext::ForEachStoragePartition(browser_context
, remove_observer_cb
);
387 void ServiceWorkerInternalsUI::GetOptions(const ListValue
* args
) {
388 DictionaryValue options
;
389 options
.SetBoolean("debug_on_start",
390 ServiceWorkerDevToolsManager::GetInstance()
391 ->debug_service_worker_on_start());
392 web_ui()->CallJavascriptFunction("serviceworker.onOptions", options
);
395 void ServiceWorkerInternalsUI::SetOption(const ListValue
* args
) {
396 std::string option_name
;
398 if (!args
->GetString(0, &option_name
) || option_name
!= "debug_on_start" ||
399 !args
->GetBoolean(1, &option_boolean
)) {
402 ServiceWorkerDevToolsManager::GetInstance()
403 ->set_debug_service_worker_on_start(option_boolean
);
406 void ServiceWorkerInternalsUI::GetAllRegistrations(const ListValue
* args
) {
407 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
408 BrowserContext
* browser_context
=
409 web_ui()->GetWebContents()->GetBrowserContext();
410 // Safe to use base::Unretained(this) because
411 // ForEachStoragePartition is synchronous.
412 BrowserContext::StoragePartitionCallback add_context_cb
=
413 base::Bind(&ServiceWorkerInternalsUI::AddContextFromStoragePartition
,
414 base::Unretained(this));
415 BrowserContext::ForEachStoragePartition(browser_context
, add_context_cb
);
418 void ServiceWorkerInternalsUI::AddContextFromStoragePartition(
419 StoragePartition
* partition
) {
420 int partition_id
= 0;
421 scoped_refptr
<ServiceWorkerContextWrapper
> context
=
422 static_cast<ServiceWorkerContextWrapper
*>(
423 partition
->GetServiceWorkerContext());
424 if (PartitionObserver
* observer
=
425 observers_
.get(reinterpret_cast<uintptr_t>(partition
))) {
426 partition_id
= observer
->partition_id();
428 partition_id
= next_partition_id_
++;
429 scoped_ptr
<PartitionObserver
> new_observer(
430 new PartitionObserver(partition_id
, web_ui()));
431 context
->AddObserver(new_observer
.get());
432 observers_
.set(reinterpret_cast<uintptr_t>(partition
), new_observer
.Pass());
435 BrowserThread::PostTask(
436 BrowserThread::IO
, FROM_HERE
,
437 base::Bind(GetRegistrationsOnIOThread
, context
,
438 base::Bind(DidGetRegistrations
, AsWeakPtr(), partition_id
,
439 context
->is_incognito() ? base::FilePath()
440 : partition
->GetPath())));
443 void ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition(
444 StoragePartition
* partition
) {
445 scoped_ptr
<PartitionObserver
> observer(
446 observers_
.take_and_erase(reinterpret_cast<uintptr_t>(partition
)));
449 scoped_refptr
<ServiceWorkerContextWrapper
> context
=
450 static_cast<ServiceWorkerContextWrapper
*>(
451 partition
->GetServiceWorkerContext());
452 context
->RemoveObserver(observer
.get());
455 void ServiceWorkerInternalsUI::FindContext(
457 StoragePartition
** result_partition
,
458 StoragePartition
* storage_partition
) const {
459 PartitionObserver
* observer
=
460 observers_
.get(reinterpret_cast<uintptr_t>(storage_partition
));
461 if (observer
&& partition_id
== observer
->partition_id()) {
462 *result_partition
= storage_partition
;
466 bool ServiceWorkerInternalsUI::GetServiceWorkerContext(
468 scoped_refptr
<ServiceWorkerContextWrapper
>* context
) const {
469 BrowserContext
* browser_context
=
470 web_ui()->GetWebContents()->GetBrowserContext();
471 StoragePartition
* result_partition(NULL
);
472 BrowserContext::StoragePartitionCallback find_context_cb
=
473 base::Bind(&ServiceWorkerInternalsUI::FindContext
,
474 base::Unretained(this),
477 BrowserContext::ForEachStoragePartition(browser_context
, find_context_cb
);
478 if (!result_partition
)
480 *context
= static_cast<ServiceWorkerContextWrapper
*>(
481 result_partition
->GetServiceWorkerContext());
485 void ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod(
486 ServiceWorkerVersionMethod method
,
487 const ListValue
* args
) {
488 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
490 const DictionaryValue
* cmd_args
= NULL
;
492 scoped_refptr
<ServiceWorkerContextWrapper
> context
;
493 std::string version_id_string
;
494 int64 version_id
= 0;
495 if (!args
->GetInteger(0, &callback_id
) ||
496 !args
->GetDictionary(1, &cmd_args
) ||
497 !cmd_args
->GetInteger("partition_id", &partition_id
) ||
498 !GetServiceWorkerContext(partition_id
, &context
) ||
499 !cmd_args
->GetString("version_id", &version_id_string
) ||
500 !base::StringToInt64(version_id_string
, &version_id
)) {
504 base::Callback
<void(ServiceWorkerStatusCode
)> callback
=
505 base::Bind(OperationCompleteCallback
, AsWeakPtr(), callback_id
);
506 CallServiceWorkerVersionMethodWithVersionID(
507 method
, context
, version_id
, callback
);
510 void ServiceWorkerInternalsUI::DispatchPushEvent(
511 const ListValue
* args
) {
512 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
515 int64 version_id
= 0;
516 std::string version_id_string
;
517 const DictionaryValue
* cmd_args
= NULL
;
518 scoped_refptr
<ServiceWorkerContextWrapper
> context
;
519 if (!args
->GetInteger(0, &callback_id
) ||
520 !args
->GetDictionary(1, &cmd_args
) ||
521 !cmd_args
->GetInteger("partition_id", &partition_id
) ||
522 !GetServiceWorkerContext(partition_id
, &context
) ||
523 !cmd_args
->GetString("version_id", &version_id_string
) ||
524 !base::StringToInt64(version_id_string
, &version_id
)) {
528 base::Callback
<void(ServiceWorkerStatusCode
)> callback
=
529 base::Bind(OperationCompleteCallback
, AsWeakPtr(), callback_id
);
530 DispatchPushEventWithVersionID(context
, version_id
, callback
);
533 void ServiceWorkerInternalsUI::InspectWorker(const ListValue
* args
) {
534 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
536 const DictionaryValue
* cmd_args
= NULL
;
538 int devtools_agent_route_id
= 0;
539 if (!args
->GetInteger(0, &callback_id
) ||
540 !args
->GetDictionary(1, &cmd_args
) ||
541 !cmd_args
->GetInteger("process_id", &process_id
) ||
542 !cmd_args
->GetInteger("devtools_agent_route_id",
543 &devtools_agent_route_id
)) {
546 base::Callback
<void(ServiceWorkerStatusCode
)> callback
=
547 base::Bind(OperationCompleteCallback
, AsWeakPtr(), callback_id
);
548 scoped_refptr
<DevToolsAgentHostImpl
> agent_host(
549 ServiceWorkerDevToolsManager::GetInstance()
550 ->GetDevToolsAgentHostForWorker(process_id
, devtools_agent_route_id
));
551 if (!agent_host
.get()) {
552 callback
.Run(SERVICE_WORKER_ERROR_NOT_FOUND
);
555 agent_host
->Inspect(web_ui()->GetWebContents()->GetBrowserContext());
556 callback
.Run(SERVICE_WORKER_OK
);
559 void ServiceWorkerInternalsUI::Unregister(const ListValue
* args
) {
560 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
563 std::string scope_string
;
564 const DictionaryValue
* cmd_args
= NULL
;
565 scoped_refptr
<ServiceWorkerContextWrapper
> context
;
566 if (!args
->GetInteger(0, &callback_id
) ||
567 !args
->GetDictionary(1, &cmd_args
) ||
568 !cmd_args
->GetInteger("partition_id", &partition_id
) ||
569 !GetServiceWorkerContext(partition_id
, &context
) ||
570 !cmd_args
->GetString("scope", &scope_string
)) {
574 base::Callback
<void(ServiceWorkerStatusCode
)> callback
=
575 base::Bind(OperationCompleteCallback
, AsWeakPtr(), callback_id
);
576 UnregisterWithScope(context
, GURL(scope_string
), callback
);
579 void ServiceWorkerInternalsUI::StartWorker(const ListValue
* args
) {
580 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
583 std::string scope_string
;
584 const DictionaryValue
* cmd_args
= NULL
;
585 scoped_refptr
<ServiceWorkerContextWrapper
> context
;
586 if (!args
->GetInteger(0, &callback_id
) ||
587 !args
->GetDictionary(1, &cmd_args
) ||
588 !cmd_args
->GetInteger("partition_id", &partition_id
) ||
589 !GetServiceWorkerContext(partition_id
, &context
) ||
590 !cmd_args
->GetString("scope", &scope_string
)) {
593 base::Callback
<void(ServiceWorkerStatusCode
)> callback
=
594 base::Bind(OperationCompleteCallback
, AsWeakPtr(), callback_id
);
595 context
->StartServiceWorker(GURL(scope_string
), callback
);
598 void ServiceWorkerInternalsUI::UnregisterWithScope(
599 scoped_refptr
<ServiceWorkerContextWrapper
> context
,
601 const ServiceWorkerInternalsUI::StatusCallback
& callback
) const {
602 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
603 BrowserThread::PostTask(
604 BrowserThread::IO
, FROM_HERE
,
605 base::Bind(&ServiceWorkerInternalsUI::UnregisterWithScope
,
606 base::Unretained(this), context
, scope
, callback
));
610 if (!context
->context()) {
611 callback
.Run(SERVICE_WORKER_ERROR_ABORT
);
615 // ServiceWorkerContextWrapper::UnregisterServiceWorker doesn't work here
616 // because that reduces a status code to boolean.
617 context
->context()->UnregisterServiceWorker(scope
, callback
);
620 } // namespace content