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..aa41cbfeeac30bb21dd8a2e7923b2cd79f23fe94 |
--- /dev/null |
+++ b/content/browser/service_worker/service_worker_internals_ui.cc |
@@ -0,0 +1,402 @@ |
+// 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. It deletes |
+// itself when complete. |
michaeln
2014/03/06 23:31:49
nit: the "it deletes itself" comment is more confu
alecflett
2014/03/06 23:46:47
Done.
|
+class ServiceWorkerInternalsUI::OperationProxy |
+ : public base::RefCountedThreadSafe< |
+ ServiceWorkerInternalsUI::OperationProxy> { |
michaeln
2014/03/06 23:31:49
nit: indent is off
alecflett
2014/03/06 23:46:47
Done.
|
+ 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.status) { |
+ case ServiceWorkerVersion::STOPPED: |
+ info->SetString("status", "STOPPED"); |
+ break; |
+ case EmbeddedWorkerInstance::STARTING: |
+ info->SetString("status", "STARTING"); |
+ break; |
+ case EmbeddedWorkerInstance::RUNNING: |
+ info->SetString("status", "RUNNING"); |
+ break; |
+ case EmbeddedWorkerInstance::STOPPING: |
+ info->SetString("status", "STOPPING"); |
+ break; |
+ } |
+ |
+ info->SetInteger("process_id", version.process_id); |
+ // is this hardware thread or internal thread? |
+ 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_) |
+ internals_->web_ui()->CallJavascriptFunction( |
michaeln
2014/03/06 23:31:49
nit: Maybe only do this part if the partition actu
alecflett
2014/03/06 23:46:47
Done.
|
+ "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 |