1 // Copyright (c) 2012 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_action_manager.h" 6 7 #include "chrome/browser/chrome_notification_types.h" 8 #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager.h" 9 #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h" 10 #include "chrome/browser/extensions/extension_action.h" 11 #include "chrome/browser/extensions/extension_service.h" 12 #include "chrome/browser/extensions/extension_system.h" 13 #include "chrome/browser/profiles/incognito_helpers.h" 14 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/common/extensions/api/extension_action/action_info.h" 16 #include "chrome/common/extensions/api/extension_action/page_action_handler.h" 17 #include "chrome/common/extensions/api/extension_action/script_badge_handler.h" 18 #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" 19 #include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" 20 #include "content/public/browser/notification_service.h" 21 #include "content/public/browser/notification_source.h" 22 #include "extensions/common/extension.h" 23 #include "extensions/common/feature_switch.h" 24 25 namespace extensions { 26 27 namespace { 28 29 // BrowserContextKeyedServiceFactory for ExtensionActionManager. 30 class ExtensionActionManagerFactory : public BrowserContextKeyedServiceFactory { 31 public: 32 // BrowserContextKeyedServiceFactory implementation: 33 static ExtensionActionManager* GetForProfile(Profile* profile) { 34 return static_cast<ExtensionActionManager*>( 35 GetInstance()->GetServiceForBrowserContext(profile, true)); 36 } 37 38 static ExtensionActionManagerFactory* GetInstance(); 39 40 private: 41 friend struct DefaultSingletonTraits<ExtensionActionManagerFactory>; 42 43 ExtensionActionManagerFactory() 44 : BrowserContextKeyedServiceFactory( 45 "ExtensionActionManager", 46 BrowserContextDependencyManager::GetInstance()) { 47 } 48 49 virtual BrowserContextKeyedService* BuildServiceInstanceFor( 50 content::BrowserContext* profile) const OVERRIDE { 51 return new ExtensionActionManager(static_cast<Profile*>(profile)); 52 } 53 54 virtual content::BrowserContext* GetBrowserContextToUse( 55 content::BrowserContext* context) const OVERRIDE { 56 return chrome::GetBrowserContextRedirectedInIncognito(context); 57 } 58 }; 59 60 ExtensionActionManagerFactory* 61 ExtensionActionManagerFactory::GetInstance() { 62 return Singleton<ExtensionActionManagerFactory>::get(); 63 } 64 65 } // namespace 66 67 ExtensionActionManager::ExtensionActionManager(Profile* profile) 68 : profile_(profile) { 69 CHECK_EQ(profile, profile->GetOriginalProfile()) 70 << "Don't instantiate this with an incognito profile."; 71 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, 72 content::Source<Profile>(profile)); 73 } 74 75 ExtensionActionManager::~ExtensionActionManager() { 76 // Don't assert that the ExtensionAction maps are empty because Extensions are 77 // sometimes (only in tests?) not unloaded before the Profile is destroyed. 78 } 79 80 ExtensionActionManager* ExtensionActionManager::Get(Profile* profile) { 81 return ExtensionActionManagerFactory::GetForProfile(profile); 82 } 83 84 void ExtensionActionManager::Observe( 85 int type, 86 const content::NotificationSource& source, 87 const content::NotificationDetails& details) { 88 switch (type) { 89 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { 90 const Extension* extension = 91 content::Details<UnloadedExtensionInfo>(details)->extension; 92 page_actions_.erase(extension->id()); 93 browser_actions_.erase(extension->id()); 94 script_badges_.erase(extension->id()); 95 system_indicators_.erase(extension->id()); 96 break; 97 } 98 } 99 } 100 101 namespace { 102 103 // Returns map[extension_id] if that entry exists. Otherwise, if 104 // action_info!=NULL, creates an ExtensionAction from it, fills in the map, and 105 // returns that. Otherwise (action_info==NULL), returns NULL. 106 ExtensionAction* GetOrCreateOrNull( 107 std::map<std::string, linked_ptr<ExtensionAction> >* map, 108 const std::string& extension_id, 109 ActionInfo::Type action_type, 110 const ActionInfo* action_info, 111 Profile* profile) { 112 std::map<std::string, linked_ptr<ExtensionAction> >::const_iterator it = 113 map->find(extension_id); 114 if (it != map->end()) 115 return it->second.get(); 116 if (!action_info) 117 return NULL; 118 119 // Only create action info for enabled extensions. 120 // This avoids bugs where actions are recreated just after being removed 121 // in response to NOTIFICATION_EXTENSION_UNLOADED in 122 // ExtensionActionManager::Observe() 123 ExtensionService* service = 124 ExtensionSystem::Get(profile)->extension_service(); 125 if (!service->GetExtensionById(extension_id, false)) 126 return NULL; 127 128 linked_ptr<ExtensionAction> action(new ExtensionAction( 129 extension_id, action_type, *action_info)); 130 (*map)[extension_id] = action; 131 return action.get(); 132 } 133 134 } // namespace 135 136 ExtensionAction* ExtensionActionManager::GetPageAction( 137 const extensions::Extension& extension) const { 138 // The action box changes the meaning of the page action area, so we 139 // need to convert page actions into browser actions. 140 if (FeatureSwitch::script_badges()->IsEnabled()) 141 return NULL; 142 return GetOrCreateOrNull(&page_actions_, extension.id(), 143 ActionInfo::TYPE_PAGE, 144 ActionInfo::GetPageActionInfo(&extension), 145 profile_); 146 } 147 148 ExtensionAction* ExtensionActionManager::GetBrowserAction( 149 const extensions::Extension& extension) const { 150 const ActionInfo* action_info = ActionInfo::GetBrowserActionInfo(&extension); 151 ActionInfo::Type action_type = ActionInfo::TYPE_BROWSER; 152 if (FeatureSwitch::script_badges()->IsEnabled() && 153 ActionInfo::GetPageActionInfo(&extension)) { 154 // The action box changes the meaning of the page action area, so we 155 // need to convert page actions into browser actions. 156 action_info = ActionInfo::GetPageActionInfo(&extension); 157 action_type = ActionInfo::TYPE_PAGE; 158 } 159 return GetOrCreateOrNull(&browser_actions_, extension.id(), 160 action_type, action_info, profile_); 161 } 162 163 ExtensionAction* ExtensionActionManager::GetSystemIndicator( 164 const extensions::Extension& extension) const { 165 // If it does not already exist, create the SystemIndicatorManager for the 166 // given profile. This could return NULL if the system indicator area is 167 // unavailable on the current system. If so, return NULL to signal that 168 // the system indicator area is unusable. 169 if (!extensions::SystemIndicatorManagerFactory::GetForProfile(profile_)) 170 return NULL; 171 172 return GetOrCreateOrNull(&system_indicators_, extension.id(), 173 ActionInfo::TYPE_SYSTEM_INDICATOR, 174 ActionInfo::GetSystemIndicatorInfo(&extension), 175 profile_); 176 } 177 178 ExtensionAction* ExtensionActionManager::GetScriptBadge( 179 const extensions::Extension& extension) const { 180 return GetOrCreateOrNull(&script_badges_, extension.id(), 181 ActionInfo::TYPE_SCRIPT_BADGE, 182 ActionInfo::GetScriptBadgeInfo(&extension), 183 profile_); 184 } 185 186 } // namespace extensions 187