// Copyright (c) 2012 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_action_manager.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager.h" #include "chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h" #include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/api/extension_action/action_info.h" #include "chrome/common/extensions/api/extension_action/page_action_handler.h" #include "chrome/common/extensions/api/extension_action/script_badge_handler.h" #include "components/browser_context_keyed_service/browser_context_dependency_manager.h" #include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "extensions/common/extension.h" #include "extensions/common/feature_switch.h" namespace extensions { namespace { // BrowserContextKeyedServiceFactory for ExtensionActionManager. class ExtensionActionManagerFactory : public BrowserContextKeyedServiceFactory { public: // BrowserContextKeyedServiceFactory implementation: static ExtensionActionManager* GetForProfile(Profile* profile) { return static_cast<ExtensionActionManager*>( GetInstance()->GetServiceForBrowserContext(profile, true)); } static ExtensionActionManagerFactory* GetInstance(); private: friend struct DefaultSingletonTraits<ExtensionActionManagerFactory>; ExtensionActionManagerFactory() : BrowserContextKeyedServiceFactory( "ExtensionActionManager", BrowserContextDependencyManager::GetInstance()) { } virtual BrowserContextKeyedService* BuildServiceInstanceFor( content::BrowserContext* profile) const OVERRIDE { return new ExtensionActionManager(static_cast<Profile*>(profile)); } virtual content::BrowserContext* GetBrowserContextToUse( content::BrowserContext* context) const OVERRIDE { return chrome::GetBrowserContextRedirectedInIncognito(context); } }; ExtensionActionManagerFactory* ExtensionActionManagerFactory::GetInstance() { return Singleton<ExtensionActionManagerFactory>::get(); } } // namespace ExtensionActionManager::ExtensionActionManager(Profile* profile) : profile_(profile) { CHECK_EQ(profile, profile->GetOriginalProfile()) << "Don't instantiate this with an incognito profile."; registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, content::Source<Profile>(profile)); } ExtensionActionManager::~ExtensionActionManager() { // Don't assert that the ExtensionAction maps are empty because Extensions are // sometimes (only in tests?) not unloaded before the Profile is destroyed. } ExtensionActionManager* ExtensionActionManager::Get(Profile* profile) { return ExtensionActionManagerFactory::GetForProfile(profile); } void ExtensionActionManager::Observe( int type, const content::NotificationSource& source, const content::NotificationDetails& details) { switch (type) { case chrome::NOTIFICATION_EXTENSION_UNLOADED: { const Extension* extension = content::Details<UnloadedExtensionInfo>(details)->extension; page_actions_.erase(extension->id()); browser_actions_.erase(extension->id()); script_badges_.erase(extension->id()); system_indicators_.erase(extension->id()); break; } } } namespace { // Returns map[extension_id] if that entry exists. Otherwise, if // action_info!=NULL, creates an ExtensionAction from it, fills in the map, and // returns that. Otherwise (action_info==NULL), returns NULL. ExtensionAction* GetOrCreateOrNull( std::map<std::string, linked_ptr<ExtensionAction> >* map, const std::string& extension_id, ActionInfo::Type action_type, const ActionInfo* action_info, Profile* profile) { std::map<std::string, linked_ptr<ExtensionAction> >::const_iterator it = map->find(extension_id); if (it != map->end()) return it->second.get(); if (!action_info) return NULL; // Only create action info for enabled extensions. // This avoids bugs where actions are recreated just after being removed // in response to NOTIFICATION_EXTENSION_UNLOADED in // ExtensionActionManager::Observe() ExtensionService* service = ExtensionSystem::Get(profile)->extension_service(); if (!service->GetExtensionById(extension_id, false)) return NULL; linked_ptr<ExtensionAction> action(new ExtensionAction( extension_id, action_type, *action_info)); (*map)[extension_id] = action; return action.get(); } } // namespace ExtensionAction* ExtensionActionManager::GetPageAction( const extensions::Extension& extension) const { // The action box changes the meaning of the page action area, so we // need to convert page actions into browser actions. if (FeatureSwitch::script_badges()->IsEnabled()) return NULL; return GetOrCreateOrNull(&page_actions_, extension.id(), ActionInfo::TYPE_PAGE, ActionInfo::GetPageActionInfo(&extension), profile_); } ExtensionAction* ExtensionActionManager::GetBrowserAction( const extensions::Extension& extension) const { const ActionInfo* action_info = ActionInfo::GetBrowserActionInfo(&extension); ActionInfo::Type action_type = ActionInfo::TYPE_BROWSER; if (FeatureSwitch::script_badges()->IsEnabled() && ActionInfo::GetPageActionInfo(&extension)) { // The action box changes the meaning of the page action area, so we // need to convert page actions into browser actions. action_info = ActionInfo::GetPageActionInfo(&extension); action_type = ActionInfo::TYPE_PAGE; } return GetOrCreateOrNull(&browser_actions_, extension.id(), action_type, action_info, profile_); } ExtensionAction* ExtensionActionManager::GetSystemIndicator( const extensions::Extension& extension) const { // If it does not already exist, create the SystemIndicatorManager for the // given profile. This could return NULL if the system indicator area is // unavailable on the current system. If so, return NULL to signal that // the system indicator area is unusable. if (!extensions::SystemIndicatorManagerFactory::GetForProfile(profile_)) return NULL; return GetOrCreateOrNull(&system_indicators_, extension.id(), ActionInfo::TYPE_SYSTEM_INDICATOR, ActionInfo::GetSystemIndicatorInfo(&extension), profile_); } ExtensionAction* ExtensionActionManager::GetScriptBadge( const extensions::Extension& extension) const { return GetOrCreateOrNull(&script_badges_, extension.id(), ActionInfo::TYPE_SCRIPT_BADGE, ActionInfo::GetScriptBadgeInfo(&extension), profile_); } } // namespace extensions