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 "chrome/browser/extensions/extension_gcm_app_handler.h" 6 7#include "base/bind.h" 8#include "base/lazy_instance.h" 9#include "base/location.h" 10#include "chrome/browser/chrome_notification_types.h" 11#include "chrome/browser/extensions/api/gcm/gcm_api.h" 12#include "chrome/browser/profiles/profile.h" 13#include "chrome/browser/services/gcm/gcm_profile_service.h" 14#include "chrome/browser/services/gcm/gcm_profile_service_factory.h" 15#include "components/gcm_driver/gcm_driver.h" 16#include "extensions/browser/extension_registry.h" 17#include "extensions/browser/extension_system.h" 18#include "extensions/common/extension.h" 19#include "extensions/common/permissions/permissions_data.h" 20 21namespace extensions { 22 23namespace { 24 25const char kDummyAppId[] = "extension.guard.dummy.id"; 26 27base::LazyInstance<BrowserContextKeyedAPIFactory<ExtensionGCMAppHandler> > 28 g_factory = LAZY_INSTANCE_INITIALIZER; 29 30bool IsGCMPermissionEnabled(const Extension* extension) { 31 return extension->permissions_data()->HasAPIPermission(APIPermission::kGcm); 32} 33 34} // namespace 35 36 37// static 38BrowserContextKeyedAPIFactory<ExtensionGCMAppHandler>* 39ExtensionGCMAppHandler::GetFactoryInstance() { 40 return g_factory.Pointer(); 41} 42 43ExtensionGCMAppHandler::ExtensionGCMAppHandler(content::BrowserContext* context) 44 : profile_(Profile::FromBrowserContext(context)), 45 extension_registry_observer_(this), 46 weak_factory_(this) { 47 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); 48 js_event_router_.reset(new extensions::GcmJsEventRouter(profile_)); 49} 50 51ExtensionGCMAppHandler::~ExtensionGCMAppHandler() { 52 const ExtensionSet& enabled_extensions = 53 ExtensionRegistry::Get(profile_)->enabled_extensions(); 54 for (ExtensionSet::const_iterator extension = enabled_extensions.begin(); 55 extension != enabled_extensions.end(); 56 ++extension) { 57 if (IsGCMPermissionEnabled(extension->get())) 58 GetGCMDriver()->RemoveAppHandler((*extension)->id()); 59 } 60} 61 62void ExtensionGCMAppHandler::ShutdownHandler() { 63 js_event_router_.reset(); 64} 65 66void ExtensionGCMAppHandler::OnMessage( 67 const std::string& app_id, 68 const gcm::GCMClient::IncomingMessage& message) { 69 js_event_router_->OnMessage(app_id, message); 70} 71 72void ExtensionGCMAppHandler::OnMessagesDeleted(const std::string& app_id) { 73 js_event_router_->OnMessagesDeleted(app_id); 74} 75 76void ExtensionGCMAppHandler::OnSendError( 77 const std::string& app_id, 78 const gcm::GCMClient::SendErrorDetails& send_error_details) { 79 js_event_router_->OnSendError(app_id, send_error_details); 80} 81 82void ExtensionGCMAppHandler::OnSendAcknowledged( 83 const std::string& app_id, 84 const std::string& message_id) { 85 // This event is not exposed to JS API. It terminates here. 86} 87 88void ExtensionGCMAppHandler::OnExtensionLoaded( 89 content::BrowserContext* browser_context, 90 const Extension* extension) { 91 if (IsGCMPermissionEnabled(extension)) 92 AddAppHandler(extension->id()); 93} 94 95void ExtensionGCMAppHandler::OnExtensionUnloaded( 96 content::BrowserContext* browser_context, 97 const Extension* extension, 98 UnloadedExtensionInfo::Reason reason) { 99 if (!IsGCMPermissionEnabled(extension)) 100 return; 101 102 if (reason == UnloadedExtensionInfo::REASON_UPDATE && 103 GetGCMDriver()->app_handlers().size() == 1) { 104 // When the extension is being updated, it will be first unloaded and then 105 // loaded again by ExtensionService::AddExtension. If the app handler for 106 // this extension is the only handler, removing it and adding it again will 107 // cause the GCM service being stopped and restarted unnecessarily. To work 108 // around this, we add a dummy app handler to guard against it. This dummy 109 // app handler will be removed once the extension loading logic is done. 110 // 111 // Also note that the GCM message routing will not be interruptted during 112 // the update process since unloading and reloading extension are done in 113 // the single function ExtensionService::AddExtension. 114 AddDummyAppHandler(); 115 116 base::MessageLoop::current()->PostTask( 117 FROM_HERE, 118 base::Bind(&ExtensionGCMAppHandler::RemoveDummyAppHandler, 119 weak_factory_.GetWeakPtr())); 120 } 121 122 RemoveAppHandler(extension->id()); 123} 124 125void ExtensionGCMAppHandler::OnExtensionUninstalled( 126 content::BrowserContext* browser_context, 127 const Extension* extension, 128 extensions::UninstallReason reason) { 129 if (IsGCMPermissionEnabled(extension)) { 130 GetGCMDriver()->Unregister( 131 extension->id(), 132 base::Bind(&ExtensionGCMAppHandler::OnUnregisterCompleted, 133 weak_factory_.GetWeakPtr(), 134 extension->id())); 135 RemoveAppHandler(extension->id()); 136 } 137} 138 139void ExtensionGCMAppHandler::AddDummyAppHandler() { 140 AddAppHandler(kDummyAppId); 141} 142 143void ExtensionGCMAppHandler::RemoveDummyAppHandler() { 144 RemoveAppHandler(kDummyAppId); 145} 146 147gcm::GCMDriver* ExtensionGCMAppHandler::GetGCMDriver() const { 148 return gcm::GCMProfileServiceFactory::GetForProfile(profile_)->driver(); 149} 150 151void ExtensionGCMAppHandler::OnUnregisterCompleted( 152 const std::string& app_id, gcm::GCMClient::Result result) { 153 // Nothing to do. 154} 155 156void ExtensionGCMAppHandler::AddAppHandler(const std::string& app_id) { 157 GetGCMDriver()->AddAppHandler(app_id, this); 158} 159 160void ExtensionGCMAppHandler::RemoveAppHandler(const std::string& app_id) { 161 GetGCMDriver()->RemoveAppHandler(app_id); 162} 163 164} // namespace extensions 165