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 21 namespace extensions { 22 23 namespace { 24 25 const char kDummyAppId[] = "extension.guard.dummy.id"; 26 27 base::LazyInstance<BrowserContextKeyedAPIFactory<ExtensionGCMAppHandler> > 28 g_factory = LAZY_INSTANCE_INITIALIZER; 29 30 bool IsGCMPermissionEnabled(const Extension* extension) { 31 return extension->permissions_data()->HasAPIPermission(APIPermission::kGcm); 32 } 33 34 } // namespace 35 36 37 // static 38 BrowserContextKeyedAPIFactory<ExtensionGCMAppHandler>* 39 ExtensionGCMAppHandler::GetFactoryInstance() { 40 return g_factory.Pointer(); 41 } 42 43 ExtensionGCMAppHandler::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 51 ExtensionGCMAppHandler::~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 62 void ExtensionGCMAppHandler::ShutdownHandler() { 63 js_event_router_.reset(); 64 } 65 66 void ExtensionGCMAppHandler::OnMessage( 67 const std::string& app_id, 68 const gcm::GCMClient::IncomingMessage& message) { 69 js_event_router_->OnMessage(app_id, message); 70 } 71 72 void ExtensionGCMAppHandler::OnMessagesDeleted(const std::string& app_id) { 73 js_event_router_->OnMessagesDeleted(app_id); 74 } 75 76 void 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 82 void 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 88 void ExtensionGCMAppHandler::OnExtensionLoaded( 89 content::BrowserContext* browser_context, 90 const Extension* extension) { 91 if (IsGCMPermissionEnabled(extension)) 92 AddAppHandler(extension->id()); 93 } 94 95 void 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 125 void 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 139 void ExtensionGCMAppHandler::AddDummyAppHandler() { 140 AddAppHandler(kDummyAppId); 141 } 142 143 void ExtensionGCMAppHandler::RemoveDummyAppHandler() { 144 RemoveAppHandler(kDummyAppId); 145 } 146 147 gcm::GCMDriver* ExtensionGCMAppHandler::GetGCMDriver() const { 148 return gcm::GCMProfileServiceFactory::GetForProfile(profile_)->driver(); 149 } 150 151 void ExtensionGCMAppHandler::OnUnregisterCompleted( 152 const std::string& app_id, gcm::GCMClient::Result result) { 153 // Nothing to do. 154 } 155 156 void ExtensionGCMAppHandler::AddAppHandler(const std::string& app_id) { 157 GetGCMDriver()->AddAppHandler(app_id, this); 158 } 159 160 void ExtensionGCMAppHandler::RemoveAppHandler(const std::string& app_id) { 161 GetGCMDriver()->RemoveAppHandler(app_id); 162 } 163 164 } // namespace extensions 165