| Index: content/browser/service_worker/service_worker_internals_ui.cc
|
| diff --git a/content/browser/service_worker/service_worker_internals_ui.cc b/content/browser/service_worker/service_worker_internals_ui.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..31789f9e49860b393b5b896aeaad5e295f63dabb
|
| --- /dev/null
|
| +++ b/content/browser/service_worker/service_worker_internals_ui.cc
|
| @@ -0,0 +1,421 @@
|
| +// Copyright (c) 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "content/browser/service_worker/service_worker_internals_ui.h"
|
| +
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/values.h"
|
| +#include "content/browser/service_worker/service_worker_context_core.h"
|
| +#include "content/browser/service_worker/service_worker_context_wrapper.h"
|
| +#include "content/browser/service_worker/service_worker_registration.h"
|
| +#include "content/browser/service_worker/service_worker_version.h"
|
| +#include "content/public/browser/browser_context.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/storage_partition.h"
|
| +#include "content/public/browser/web_contents.h"
|
| +#include "content/public/browser/web_ui.h"
|
| +#include "content/public/browser/web_ui_data_source.h"
|
| +#include "content/public/common/url_constants.h"
|
| +#include "grit/content_resources.h"
|
| +
|
| +using base::DictionaryValue;
|
| +using base::FundamentalValue;
|
| +using base::ListValue;
|
| +using base::StringValue;
|
| +using base::Value;
|
| +using base::WeakPtr;
|
| +
|
| +namespace content {
|
| +
|
| +// This class proxies calls to the ServiceWorker APIs on the IO
|
| +// thread, and then calls back JavaScript on the UI thread.
|
| +class ServiceWorkerInternalsUI::OperationProxy
|
| + : public base::RefCountedThreadSafe<
|
| + ServiceWorkerInternalsUI::OperationProxy> {
|
| + public:
|
| + OperationProxy(const WeakPtr<ServiceWorkerInternalsUI> internals,
|
| + scoped_ptr<ListValue> original_args)
|
| + : internals_(internals), original_args_(original_args.Pass()) {}
|
| +
|
| + void GetRegistrationsOnIOThread(ServiceWorkerContextWrapper* context,
|
| + const base::FilePath& context_path);
|
| + void UnregisterOnIOThread(scoped_refptr<ServiceWorkerContextWrapper> context,
|
| + const GURL& scope);
|
| + void StartWorkerOnIOThread(scoped_refptr<ServiceWorkerContextWrapper> context,
|
| + const GURL& scope);
|
| + void StopWorkerOnIOThread(scoped_refptr<ServiceWorkerContextWrapper> context,
|
| + const GURL& scope);
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<OperationProxy>;
|
| + ~OperationProxy() {}
|
| + void OnHaveRegistrations(
|
| + const base::FilePath& context_path,
|
| + const std::vector<ServiceWorkerRegistrationInfo>& registrations);
|
| +
|
| + void OperationComplete(ServiceWorkerStatusCode status);
|
| +
|
| + void StartActiveWorker(
|
| + ServiceWorkerStatusCode status,
|
| + const scoped_refptr<ServiceWorkerRegistration>& registration);
|
| +
|
| + void StopActiveWorker(
|
| + ServiceWorkerStatusCode status,
|
| + const scoped_refptr<ServiceWorkerRegistration>& registration);
|
| +
|
| + WeakPtr<ServiceWorkerInternalsUI> internals_;
|
| + scoped_ptr<ListValue> original_args_;
|
| +};
|
| +
|
| +ServiceWorkerInternalsUI::ServiceWorkerInternalsUI(WebUI* web_ui)
|
| + : WebUIController(web_ui) {
|
| + WebUIDataSource* source =
|
| + WebUIDataSource::Create(kChromeUIServiceWorkerInternalsHost);
|
| + source->SetUseJsonJSFormatV2();
|
| + source->SetJsonPath("strings.js");
|
| + source->AddResourcePath("serviceworker_internals.js",
|
| + IDR_SERVICE_WORKER_INTERNALS_JS);
|
| + source->AddResourcePath("serviceworker_internals.css",
|
| + IDR_SERVICE_WORKER_INTERNALS_CSS);
|
| + source->SetDefaultResource(IDR_SERVICE_WORKER_INTERNALS_HTML);
|
| +
|
| + BrowserContext* browser_context =
|
| + web_ui->GetWebContents()->GetBrowserContext();
|
| + WebUIDataSource::Add(browser_context, source);
|
| +
|
| + web_ui->RegisterMessageCallback(
|
| + "getAllRegistrations",
|
| + base::Bind(&ServiceWorkerInternalsUI::GetAllRegistrations,
|
| + base::Unretained(this)));
|
| + web_ui->RegisterMessageCallback(
|
| + "start",
|
| + base::Bind(&ServiceWorkerInternalsUI::StartWorker,
|
| + base::Unretained(this)));
|
| + web_ui->RegisterMessageCallback(
|
| + "stop",
|
| + base::Bind(&ServiceWorkerInternalsUI::StopWorker,
|
| + base::Unretained(this)));
|
| + web_ui->RegisterMessageCallback(
|
| + "unregister",
|
| + base::Bind(&ServiceWorkerInternalsUI::Unregister,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +ServiceWorkerInternalsUI::~ServiceWorkerInternalsUI() {}
|
| +
|
| +void ServiceWorkerInternalsUI::GetAllRegistrations(const ListValue* args) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + BrowserContext* browser_context =
|
| + web_ui()->GetWebContents()->GetBrowserContext();
|
| +
|
| + // Safe to use base::Unretained(this) because
|
| + // ForEachStoragePartition is synchronous.
|
| + BrowserContext::StoragePartitionCallback cb =
|
| + base::Bind(&ServiceWorkerInternalsUI::AddContextFromStoragePartition,
|
| + base::Unretained(this));
|
| + BrowserContext::ForEachStoragePartition(browser_context, cb);
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::AddContextFromStoragePartition(
|
| + StoragePartition* partition) {
|
| + scoped_refptr<ServiceWorkerContextWrapper> context =
|
| + partition->GetServiceWorkerContext();
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(
|
| + &ServiceWorkerInternalsUI::OperationProxy::GetRegistrationsOnIOThread,
|
| + new OperationProxy(AsWeakPtr(), scoped_ptr<ListValue>()),
|
| + context,
|
| + partition->GetPath()));
|
| +}
|
| +
|
| +namespace {
|
| +void FindContext(const base::FilePath& partition_path,
|
| + StoragePartition** result_partition,
|
| + scoped_refptr<ServiceWorkerContextWrapper>* result_context,
|
| + StoragePartition* storage_partition) {
|
| + if (storage_partition->GetPath() == partition_path) {
|
| + *result_partition = storage_partition;
|
| + *result_context = storage_partition->GetServiceWorkerContext();
|
| + }
|
| +}
|
| +} // namespace
|
| +
|
| +bool ServiceWorkerInternalsUI::GetRegistrationInfo(
|
| + const ListValue* args,
|
| + base::FilePath* partition_path,
|
| + GURL* scope,
|
| + scoped_refptr<ServiceWorkerContextWrapper>* context) const {
|
| + base::FilePath::StringType path_string;
|
| + if (!args->GetString(0, &path_string))
|
| + return false;
|
| + *partition_path = base::FilePath(path_string);
|
| +
|
| + std::string scope_string;
|
| + if (!args->GetString(1, &scope_string))
|
| + return false;
|
| + *scope = GURL(scope_string);
|
| +
|
| + BrowserContext* browser_context =
|
| + web_ui()->GetWebContents()->GetBrowserContext();
|
| +
|
| + StoragePartition* result_partition(NULL);
|
| + BrowserContext::StoragePartitionCallback cb =
|
| + base::Bind(&FindContext, *partition_path, &result_partition, context);
|
| + BrowserContext::ForEachStoragePartition(browser_context, cb);
|
| +
|
| + if (!result_partition || !(*context))
|
| + return false;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::Unregister(const ListValue* args) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + base::FilePath partition_path;
|
| + GURL scope;
|
| + scoped_refptr<ServiceWorkerContextWrapper> context;
|
| + if (!GetRegistrationInfo(args, &partition_path, &scope, &context))
|
| + return;
|
| +
|
| + scoped_ptr<ListValue> args_copy(args->DeepCopy());
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(
|
| + &ServiceWorkerInternalsUI::OperationProxy::UnregisterOnIOThread,
|
| + new OperationProxy(AsWeakPtr(), args_copy.Pass()),
|
| + context,
|
| + scope));
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::StartWorker(const ListValue* args) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + base::FilePath partition_path;
|
| + GURL scope;
|
| + scoped_refptr<ServiceWorkerContextWrapper> context;
|
| + if (!GetRegistrationInfo(args, &partition_path, &scope, &context))
|
| + return;
|
| +
|
| + scoped_ptr<ListValue> args_copy(args->DeepCopy());
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(
|
| + &ServiceWorkerInternalsUI::OperationProxy::StartWorkerOnIOThread,
|
| + new OperationProxy(AsWeakPtr(), args_copy.Pass()),
|
| + context,
|
| + scope));
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::StopWorker(const ListValue* args) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + base::FilePath partition_path;
|
| + GURL scope;
|
| + scoped_refptr<ServiceWorkerContextWrapper> context;
|
| + if (!GetRegistrationInfo(args, &partition_path, &scope, &context))
|
| + return;
|
| +
|
| + scoped_ptr<ListValue> args_copy(args->DeepCopy());
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(
|
| + &ServiceWorkerInternalsUI::OperationProxy::StopWorkerOnIOThread,
|
| + new OperationProxy(AsWeakPtr(), args_copy.Pass()),
|
| + context,
|
| + scope));
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::OperationProxy::GetRegistrationsOnIOThread(
|
| + ServiceWorkerContextWrapper* context,
|
| + const base::FilePath& context_path) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| +
|
| + context->context()->storage()->GetAllRegistrations(
|
| + base::Bind(&ServiceWorkerInternalsUI::OperationProxy::OnHaveRegistrations,
|
| + this,
|
| + context_path));
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::OperationProxy::UnregisterOnIOThread(
|
| + scoped_refptr<ServiceWorkerContextWrapper> context,
|
| + const GURL& scope) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + context->context()->UnregisterServiceWorker(
|
| + scope,
|
| + 0, // render process id?
|
| + base::Bind(&ServiceWorkerInternalsUI::OperationProxy::OperationComplete,
|
| + this));
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::OperationProxy::StartWorkerOnIOThread(
|
| + scoped_refptr<ServiceWorkerContextWrapper> context,
|
| + const GURL& scope) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + // TODO(alecflett): Add support for starting/stopping workers for
|
| + // pending versions too.
|
| + context->context()->storage()->FindRegistrationForPattern(
|
| + scope,
|
| + base::Bind(&ServiceWorkerInternalsUI::OperationProxy::StartActiveWorker,
|
| + this));
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::OperationProxy::StopWorkerOnIOThread(
|
| + scoped_refptr<ServiceWorkerContextWrapper> context,
|
| + const GURL& scope) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + // TODO(alecflett): Add support for starting/stopping workers for
|
| + // pending versions too.
|
| + context->context()->storage()->FindRegistrationForPattern(
|
| + scope,
|
| + base::Bind(&ServiceWorkerInternalsUI::OperationProxy::StopActiveWorker,
|
| + this));
|
| +}
|
| +
|
| +namespace {
|
| +void UpdateVersionInfo(const ServiceWorkerVersionInfo& version,
|
| + DictionaryValue* info) {
|
| + switch (version.running_status) {
|
| + case ServiceWorkerVersion::STOPPED:
|
| + info->SetString("running_status", "STOPPED");
|
| + break;
|
| + case ServiceWorkerVersion::STARTING:
|
| + info->SetString("running_status", "STARTING");
|
| + break;
|
| + case ServiceWorkerVersion::RUNNING:
|
| + info->SetString("running_status", "RUNNING");
|
| + break;
|
| + case ServiceWorkerVersion::STOPPING:
|
| + info->SetString("running_status", "STOPPING");
|
| + break;
|
| + }
|
| +
|
| + switch (version.status) {
|
| + case ServiceWorkerVersion::NEW:
|
| + info->SetString("status", "NEW");
|
| + break;
|
| + case ServiceWorkerVersion::INSTALLING:
|
| + info->SetString("status", "INSTALLING");
|
| + break;
|
| + case ServiceWorkerVersion::INSTALLED:
|
| + info->SetString("status", "INSTALLED");
|
| + break;
|
| + case ServiceWorkerVersion::ACTIVATING:
|
| + info->SetString("status", "ACTIVATING");
|
| + break;
|
| + case ServiceWorkerVersion::ACTIVE:
|
| + info->SetString("status", "ACTIVE");
|
| + break;
|
| + case ServiceWorkerVersion::DEACTIVATED:
|
| + info->SetString("status", "DEACTIVATED");
|
| + break;
|
| + }
|
| +
|
| + info->SetInteger("process_id", version.process_id);
|
| + info->SetInteger("thread_id", version.thread_id);
|
| +}
|
| +} // namespace
|
| +
|
| +void ServiceWorkerInternalsUI::OperationProxy::OnHaveRegistrations(
|
| + const base::FilePath& context_path,
|
| + const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
|
| + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI,
|
| + FROM_HERE,
|
| + base::Bind(
|
| + &ServiceWorkerInternalsUI::OperationProxy::OnHaveRegistrations,
|
| + this,
|
| + context_path,
|
| + registrations));
|
| + return;
|
| + }
|
| +
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + ListValue result;
|
| + for (std::vector<ServiceWorkerRegistrationInfo>::const_iterator it =
|
| + registrations.begin();
|
| + it != registrations.end();
|
| + ++it) {
|
| + const ServiceWorkerRegistrationInfo& registration = *it;
|
| + DictionaryValue* registration_info = new DictionaryValue();
|
| + registration_info->SetString("scope", registration.pattern.spec());
|
| + registration_info->SetString("script_url", registration.script_url.spec());
|
| +
|
| + if (!registration.active_version.is_null) {
|
| + DictionaryValue* active_info = new DictionaryValue();
|
| + UpdateVersionInfo(registration.active_version, active_info);
|
| + registration_info->Set("active", active_info);
|
| + }
|
| +
|
| + if (!registration.pending_version.is_null) {
|
| + DictionaryValue* pending_info = new DictionaryValue();
|
| + UpdateVersionInfo(registration.active_version, pending_info);
|
| + registration_info->Set("pending", pending_info);
|
| + }
|
| +
|
| + result.Append(registration_info);
|
| + }
|
| +
|
| + if (internals_ && !result.empty())
|
| + internals_->web_ui()->CallJavascriptFunction(
|
| + "serviceworker.onPartitionData",
|
| + result,
|
| + StringValue(context_path.value()));
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::OperationProxy::OperationComplete(
|
| + ServiceWorkerStatusCode status) {
|
| + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI,
|
| + FROM_HERE,
|
| + base::Bind(&ServiceWorkerInternalsUI::OperationProxy::OperationComplete,
|
| + this,
|
| + status));
|
| + return;
|
| + }
|
| +
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + original_args_->Insert(0, new FundamentalValue(static_cast<int>(status)));
|
| + if (internals_)
|
| + internals_->web_ui()->CallJavascriptFunction(
|
| + "serviceworker.onOperationComplete",
|
| + std::vector<const Value*>(original_args_->begin(),
|
| + original_args_->end()));
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::OperationProxy::StartActiveWorker(
|
| + ServiceWorkerStatusCode status,
|
| + const scoped_refptr<ServiceWorkerRegistration>& registration) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + if (status == SERVICE_WORKER_OK) {
|
| + registration->active_version()->StartWorker(base::Bind(
|
| + &ServiceWorkerInternalsUI::OperationProxy::OperationComplete, this));
|
| + return;
|
| + }
|
| +
|
| + OperationComplete(status);
|
| +}
|
| +
|
| +void ServiceWorkerInternalsUI::OperationProxy::StopActiveWorker(
|
| + ServiceWorkerStatusCode status,
|
| + const scoped_refptr<ServiceWorkerRegistration>& registration) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + if (status == SERVICE_WORKER_OK) {
|
| + registration->active_version()->StopWorker(base::Bind(
|
| + &ServiceWorkerInternalsUI::OperationProxy::OperationComplete, this));
|
| + return;
|
| + }
|
| +
|
| + OperationComplete(status);
|
| +}
|
| +
|
| +} // namespace content
|
|
|