Home | History | Annotate | Download | only in service_worker
      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_manager_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/public/browser/browser_context.h"
     21 #include "content/public/browser/browser_thread.h"
     22 #include "content/public/browser/storage_partition.h"
     23 #include "content/public/browser/web_contents.h"
     24 #include "content/public/browser/web_ui.h"
     25 #include "content/public/browser/web_ui_data_source.h"
     26 #include "content/public/common/url_constants.h"
     27 #include "grit/content_resources.h"
     28 
     29 using base::DictionaryValue;
     30 using base::FundamentalValue;
     31 using base::ListValue;
     32 using base::StringValue;
     33 using base::Value;
     34 using base::WeakPtr;
     35 
     36 namespace content {
     37 
     38 namespace {
     39 
     40 void 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 
     59 void 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) {
     79     callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
     80     return;
     81   }
     82   (*version.*method)(callback);
     83 }
     84 
     85 void 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) {
    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 
    110 void 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 
    125 void WorkerStarted(const scoped_refptr<ServiceWorkerRegistration>& registration,
    126                    const ServiceWorkerInternalsUI::StatusCallback& callback,
    127                    ServiceWorkerStatusCode status) {
    128   callback.Run(status);
    129 }
    130 
    131 void 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 
    147 void 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 
    162 void 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::ACTIVE:
    193       info->SetString("status", "ACTIVE");
    194       break;
    195     case ServiceWorkerVersion::DEACTIVATED:
    196       info->SetString("status", "DEACTIVATED");
    197       break;
    198   }
    199   info->SetString("version_id", base::Int64ToString(version.version_id));
    200   info->SetInteger("process_id", version.process_id);
    201   info->SetInteger("thread_id", version.thread_id);
    202   info->SetInteger("devtools_agent_route_id", version.devtools_agent_route_id);
    203 }
    204 
    205 ListValue* GetRegistrationListValue(
    206     const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
    207   ListValue* result = new ListValue();
    208   for (std::vector<ServiceWorkerRegistrationInfo>::const_iterator it =
    209            registrations.begin();
    210        it != registrations.end();
    211        ++it) {
    212     const ServiceWorkerRegistrationInfo& registration = *it;
    213     DictionaryValue* registration_info = new DictionaryValue();
    214     registration_info->SetString("scope", registration.pattern.spec());
    215     registration_info->SetString("script_url", registration.script_url.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 
    236 ListValue* 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 
    250 void 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 
    258 void 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 
    275 void 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 
    298 class 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 
    389 ServiceWorkerInternalsUI::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 
    445 ServiceWorkerInternalsUI::~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 
    456 void 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 
    464 void 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 
    475 void 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 
    487 void 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   BrowserThread::PostTask(
    504       BrowserThread::IO,
    505       FROM_HERE,
    506       base::Bind(GetRegistrationsOnIOThread,
    507                  context,
    508                  base::Bind(OnStoredRegistrations,
    509                             context,
    510                             base::Bind(OnAllRegistrations,
    511                                        AsWeakPtr(),
    512                                        partition_id,
    513                                        partition->GetPath()))));
    514 }
    515 
    516 void ServiceWorkerInternalsUI::RemoveObserverFromStoragePartition(
    517     StoragePartition* partition) {
    518   scoped_ptr<PartitionObserver> observer(
    519       observers_.take_and_erase(reinterpret_cast<uintptr_t>(partition)));
    520   if (!observer.get())
    521     return;
    522   scoped_refptr<ServiceWorkerContextWrapper> context =
    523       static_cast<ServiceWorkerContextWrapper*>(
    524           partition->GetServiceWorkerContext());
    525   context->RemoveObserver(observer.get());
    526 }
    527 
    528 void ServiceWorkerInternalsUI::FindContext(
    529     int partition_id,
    530     StoragePartition** result_partition,
    531     StoragePartition* storage_partition) const {
    532   PartitionObserver* observer =
    533       observers_.get(reinterpret_cast<uintptr_t>(storage_partition));
    534   if (observer && partition_id == observer->partition_id()) {
    535     *result_partition = storage_partition;
    536   }
    537 }
    538 
    539 bool ServiceWorkerInternalsUI::GetServiceWorkerContext(
    540     int partition_id,
    541     scoped_refptr<ServiceWorkerContextWrapper>* context) const {
    542   BrowserContext* browser_context =
    543       web_ui()->GetWebContents()->GetBrowserContext();
    544   StoragePartition* result_partition(NULL);
    545   BrowserContext::StoragePartitionCallback find_context_cb =
    546       base::Bind(&ServiceWorkerInternalsUI::FindContext,
    547                  base::Unretained(this),
    548                  partition_id,
    549                  &result_partition);
    550   BrowserContext::ForEachStoragePartition(browser_context, find_context_cb);
    551   if (!result_partition)
    552     return false;
    553   *context = static_cast<ServiceWorkerContextWrapper*>(
    554       result_partition->GetServiceWorkerContext());
    555   return true;
    556 }
    557 
    558 void ServiceWorkerInternalsUI::CallServiceWorkerVersionMethod(
    559     ServiceWorkerVersionMethod method,
    560     const ListValue* args) {
    561   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    562   int callback_id;
    563   int partition_id;
    564   int64 version_id;
    565   std::string version_id_string;
    566   const DictionaryValue* cmd_args = NULL;
    567   scoped_refptr<ServiceWorkerContextWrapper> context;
    568   if (!args->GetInteger(0, &callback_id) ||
    569       !args->GetDictionary(1, &cmd_args) ||
    570       !cmd_args->GetInteger("partition_id", &partition_id) ||
    571       !GetServiceWorkerContext(partition_id, &context) ||
    572       !cmd_args->GetString("version_id", &version_id_string) ||
    573       !base::StringToInt64(version_id_string, &version_id)) {
    574     return;
    575   }
    576 
    577   base::Callback<void(ServiceWorkerStatusCode)> callback =
    578       base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
    579   CallServiceWorkerVersionMethodWithVersionID(
    580       method, context, version_id, callback);
    581 }
    582 
    583 void ServiceWorkerInternalsUI::DispatchPushEvent(
    584     const ListValue* args) {
    585   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    586   int callback_id;
    587   int partition_id;
    588   int64 version_id;
    589   std::string version_id_string;
    590   const DictionaryValue* cmd_args = NULL;
    591   scoped_refptr<ServiceWorkerContextWrapper> context;
    592   if (!args->GetInteger(0, &callback_id) ||
    593       !args->GetDictionary(1, &cmd_args) ||
    594       !cmd_args->GetInteger("partition_id", &partition_id) ||
    595       !GetServiceWorkerContext(partition_id, &context) ||
    596       !cmd_args->GetString("version_id", &version_id_string) ||
    597       !base::StringToInt64(version_id_string, &version_id)) {
    598     return;
    599   }
    600 
    601   base::Callback<void(ServiceWorkerStatusCode)> callback =
    602       base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
    603   DispatchPushEventWithVersionID(context, version_id, callback);
    604 }
    605 
    606 void ServiceWorkerInternalsUI::InspectWorker(const ListValue* args) {
    607   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    608   int callback_id;
    609   int process_id;
    610   int devtools_agent_route_id;
    611   const DictionaryValue* cmd_args = NULL;
    612   scoped_refptr<ServiceWorkerContextWrapper> context;
    613   if (!args->GetInteger(0, &callback_id) ||
    614       !args->GetDictionary(1, &cmd_args) ||
    615       !cmd_args->GetInteger("process_id", &process_id) ||
    616       !cmd_args->GetInteger("devtools_agent_route_id",
    617                             &devtools_agent_route_id)) {
    618     return;
    619   }
    620   base::Callback<void(ServiceWorkerStatusCode)> callback =
    621       base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
    622   scoped_refptr<DevToolsAgentHost> agent_host(
    623       EmbeddedWorkerDevToolsManager::GetInstance()
    624           ->GetDevToolsAgentHostForWorker(process_id, devtools_agent_route_id));
    625   if (!agent_host) {
    626     callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND);
    627     return;
    628   }
    629   DevToolsManagerImpl::GetInstance()->Inspect(
    630       web_ui()->GetWebContents()->GetBrowserContext(), agent_host.get());
    631   callback.Run(SERVICE_WORKER_OK);
    632 }
    633 
    634 void ServiceWorkerInternalsUI::Unregister(const ListValue* args) {
    635   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    636   int callback_id;
    637   int partition_id;
    638   std::string scope_string;
    639   const DictionaryValue* cmd_args = NULL;
    640   scoped_refptr<ServiceWorkerContextWrapper> context;
    641   if (!args->GetInteger(0, &callback_id) ||
    642       !args->GetDictionary(1, &cmd_args) ||
    643       !cmd_args->GetInteger("partition_id", &partition_id) ||
    644       !GetServiceWorkerContext(partition_id, &context) ||
    645       !cmd_args->GetString("scope", &scope_string)) {
    646     return;
    647   }
    648 
    649   base::Callback<void(ServiceWorkerStatusCode)> callback =
    650       base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
    651   UnregisterWithScope(context, GURL(scope_string), callback);
    652 }
    653 
    654 void ServiceWorkerInternalsUI::StartWorker(const ListValue* args) {
    655   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    656   int callback_id;
    657   int partition_id;
    658   std::string scope_string;
    659   const DictionaryValue* cmd_args = NULL;
    660   scoped_refptr<ServiceWorkerContextWrapper> context;
    661   if (!args->GetInteger(0, &callback_id) ||
    662       !args->GetDictionary(1, &cmd_args) ||
    663       !cmd_args->GetInteger("partition_id", &partition_id) ||
    664       !GetServiceWorkerContext(partition_id, &context) ||
    665       !cmd_args->GetString("scope", &scope_string)) {
    666     return;
    667   }
    668 
    669   base::Callback<void(ServiceWorkerStatusCode)> callback =
    670       base::Bind(OperationCompleteCallback, AsWeakPtr(), callback_id);
    671   FindRegistrationForPattern(
    672       context, GURL(scope_string), base::Bind(StartActiveWorker, callback));
    673 }
    674 
    675 }  // namespace content
    676