| Index: extensions/browser/service_worker_manager.cc
|
| diff --git a/extensions/browser/service_worker_manager.cc b/extensions/browser/service_worker_manager.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..0e30c93f7a34768e31be29cb5d74b52c466967eb
|
| --- /dev/null
|
| +++ b/extensions/browser/service_worker_manager.cc
|
| @@ -0,0 +1,294 @@
|
| +// Copyright 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 "extensions/browser/service_worker_manager.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "components/keyed_service/content/browser_context_dependency_manager.h"
|
| +#include "content/public/browser/browser_context.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "content/public/browser/render_process_host.h"
|
| +#include "content/public/browser/service_worker_context.h"
|
| +#include "content/public/browser/storage_partition.h"
|
| +#include "extensions/browser/extension_host.h"
|
| +#include "extensions/browser/extension_system.h"
|
| +#include "extensions/browser/extensions_browser_client.h"
|
| +#include "extensions/browser/process_manager.h"
|
| +#include "extensions/common/manifest_handlers/background_info.h"
|
| +
|
| +namespace extensions {
|
| +
|
| +using base::Callback;
|
| +using base::Closure;
|
| +using base::WeakPtr;
|
| +using content::BrowserContext;
|
| +using content::BrowserThread;
|
| +
|
| +ServiceWorkerManager::State::State() {}
|
| +ServiceWorkerManager::State::~State() {}
|
| +
|
| +ServiceWorkerManager::ServiceWorkerManager(BrowserContext* context)
|
| + : context_(context), weak_this_factory_(this) {}
|
| +ServiceWorkerManager::~ServiceWorkerManager() {}
|
| +
|
| +ServiceWorkerManager* ServiceWorkerManager::Get(
|
| + content::BrowserContext* context) {
|
| + return ServiceWorkerManagerFactory::GetForBrowserContext(context);
|
| +}
|
| +
|
| +content::StoragePartition* ServiceWorkerManager::GetStoragePartition(
|
| + const ExtensionId& ext_id) const {
|
| + return content::BrowserContext::GetStoragePartitionForSite(
|
| + context_, Extension::GetBaseURLFromExtensionId(ext_id));
|
| +}
|
| +
|
| +content::ServiceWorkerContext* ServiceWorkerManager::GetSWContext(
|
| + const ExtensionId& ext_id) const {
|
| + return GetStoragePartition(ext_id)->GetServiceWorkerContext();
|
| +}
|
| +
|
| +// alecflett says that if we send a series of RegisterServiceWorker and
|
| +// UnregisterServiceWorker calls on the same scope to a
|
| +// ServiceWorkerContextCore, we're guaranteed that the callbacks come back in
|
| +// the same order, and that the last one will be the final state.
|
| +void ServiceWorkerManager::RegisterExtension(const Extension* extension) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + CHECK(BackgroundInfo::HasServiceWorker(extension));
|
| + State& ext_state = states_[extension->id()];
|
| + if (ext_state.registration == REGISTERING ||
|
| + ext_state.registration == REGISTERED)
|
| + return;
|
| + ext_state.registration = REGISTERING;
|
| + ++ext_state.outstanding_state_changes;
|
| + const GURL service_worker_script = extension->GetResourceURL(
|
| + BackgroundInfo::GetServiceWorkerScript(extension));
|
| + // TODO(jyasskin): Create the extension process in a cleaner way. We don't
|
| + // need a view, for instance. Using the service_worker_script as the
|
| + // background host is just totally horrible.
|
| + extensions::ProcessManager* process_manager =
|
| + ExtensionSystem::Get(context_)->process_manager();
|
| + CHECK(process_manager->CreateBackgroundHost(
|
| + extension,
|
| + service_worker_script,
|
| + base::Bind(&ServiceWorkerManager::ContinueRegistrationWithExtensionHost,
|
| + WeakThis(),
|
| + extension->id(),
|
| + extension->GetResourceURL("/*"),
|
| + service_worker_script)));
|
| +}
|
| +
|
| +void ServiceWorkerManager::ContinueRegistrationWithExtensionHost(
|
| + const ExtensionId& extension_id,
|
| + const GURL& scope,
|
| + const GURL& service_worker_script) {
|
| + extensions::ProcessManager* process_manager =
|
| + ExtensionSystem::Get(context_)->process_manager();
|
| + ExtensionHost* host =
|
| + process_manager->GetBackgroundHostForExtension(extension_id);
|
| +
|
| + GetSWContext(extension_id)->RegisterServiceWorker(
|
| + scope,
|
| + service_worker_script,
|
| + host->render_process_host()->GetID(),
|
| + base::Bind(
|
| + &ServiceWorkerManager::FinishRegistration, WeakThis(), extension_id));
|
| +}
|
| +
|
| +void ServiceWorkerManager::FinishRegistration(const ExtensionId& extension_id,
|
| + bool success) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + State& ext_state = states_[extension_id];
|
| + --ext_state.outstanding_state_changes;
|
| + DCHECK_GE(ext_state.outstanding_state_changes, 0);
|
| + if (ext_state.outstanding_state_changes > 0)
|
| + return;
|
| +
|
| + DCHECK_EQ(ext_state.registration, REGISTERING);
|
| + std::vector<Closure> to_run;
|
| + if (success) {
|
| + ext_state.registration = REGISTERED;
|
| + to_run.swap(ext_state.registration_succeeded);
|
| + ext_state.registration_failed.clear();
|
| + } else {
|
| + LOG(ERROR) << "Service Worker Registration failed for extension "
|
| + << extension_id;
|
| + to_run.swap(ext_state.registration_failed);
|
| + states_.erase(extension_id);
|
| + }
|
| +
|
| + for (size_t i = 0; i < to_run.size(); ++i) {
|
| + to_run[i].Run();
|
| + }
|
| +}
|
| +
|
| +void ServiceWorkerManager::UnregisterExtension(const Extension* extension) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + CHECK(BackgroundInfo::HasServiceWorker(extension));
|
| +
|
| + base::hash_map<ExtensionId, State>::iterator it =
|
| + states_.find(extension->id());
|
| + if (it == states_.end()) {
|
| + // Extension isn't registered.
|
| + return;
|
| + }
|
| + State& ext_state = it->second;
|
| + if (ext_state.registration == UNREGISTERING)
|
| + return;
|
| +
|
| + ext_state.registration = UNREGISTERING;
|
| + ++ext_state.outstanding_state_changes;
|
| +
|
| + const GURL service_worker_script = extension->GetResourceURL(
|
| + BackgroundInfo::GetServiceWorkerScript(extension));
|
| + // TODO(jyasskin): Create the extension process in a cleaner way. We don't
|
| + // need a view, for instance. Using the service_worker_script as the
|
| + // background host is just totally horrible.
|
| + extensions::ProcessManager* process_manager =
|
| + ExtensionSystem::Get(context_)->process_manager();
|
| + CHECK(process_manager->CreateBackgroundHost(
|
| + extension,
|
| + service_worker_script,
|
| + base::Bind(&ServiceWorkerManager::ContinueUnregistrationWithExtensionHost,
|
| + WeakThis(),
|
| + extension->id(),
|
| + extension->GetResourceURL("/*"))));
|
| +}
|
| +
|
| +void ServiceWorkerManager::ContinueUnregistrationWithExtensionHost(
|
| + const ExtensionId& extension_id,
|
| + const GURL& scope) {
|
| + extensions::ProcessManager* process_manager =
|
| + ExtensionSystem::Get(context_)->process_manager();
|
| + ExtensionHost* host =
|
| + process_manager->GetBackgroundHostForExtension(extension_id);
|
| +
|
| + GetSWContext(extension_id)->UnregisterServiceWorker(
|
| + scope,
|
| + host->render_process_host()->GetID(),
|
| + base::Bind(&ServiceWorkerManager::FinishUnregistration,
|
| + WeakThis(),
|
| + extension_id));
|
| +}
|
| +
|
| +void ServiceWorkerManager::FinishUnregistration(const ExtensionId& extension_id,
|
| + bool success) {
|
| + DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| + State& ext_state = states_[extension_id];
|
| + --ext_state.outstanding_state_changes;
|
| + DCHECK_GE(ext_state.outstanding_state_changes, 0);
|
| + if (ext_state.outstanding_state_changes > 0)
|
| + return;
|
| +
|
| + DCHECK_EQ(ext_state.registration, UNREGISTERING);
|
| + std::vector<Closure> to_run;
|
| + if (success) {
|
| + to_run.swap(ext_state.unregistration_succeeded);
|
| + states_.erase(extension_id);
|
| + } else {
|
| + LOG(ERROR) << "Service Worker Unregistration failed for extension "
|
| + << extension_id;
|
| + ext_state.registration = REGISTERED;
|
| + to_run.swap(ext_state.unregistration_failed);
|
| + ext_state.unregistration_succeeded.clear();
|
| + }
|
| +
|
| + for (size_t i = 0; i < to_run.size(); ++i) {
|
| + to_run[i].Run();
|
| + }
|
| +}
|
| +
|
| +void ServiceWorkerManager::WhenRegistered(
|
| + const Extension* extension,
|
| + const tracked_objects::Location& from_here,
|
| + const base::Closure& success,
|
| + const base::Closure& failure) {
|
| + base::hash_map<ExtensionId, State>::iterator it =
|
| + states_.find(extension->id());
|
| + if (it == states_.end())
|
| + base::MessageLoop::current()->PostTask(from_here, failure);
|
| +
|
| + State& state = it->second;
|
| + switch (state.registration) {
|
| + case UNREGISTERED:
|
| + case UNREGISTERING:
|
| + base::MessageLoop::current()->PostTask(from_here, failure);
|
| + break;
|
| + case REGISTERED:
|
| + base::MessageLoop::current()->PostTask(from_here, success);
|
| + break;
|
| + case REGISTERING:
|
| + state.registration_succeeded.push_back(success);
|
| + state.registration_failed.push_back(failure);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void ServiceWorkerManager::WhenUnregistered(
|
| + const Extension* extension,
|
| + const tracked_objects::Location& from_here,
|
| + const base::Closure& success,
|
| + const base::Closure& failure) {
|
| + base::hash_map<ExtensionId, State>::iterator it =
|
| + states_.find(extension->id());
|
| + if (it == states_.end())
|
| + base::MessageLoop::current()->PostTask(from_here, success);
|
| +
|
| + State& state = it->second;
|
| + switch(state.registration) {
|
| + case REGISTERED:
|
| + case REGISTERING:
|
| + base::MessageLoop::current()->PostTask(from_here, failure);
|
| + break;
|
| + case UNREGISTERED:
|
| + base::MessageLoop::current()->PostTask(from_here, success);
|
| + break;
|
| + case UNREGISTERING:
|
| + state.unregistration_succeeded.push_back(success);
|
| + state.unregistration_failed.push_back(failure);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +WeakPtr<ServiceWorkerManager> ServiceWorkerManager::WeakThis() {
|
| + return weak_this_factory_.GetWeakPtr();
|
| +}
|
| +
|
| +// ServiceWorkerManagerFactory
|
| +
|
| +ServiceWorkerManager* ServiceWorkerManagerFactory::GetForBrowserContext(
|
| + content::BrowserContext* context) {
|
| + return static_cast<ServiceWorkerManager*>(
|
| + GetInstance()->GetServiceForBrowserContext(context, true));
|
| +}
|
| +
|
| +ServiceWorkerManagerFactory* ServiceWorkerManagerFactory::GetInstance() {
|
| + return Singleton<ServiceWorkerManagerFactory>::get();
|
| +}
|
| +
|
| +void ServiceWorkerManagerFactory::SetInstanceForTesting(
|
| + content::BrowserContext* context,
|
| + ServiceWorkerManager* manager) {
|
| + Associate(context, manager);
|
| +}
|
| +
|
| +ServiceWorkerManagerFactory::ServiceWorkerManagerFactory()
|
| + : BrowserContextKeyedServiceFactory(
|
| + "ServiceWorkerManager",
|
| + BrowserContextDependencyManager::GetInstance()) {}
|
| +
|
| +ServiceWorkerManagerFactory::~ServiceWorkerManagerFactory() {}
|
| +
|
| +KeyedService* ServiceWorkerManagerFactory::BuildServiceInstanceFor(
|
| + content::BrowserContext* context) const {
|
| + return new ServiceWorkerManager(context);
|
| +}
|
| +
|
| +// TODO(jyasskin): Deal with incognito mode.
|
| +content::BrowserContext* ServiceWorkerManagerFactory::GetBrowserContextToUse(
|
| + content::BrowserContext* context) const {
|
| + return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
|
| +}
|
| +
|
| +} // namespace extensions
|
|
|