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.
4
5#include "content/browser/service_worker/service_worker_internals_ui.h"
6
7#include <string>
8#include <vector>
9
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/embedded_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"
28
29using base::DictionaryValue;
30using base::FundamentalValue;
31using base::ListValue;
32using base::StringValue;
33using base::Value;
34using base::WeakPtr;
35
36namespace content {
37
38namespace {
39
40void OperationCompleteCallback(WeakPtr<ServiceWorkerInternalsUI> internals,
41                               int callback_id,
42                               ServiceWorkerStatusCode status) {
43  if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
44    BrowserThread::PostTask(
45        BrowserThread::UI,
46        FROM_HERE,
47        base::Bind(OperationCompleteCallback, internals, callback_id, status));
48    return;
49  }
50  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
51  if (internals) {
52    internals->web_ui()->CallJavascriptFunction(
53        "serviceworker.onOperationComplete",
54        FundamentalValue(static_cast<int>(status)),
55        FundamentalValue(callback_id));
56  }
57}
58
59void CallServiceWorkerVersionMethodWithVersionID(
60    ServiceWorkerInternalsUI::ServiceWorkerVersionMethod method,
61    scoped_refptr<ServiceWorkerContextWrapper> context,
62    int64 version_id,
63    const ServiceWorkerInternalsUI::StatusCallback& callback) {
64  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
65    BrowserThread::PostTask(
66        BrowserThread::IO,
67        FROM_HERE,
68        base::Bind(CallServiceWorkerVersionMethodWithVersionID,
69                   method,
70                   context,
71                   version_id,
72                   callback));
73    return;
74  }
75  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
76  scoped_refptr<ServiceWorkerVersion> version =
77      context->context()->GetLiveVersion(version_id);
78  if (!version.get()) {
79    callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
80    return;
81  }
82  (*version.get().*method)(callback);
83}
84
85void DispatchPushEventWithVersionID(
86    scoped_refptr<ServiceWorkerContextWrapper> context,
87    int64 version_id,
88    const ServiceWorkerInternalsUI::StatusCallback& callback) {
89  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
90    BrowserThread::PostTask(
91        BrowserThread::IO,
92        FROM_HERE,
93        base::Bind(DispatchPushEventWithVersionID,
94                   context,
95                   version_id,
96                   callback));
97    return;
98  }
99  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
100  scoped_refptr<ServiceWorkerVersion> version =
101      context->context()->GetLiveVersion(version_id);
102  if (!version.get()) {
103    callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
104    return;
105  }
106  std::string data = "Test push message from ServiceWorkerInternals.";
107  version->DispatchPushEvent(callback, data);
108}
109
110void UnregisterWithScope(
111    scoped_refptr<ServiceWorkerContextWrapper> context,
112    const GURL& scope,
113    const ServiceWorkerInternalsUI::StatusCallback& callback) {
114  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
115    BrowserThread::PostTask(
116        BrowserThread::IO,
117        FROM_HERE,
118        base::Bind(UnregisterWithScope, context, scope, callback));
119    return;
120  }
121  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
122  context->context()->UnregisterServiceWorker(scope, callback);
123}
124
125void WorkerStarted(const scoped_refptr<ServiceWorkerRegistration>& registration,
126                   const ServiceWorkerInternalsUI::StatusCallback& callback,
127                   ServiceWorkerStatusCode status) {
128  callback.Run(status);
129}
130
131void StartActiveWorker(
132    const ServiceWorkerInternalsUI::StatusCallback& callback,
133    ServiceWorkerStatusCode status,
134    const scoped_refptr<ServiceWorkerRegistration>& registration) {
135  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
136  if (status == SERVICE_WORKER_OK) {
137    // Pass the reference of |registration| to WorkerStarted callback to prevent
138    // it from being deleted while starting the worker. If the refcount of
139    // |registration| is 1, it will be deleted after WorkerStarted is called.
140    registration->active_version()->StartWorker(
141        base::Bind(WorkerStarted, registration, callback));
142    return;
143  }
144  callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
145}
146
147void FindRegistrationForPattern(
148    scoped_refptr<ServiceWorkerContextWrapper> context,
149    const GURL& scope,
150    const ServiceWorkerStorage::FindRegistrationCallback callback) {
151  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
152    BrowserThread::PostTask(
153        BrowserThread::IO,
154        FROM_HERE,
155        base::Bind(FindRegistrationForPattern, context, scope, callback));
156    return;
157  }
158  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
159  context->context()->storage()->FindRegistrationForPattern(scope, callback);
160}
161
162void UpdateVersionInfo(const ServiceWorkerVersionInfo& version,
163                       DictionaryValue* info) {
164  switch (version.running_status) {
165    case ServiceWorkerVersion::STOPPED:
166      info->SetString("running_status", "STOPPED");
167      break;
168    case ServiceWorkerVersion::STARTING:
169      info->SetString("running_status", "STARTING");
170      break;
171    case ServiceWorkerVersion::RUNNING:
172      info->SetString("running_status", "RUNNING");
173      break;
174    case ServiceWorkerVersion::STOPPING:
175      info->SetString("running_status", "STOPPING");
176      break;
177  }
178
179  switch (version.status) {
180    case ServiceWorkerVersion::NEW:
181      info->SetString("status", "NEW");
182      break;
183    case ServiceWorkerVersion::INSTALLING:
184      info->SetString("status", "INSTALLING");
185      break;
186    case ServiceWorkerVersion::INSTALLED:
187      info->SetString("status", "INSTALLED");
188      break;
189    case ServiceWorkerVersion::ACTIVATING:
190      info->SetString("status", "ACTIVATING");
191      break;
192    case ServiceWorkerVersion::ACTIVATED:
193      info->SetString("status", "ACTIVATED");
194      break;
195    case ServiceWorkerVersion::REDUNDANT:
196      info->SetString("status", "REDUNDANT");
197      break;
198  }
199  info->SetString("script_url", version.script_url.spec());
200  info->SetString("version_id", base::Int64ToString(version.version_id));
201  info->SetInteger("process_id", version.process_id);
202  info->SetInteger("thread_id", version.thread_id);
203  info->SetInteger("devtools_agent_route_id", version.devtools_agent_route_id);
204}
205
206ListValue* GetRegistrationListValue(
207    const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
208  ListValue* result = new ListValue();
209  for (std::vector<ServiceWorkerRegistrationInfo>::const_iterator it =
210           registrations.begin();
211       it != registrations.end();
212       ++it) {
213    const ServiceWorkerRegistrationInfo& registration = *it;
214    DictionaryValue* registration_info = new DictionaryValue();
215    registration_info->SetString("scope", registration.pattern.spec());
216    registration_info->SetString(
217        "registration_id", base::Int64ToString(registration.registration_id));
218
219    if (!registration.active_version.is_null) {
220      DictionaryValue* active_info = new DictionaryValue();
221      UpdateVersionInfo(registration.active_version, active_info);
222      registration_info->Set("active", active_info);
223    }
224
225    if (!registration.waiting_version.is_null) {
226      DictionaryValue* waiting_info = new DictionaryValue();
227      UpdateVersionInfo(registration.waiting_version, waiting_info);
228      registration_info->Set("waiting", waiting_info);
229    }
230
231    result->Append(registration_info);
232  }
233  return result;
234}
235
236ListValue* GetVersionListValue(
237    const std::vector<ServiceWorkerVersionInfo>& versions) {
238  ListValue* result = new ListValue();
239  for (std::vector<ServiceWorkerVersionInfo>::const_iterator it =
240           versions.begin();
241       it != versions.end();
242       ++it) {
243    DictionaryValue* info = new DictionaryValue();
244    UpdateVersionInfo(*it, info);
245    result->Append(info);
246  }
247  return result;
248}
249
250void GetRegistrationsOnIOThread(
251    scoped_refptr<ServiceWorkerContextWrapper> context,
252    base::Callback<void(const std::vector<ServiceWorkerRegistrationInfo>&)>
253        callback) {
254  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
255  context->context()->storage()->GetAllRegistrations(callback);
256}
257
258void OnStoredRegistrations(
259    scoped_refptr<ServiceWorkerContextWrapper> context,
260    base::Callback<void(const std::vector<ServiceWorkerRegistrationInfo>&,
261                        const std::vector<ServiceWorkerVersionInfo>&,
262                        const std::vector<ServiceWorkerRegistrationInfo>&)>
263        callback,
264    const std::vector<ServiceWorkerRegistrationInfo>& stored_registrations) {
265  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
266  BrowserThread::PostTask(
267      BrowserThread::UI,
268      FROM_HERE,
269      base::Bind(callback,
270                 context->context()->GetAllLiveRegistrationInfo(),
271                 context->context()->GetAllLiveVersionInfo(),
272                 stored_registrations));
273}
274
275void OnAllRegistrations(
276    WeakPtr<ServiceWorkerInternalsUI> internals,
277    int partition_id,
278    const base::FilePath& context_path,
279    const std::vector<ServiceWorkerRegistrationInfo>& live_registrations,
280    const std::vector<ServiceWorkerVersionInfo>& live_versions,
281    const std::vector<ServiceWorkerRegistrationInfo>& stored_registrations) {
282  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283  if (!internals)
284    return;
285
286  ScopedVector<const Value> args;
287  args.push_back(GetRegistrationListValue(live_registrations));
288  args.push_back(GetVersionListValue(live_versions));
289  args.push_back(GetRegistrationListValue(stored_registrations));
290  args.push_back(new FundamentalValue(partition_id));
291  args.push_back(new StringValue(context_path.value()));
292  internals->web_ui()->CallJavascriptFunction("serviceworker.onPartitionData",
293                                              args.get());
294}
295
296}  // namespace
297
298class ServiceWorkerInternalsUI::PartitionObserver
299    : public ServiceWorkerContextObserver {
300 public:
301  PartitionObserver(int partition_id, WebUI* web_ui)
302      : partition_id_(partition_id), web_ui_(web_ui) {}
303  virtual ~PartitionObserver() {}
304  // ServiceWorkerContextObserver overrides:
305  virtual void OnWorkerStarted(int64 version_id,
306                               int process_id,
307                               int thread_id) OVERRIDE {
308    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
309    web_ui_->CallJavascriptFunction(
310        "serviceworker.onWorkerStarted",
311        FundamentalValue(partition_id_),
312        StringValue(base::Int64ToString(version_id)),
313        FundamentalValue(process_id),
314        FundamentalValue(thread_id));
315  }
316  virtual void OnWorkerStopped(int64 version_id,
317                               int process_id,
318                               int thread_id) OVERRIDE {
319    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
320    web_ui_->CallJavascriptFunction(
321        "serviceworker.onWorkerStopped",
322        FundamentalValue(partition_id_),
323        StringValue(base::Int64ToString(version_id)),
324        FundamentalValue(process_id),
325        FundamentalValue(thread_id));
326  }
327  virtual void OnVersionStateChanged(int64 version_id) OVERRIDE {
328    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
329    web_ui_->CallJavascriptFunction(
330        "serviceworker.onVersionStateChanged",
331        FundamentalValue(partition_id_),
332        StringValue(base::Int64ToString(version_id)));
333  }
334  virtual void OnErrorReported(int64 version_id,
335                               int process_id,
336                               int thread_id,
337                               const ErrorInfo& info) OVERRIDE {
338    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
339    ScopedVector<const Value> args;
340    args.push_back(new FundamentalValue(partition_id_));
341    args.push_back(new StringValue(base::Int64ToString(version_id)));
342    args.push_back(new FundamentalValue(process_id));
343    args.push_back(new FundamentalValue(thread_id));
344    scoped_ptr<DictionaryValue> value(new DictionaryValue());
345    value->SetString("message", info.error_message);
346    value->SetInteger("lineNumber", info.line_number);
347    value->SetInteger("columnNumber", info.column_number);
348    value->SetString("sourceURL", info.source_url.spec());
349    args.push_back(value.release());
350    web_ui_->CallJavascriptFunction("serviceworker.onErrorReported",
351                                    args.get());
352  }
353  virtual void OnReportConsoleMessage(int64 version_id,
354                                      int process_id,
355                                      int thread_id,
356                                      const ConsoleMessage& message) OVERRIDE {
357    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358    ScopedVector<const Value> args;
359    args.push_back(new FundamentalValue(partition_id_));
360    args.push_back(new StringValue(base::Int64ToString(version_id)));
361    args.push_back(new FundamentalValue(process_id));
362    args.push_back(new FundamentalValue(thread_id));
363    scoped_ptr<DictionaryValue> value(new DictionaryValue());
364    value->SetInteger("sourceIdentifier", message.source_identifier);
365    value->SetInteger("message_level", message.message_level);
366    value->SetString("message", message.message);
367    value->SetInteger("lineNumber", message.line_number);
368    value->SetString("sourceURL", message.source_url.spec());
369    args.push_back(value.release());
370    web_ui_->CallJavascriptFunction("serviceworker.onConsoleMessageReported",
371                                    args.get());
372  }
373  virtual void OnRegistrationStored(const GURL& pattern) OVERRIDE {
374    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
375    web_ui_->CallJavascriptFunction("serviceworker.onRegistrationStored",
376                                    StringValue(pattern.spec()));
377  }
378  virtual void OnRegistrationDeleted(const GURL& pattern) OVERRIDE {
379    web_ui_->CallJavascriptFunction("serviceworker.onRegistrationDeleted",
380                                    StringValue(pattern.spec()));
381  }
382  int partition_id() const { return partition_id_; }
383
384 private:
385  const int partition_id_;
386  WebUI* const web_ui_;
387};
388
389ServiceWorkerInternalsUI::ServiceWorkerInternalsUI(WebUI* web_ui)
390    : WebUIController(web_ui), next_partition_id_(0) {
391  WebUIDataSource* source =
392      WebUIDataSource::Create(kChromeUIServiceWorkerInternalsHost);
393  source->SetUseJsonJSFormatV2();
394  source->SetJsonPath("strings.js");
395  source->AddResourcePath("serviceworker_internals.js",
396                          IDR_SERVICE_WORKER_INTERNALS_JS);
397  source->AddResourcePath("serviceworker_internals.css",
398                          IDR_SERVICE_WORKER_INTERNALS_CSS);
399  source->SetDefaultResource(IDR_SERVICE_WORKER_INTERNALS_HTML);
400  source->DisableDenyXFrameOptions();
401
402  BrowserContext* browser_context =
403      web_ui->GetWebContents()->GetBrowserContext();
404  WebUIDataSource::Add(browser_context, source);
405
406  web_ui->RegisterMessageCallback(
407      "GetOptions",
408      base::Bind(&ServiceWorkerInternalsUI::GetOptions,
409                 base::Unretained(this)));
410  web_ui->RegisterMessageCallback(
411      "SetOption",
412      base::Bind(&ServiceWorkerInternalsUI::SetOption, base::Unretained(this)));
413  web_ui->RegisterMessageCallback(
414      "getAllRegistrations",
415      base::Bind(&ServiceWorkerInternalsUI::GetAllRegistrations,
416                 base::Unretained(this)));
417  web_ui->RegisterMessageCallback(
418      "stop",
419      base::Bind(&ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod,
420                 base::Unretained(this),
421                 &ServiceWorkerVersion::StopWorker));
422  web_ui->RegisterMessageCallback(
423      "sync",
424      base::Bind(&ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod,
425                 base::Unretained(this),
426                 &ServiceWorkerVersion::DispatchSyncEvent));
427  web_ui->RegisterMessageCallback(
428      "push",
429      base::Bind(&ServiceWorkerInternalsUI::DispatchPushEvent,
430                 base::Unretained(this)));
431  web_ui->RegisterMessageCallback(
432      "inspect",
433      base::Bind(&ServiceWorkerInternalsUI::InspectWorker,
434                 base::Unretained(this)));
435  web_ui->RegisterMessageCallback(
436      "unregister",
437      base::Bind(&ServiceWorkerInternalsUI::Unregister,
438                 base::Unretained(this)));
439  web_ui->RegisterMessageCallback(
440      "start",
441      base::Bind(&ServiceWorkerInternalsUI::StartWorker,
442                 base::Unretained(this)));
443}
444
445ServiceWorkerInternalsUI::~ServiceWorkerInternalsUI() {
446  BrowserContext* browser_context =
447      web_ui()->GetWebContents()->GetBrowserContext();
448  // Safe to use base::Unretained(this) because
449  // ForEachStoragePartition is synchronous.
450  BrowserContext::StoragePartitionCallback remove_observer_cb =
451      base::Bind(&ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition,
452                 base::Unretained(this));
453  BrowserContext::ForEachStoragePartition(browser_context, remove_observer_cb);
454}
455
456void ServiceWorkerInternalsUI::GetOptions(const ListValue* args) {
457  DictionaryValue options;
458  options.SetBoolean("debug_on_start",
459                     EmbeddedWorkerDevToolsManager::GetInstance()
460                         ->debug_service_worker_on_start());
461  web_ui()->CallJavascriptFunction("serviceworker.onOptions", options);
462}
463
464void ServiceWorkerInternalsUI::SetOption(const ListValue* args) {
465  std::string option_name;
466  bool option_boolean;
467  if (!args->GetString(0, &option_name) || option_name != "debug_on_start" ||
468      !args->GetBoolean(1, &option_boolean)) {
469    return;
470  }
471  EmbeddedWorkerDevToolsManager::GetInstance()
472      ->set_debug_service_worker_on_start(option_boolean);
473}
474
475void ServiceWorkerInternalsUI::GetAllRegistrations(const ListValue* args) {
476  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
477  BrowserContext* browser_context =
478      web_ui()->GetWebContents()->GetBrowserContext();
479  // Safe to use base::Unretained(this) because
480  // ForEachStoragePartition is synchronous.
481  BrowserContext::StoragePartitionCallback add_context_cb =
482      base::Bind(&ServiceWorkerInternalsUI::AddContextFromStoragePartition,
483                 base::Unretained(this));
484  BrowserContext::ForEachStoragePartition(browser_context, add_context_cb);
485}
486
487void ServiceWorkerInternalsUI::AddContextFromStoragePartition(
488    StoragePartition* partition) {
489  int partition_id = 0;
490  scoped_refptr<ServiceWorkerContextWrapper> context =
491      static_cast<ServiceWorkerContextWrapper*>(
492          partition->GetServiceWorkerContext());
493  if (PartitionObserver* observer =
494          observers_.get(reinterpret_cast<uintptr_t>(partition))) {
495    partition_id = observer->partition_id();
496  } else {
497    partition_id = next_partition_id_++;
498    scoped_ptr<PartitionObserver> new_observer(
499        new PartitionObserver(partition_id, web_ui()));
500    context->AddObserver(new_observer.get());
501    observers_.set(reinterpret_cast<uintptr_t>(partition), new_observer.Pass());
502  }
503
504  BrowserThread::PostTask(
505      BrowserThread::IO,
506      FROM_HERE,
507      base::Bind(GetRegistrationsOnIOThread,
508                 context,
509                 base::Bind(OnStoredRegistrations,
510                            context,
511                            base::Bind(OnAllRegistrations,
512                                       AsWeakPtr(),
513                                       partition_id,
514                                       context->is_incognito()
515                                           ? base::FilePath()
516                                           : partition->GetPath()))));
517}
518
519void ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition(
520    StoragePartition* partition) {
521  scoped_ptr<PartitionObserver> observer(
522      observers_.take_and_erase(reinterpret_cast<uintptr_t>(partition)));
523  if (!observer.get())
524    return;
525  scoped_refptr<ServiceWorkerContextWrapper> context =
526      static_cast<ServiceWorkerContextWrapper*>(
527          partition->GetServiceWorkerContext());
528  context->RemoveObserver(observer.get());
529}
530
531void ServiceWorkerInternalsUI::FindContext(
532    int partition_id,
533    StoragePartition** result_partition,
534    StoragePartition* storage_partition) const {
535  PartitionObserver* observer =
536      observers_.get(reinterpret_cast<uintptr_t>(storage_partition));
537  if (observer && partition_id == observer->partition_id()) {
538    *result_partition = storage_partition;
539  }
540}
541
542bool ServiceWorkerInternalsUI::GetServiceWorkerContext(
543    int partition_id,
544    scoped_refptr<ServiceWorkerContextWrapper>* context) const {
545  BrowserContext* browser_context =
546      web_ui()->GetWebContents()->GetBrowserContext();
547  StoragePartition* result_partition(NULL);
548  BrowserContext::StoragePartitionCallback find_context_cb =
549      base::Bind(&ServiceWorkerInternalsUI::FindContext,
550                 base::Unretained(this),
551                 partition_id,
552                 &result_partition);
553  BrowserContext::ForEachStoragePartition(browser_context, find_context_cb);
554  if (!result_partition)
555    return false;
556  *context = static_cast<ServiceWorkerContextWrapper*>(
557      result_partition->GetServiceWorkerContext());
558  return true;
559}
560
561void ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod(
562    ServiceWorkerVersionMethod method,
563    const ListValue* args) {
564  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
565  int callback_id;
566  const DictionaryValue* cmd_args = NULL;
567  int partition_id;
568  scoped_refptr<ServiceWorkerContextWrapper> context;
569  std::string version_id_string;
570  int64 version_id = 0;
571  if (!args->GetInteger(0, &callback_id) ||
572      !args->GetDictionary(1, &cmd_args) ||
573      !cmd_args->GetInteger("partition_id", &partition_id) ||
574      !GetServiceWorkerContext(partition_id, &context) ||
575      !cmd_args->GetString("version_id", &version_id_string) ||
576      !base::StringToInt64(version_id_string, &version_id)) {
577    return;
578  }
579
580  base::Callback<void(ServiceWorkerStatusCode)> callback =
581      base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
582  CallServiceWorkerVersionMethodWithVersionID(
583      method, context, version_id, callback);
584}
585
586void ServiceWorkerInternalsUI::DispatchPushEvent(
587    const ListValue* args) {
588  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
589  int callback_id;
590  int partition_id;
591  int64 version_id = 0;
592  std::string version_id_string;
593  const DictionaryValue* cmd_args = NULL;
594  scoped_refptr<ServiceWorkerContextWrapper> context;
595  if (!args->GetInteger(0, &callback_id) ||
596      !args->GetDictionary(1, &cmd_args) ||
597      !cmd_args->GetInteger("partition_id", &partition_id) ||
598      !GetServiceWorkerContext(partition_id, &context) ||
599      !cmd_args->GetString("version_id", &version_id_string) ||
600      !base::StringToInt64(version_id_string, &version_id)) {
601    return;
602  }
603
604  base::Callback<void(ServiceWorkerStatusCode)> callback =
605      base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
606  DispatchPushEventWithVersionID(context, version_id, callback);
607}
608
609void ServiceWorkerInternalsUI::InspectWorker(const ListValue* args) {
610  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
611  int callback_id;
612  const DictionaryValue* cmd_args = NULL;
613  int process_id = 0;
614  int devtools_agent_route_id = 0;
615  if (!args->GetInteger(0, &callback_id) ||
616      !args->GetDictionary(1, &cmd_args) ||
617      !cmd_args->GetInteger("process_id", &process_id) ||
618      !cmd_args->GetInteger("devtools_agent_route_id",
619                            &devtools_agent_route_id)) {
620    return;
621  }
622  base::Callback<void(ServiceWorkerStatusCode)> callback =
623      base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
624  scoped_refptr<DevToolsAgentHostImpl> agent_host(
625      EmbeddedWorkerDevToolsManager::GetInstance()
626          ->GetDevToolsAgentHostForWorker(process_id, devtools_agent_route_id));
627  if (!agent_host.get()) {
628    callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
629    return;
630  }
631  agent_host->Inspect(web_ui()->GetWebContents()->GetBrowserContext());
632  callback.Run(SERVICE_WORKER_OK);
633}
634
635void ServiceWorkerInternalsUI::Unregister(const ListValue* args) {
636  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
637  int callback_id;
638  int partition_id;
639  std::string scope_string;
640  const DictionaryValue* cmd_args = NULL;
641  scoped_refptr<ServiceWorkerContextWrapper> context;
642  if (!args->GetInteger(0, &callback_id) ||
643      !args->GetDictionary(1, &cmd_args) ||
644      !cmd_args->GetInteger("partition_id", &partition_id) ||
645      !GetServiceWorkerContext(partition_id, &context) ||
646      !cmd_args->GetString("scope", &scope_string)) {
647    return;
648  }
649
650  base::Callback<void(ServiceWorkerStatusCode)> callback =
651      base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
652  UnregisterWithScope(context, GURL(scope_string), callback);
653}
654
655void ServiceWorkerInternalsUI::StartWorker(const ListValue* args) {
656  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
657  int callback_id;
658  int partition_id;
659  std::string scope_string;
660  const DictionaryValue* cmd_args = NULL;
661  scoped_refptr<ServiceWorkerContextWrapper> context;
662  if (!args->GetInteger(0, &callback_id) ||
663      !args->GetDictionary(1, &cmd_args) ||
664      !cmd_args->GetInteger("partition_id", &partition_id) ||
665      !GetServiceWorkerContext(partition_id, &context) ||
666      !cmd_args->GetString("scope", &scope_string)) {
667    return;
668  }
669
670  base::Callback<void(ServiceWorkerStatusCode)> callback =
671      base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
672  FindRegistrationForPattern(
673      context, GURL(scope_string), base::Bind(StartActiveWorker, callback));
674}
675
676}  // namespace content
677