// 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 "chrome/browser/extensions/extension_gcm_app_handler.h" #include "base/bind.h" #include "base/lazy_instance.h" #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/thread_task_runner_handle.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/gcm/gcm_api.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/services/gcm/gcm_profile_service.h" #include "chrome/browser/services/gcm/gcm_profile_service_factory.h" #include "chrome/browser/services/gcm/instance_id/instance_id_profile_service.h" #include "chrome/browser/services/gcm/instance_id/instance_id_profile_service_factory.h" #include "components/gcm_driver/gcm_driver.h" #include "components/gcm_driver/instance_id/instance_id_driver.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/common/extension.h" #include "extensions/common/permissions/permissions_data.h" namespace extensions { namespace { const char kDummyAppId[] = "extension.guard.dummy.id"; base::LazyInstance > g_factory = LAZY_INSTANCE_INITIALIZER; bool IsGCMPermissionEnabled(const Extension* extension) { return extension->permissions_data()->HasAPIPermission(APIPermission::kGcm); } } // namespace // static BrowserContextKeyedAPIFactory* ExtensionGCMAppHandler::GetFactoryInstance() { return g_factory.Pointer(); } ExtensionGCMAppHandler::ExtensionGCMAppHandler(content::BrowserContext* context) : profile_(Profile::FromBrowserContext(context)), extension_registry_observer_(this), weak_factory_(this) { extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); js_event_router_.reset(new extensions::GcmJsEventRouter(profile_)); } ExtensionGCMAppHandler::~ExtensionGCMAppHandler() { const ExtensionSet& enabled_extensions = ExtensionRegistry::Get(profile_)->enabled_extensions(); for (ExtensionSet::const_iterator extension = enabled_extensions.begin(); extension != enabled_extensions.end(); ++extension) { if (IsGCMPermissionEnabled(extension->get())) GetGCMDriver()->RemoveAppHandler((*extension)->id()); } } void ExtensionGCMAppHandler::ShutdownHandler() { js_event_router_.reset(); } void ExtensionGCMAppHandler::OnMessage(const std::string& app_id, const gcm::IncomingMessage& message) { js_event_router_->OnMessage(app_id, message); } void ExtensionGCMAppHandler::OnMessagesDeleted(const std::string& app_id) { js_event_router_->OnMessagesDeleted(app_id); } void ExtensionGCMAppHandler::OnSendError( const std::string& app_id, const gcm::GCMClient::SendErrorDetails& send_error_details) { js_event_router_->OnSendError(app_id, send_error_details); } void ExtensionGCMAppHandler::OnSendAcknowledged( const std::string& app_id, const std::string& message_id) { // This event is not exposed to JS API. It terminates here. } void ExtensionGCMAppHandler::OnExtensionLoaded( content::BrowserContext* browser_context, const Extension* extension) { if (IsGCMPermissionEnabled(extension)) AddAppHandler(extension->id()); } void ExtensionGCMAppHandler::OnExtensionUnloaded( content::BrowserContext* browser_context, const Extension* extension, UnloadedExtensionInfo::Reason reason) { if (!IsGCMPermissionEnabled(extension)) return; if (reason == UnloadedExtensionInfo::REASON_UPDATE && GetGCMDriver()->app_handlers().size() >= 1) { // When the extension is being updated, it will be first unloaded and then // loaded again by ExtensionService::AddExtension. If the app handler for // this extension is the only handler, removing it and adding it again will // cause the GCM service being stopped and restarted unnecessarily. To work // around this, we add a dummy app handler to guard against it. This dummy // app handler will be removed once the extension loading logic is done. // // Note that this dummy app handler is added when there is at least one // handler. This is because there might be a built-in app handler, like // GCMAccountMapper, which is automatically added and removed by // GCMDriverDesktop. // // Also note that the GCM message routing will not be interruptted during // the update process since unloading and reloading extension are done in // the single function ExtensionService::AddExtension. AddDummyAppHandler(); base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&ExtensionGCMAppHandler::RemoveDummyAppHandler, weak_factory_.GetWeakPtr())); } // When the extention is being uninstalled, it will be unloaded first. We // should not remove the app handler in this case and it will be handled // in OnExtensionUninstalled. if (reason != UnloadedExtensionInfo::REASON_UNINSTALL) RemoveAppHandler(extension->id()); } void ExtensionGCMAppHandler::OnExtensionUninstalled( content::BrowserContext* browser_context, const Extension* extension, extensions::UninstallReason reason) { if (IsGCMPermissionEnabled(extension)) { // Let's first remove InstanceID data. GCM unregistration will be triggered // after the asynchronous call is returned in OnDeleteIDCompleted. GetInstanceIDDriver()->GetInstanceID(extension->id())->DeleteID( base::Bind(&ExtensionGCMAppHandler::OnDeleteIDCompleted, weak_factory_.GetWeakPtr(), extension->id())); } } void ExtensionGCMAppHandler::AddDummyAppHandler() { AddAppHandler(kDummyAppId); } void ExtensionGCMAppHandler::RemoveDummyAppHandler() { RemoveAppHandler(kDummyAppId); } gcm::GCMDriver* ExtensionGCMAppHandler::GetGCMDriver() const { return gcm::GCMProfileServiceFactory::GetForProfile(profile_)->driver(); } instance_id::InstanceIDDriver* ExtensionGCMAppHandler::GetInstanceIDDriver() const { return instance_id::InstanceIDProfileServiceFactory::GetForProfile(profile_)-> driver(); } void ExtensionGCMAppHandler::OnUnregisterCompleted( const std::string& app_id, gcm::GCMClient::Result result) { RemoveAppHandler(app_id); } void ExtensionGCMAppHandler::OnDeleteIDCompleted( const std::string& app_id, instance_id::InstanceID::Result result) { GetGCMDriver()->Unregister( app_id, base::Bind(&ExtensionGCMAppHandler::OnUnregisterCompleted, weak_factory_.GetWeakPtr(), app_id)); // InstanceIDDriver::RemoveInstanceID will delete the InstanceID itself. // Postpone to do it outside this calling context to avoid any risk to // the caller. base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&ExtensionGCMAppHandler::RemoveInstanceID, weak_factory_.GetWeakPtr(), app_id)); } void ExtensionGCMAppHandler::RemoveInstanceID(const std::string& app_id) { GetInstanceIDDriver()->RemoveInstanceID(app_id); } void ExtensionGCMAppHandler::AddAppHandler(const std::string& app_id) { GetGCMDriver()->AddAppHandler(app_id, this); } void ExtensionGCMAppHandler::RemoveAppHandler(const std::string& app_id) { GetGCMDriver()->RemoveAppHandler(app_id); } } // namespace extensions