15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action_manager.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
77dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/api/system_indicator/system_indicator_manager.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/extensions/api/system_indicator/system_indicator_manager_factory.h"
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_action.h"
11a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "chrome/browser/extensions/extension_service.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_system.h"
13c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)#include "chrome/browser/profiles/incognito_helpers.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/api/extension_action/action_info.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/api/extension_action/page_action_handler.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/extensions/api/extension_action/script_badge_handler.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/feature_switch.h"
2090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "components/browser_context_keyed_service/browser_context_dependency_manager.h"
2190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "components/browser_context_keyed_service/browser_context_keyed_service_factory.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_source.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// BrowserContextKeyedServiceFactory for ExtensionActionManager.
3090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)class ExtensionActionManagerFactory : public BrowserContextKeyedServiceFactory {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
3290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // BrowserContextKeyedServiceFactory implementation:
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static ExtensionActionManager* GetForProfile(Profile* profile) {
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return static_cast<ExtensionActionManager*>(
3590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        GetInstance()->GetServiceForBrowserContext(profile, true));
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static ExtensionActionManagerFactory* GetInstance();
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  friend struct DefaultSingletonTraits<ExtensionActionManagerFactory>;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ExtensionActionManagerFactory()
4490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      : BrowserContextKeyedServiceFactory(
4590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          "ExtensionActionManager",
4690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)          BrowserContextDependencyManager::GetInstance()) {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  virtual BrowserContextKeyedService* BuildServiceInstanceFor(
50c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::BrowserContext* profile) const OVERRIDE {
51c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return new ExtensionActionManager(static_cast<Profile*>(profile));
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
54c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  virtual content::BrowserContext* GetBrowserContextToUse(
55c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      content::BrowserContext* context) const OVERRIDE {
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return chrome::GetBrowserContextRedirectedInIncognito(context);
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionActionManagerFactory*
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionActionManagerFactory::GetInstance() {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return Singleton<ExtensionActionManagerFactory>::get();
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ExtensionActionManager::ExtensionActionManager(Profile* profile)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : profile_(profile) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK_EQ(profile, profile->GetOriginalProfile())
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      << "Don't instantiate this with an incognito profile.";
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 content::Source<Profile>(profile));
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionActionManager::~ExtensionActionManager() {
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Don't assert that the ExtensionAction maps are empty because Extensions are
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // sometimes (only in tests?) not unloaded before the Profile is destroyed.
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionActionManager* ExtensionActionManager::Get(Profile* profile) {
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ExtensionActionManagerFactory::GetForProfile(profile);
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExtensionActionManager::Observe(
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int type,
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationSource& source,
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const content::NotificationDetails& details) {
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      const Extension* extension =
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          content::Details<UnloadedExtensionInfo>(details)->extension;
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      page_actions_.erase(extension->id());
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      browser_actions_.erase(extension->id());
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      script_badges_.erase(extension->id());
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      system_indicators_.erase(extension->id());
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns map[extension_id] if that entry exists. Otherwise, if
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// action_info!=NULL, creates an ExtensionAction from it, fills in the map, and
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// returns that.  Otherwise (action_info==NULL), returns NULL.
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction* GetOrCreateOrNull(
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::map<std::string, linked_ptr<ExtensionAction> >* map,
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& extension_id,
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ActionInfo::Type action_type,
110a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    const ActionInfo* action_info,
111a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    Profile* profile) {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::map<std::string, linked_ptr<ExtensionAction> >::const_iterator it =
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      map->find(extension_id);
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (it != map->end())
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return it->second.get();
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!action_info)
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
118a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
119a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // Only create action info for enabled extensions.
120a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // This avoids bugs where actions are recreated just after being removed
121a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // in response to NOTIFICATION_EXTENSION_UNLOADED in
122a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  // ExtensionActionManager::Observe()
123a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  ExtensionService* service =
124a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)      ExtensionSystem::Get(profile)->extension_service();
125a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  if (!service->GetExtensionById(extension_id, false))
126a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    return NULL;
127a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  linked_ptr<ExtensionAction> action(new ExtensionAction(
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      extension_id, action_type, *action_info));
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  (*map)[extension_id] = action;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return action.get();
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction* ExtensionActionManager::GetPageAction(
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const extensions::Extension& extension) const {
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The action box changes the meaning of the page action area, so we
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // need to convert page actions into browser actions.
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FeatureSwitch::script_badges()->IsEnabled())
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return NULL;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetOrCreateOrNull(&page_actions_, extension.id(),
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           ActionInfo::TYPE_PAGE,
144a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                           ActionInfo::GetPageActionInfo(&extension),
145a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                           profile_);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction* ExtensionActionManager::GetBrowserAction(
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const extensions::Extension& extension) const {
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const ActionInfo* action_info = ActionInfo::GetBrowserActionInfo(&extension);
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ActionInfo::Type action_type = ActionInfo::TYPE_BROWSER;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FeatureSwitch::script_badges()->IsEnabled() &&
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ActionInfo::GetPageActionInfo(&extension)) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The action box changes the meaning of the page action area, so we
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // need to convert page actions into browser actions.
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    action_info = ActionInfo::GetPageActionInfo(&extension);
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    action_type = ActionInfo::TYPE_PAGE;
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetOrCreateOrNull(&browser_actions_, extension.id(),
160a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                           action_type, action_info, profile_);
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ExtensionAction* ExtensionActionManager::GetSystemIndicator(
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const extensions::Extension& extension) const {
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If it does not already exist, create the SystemIndicatorManager for the
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // given profile.  This could return NULL if the system indicator area is
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // unavailable on the current system.  If so, return NULL to signal that
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the system indicator area is unusable.
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!extensions::SystemIndicatorManagerFactory::GetForProfile(profile_))
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return GetOrCreateOrNull(&system_indicators_, extension.id(),
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           ActionInfo::TYPE_SYSTEM_INDICATOR,
174a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                           ActionInfo::GetSystemIndicatorInfo(&extension),
175a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                           profile_);
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ExtensionAction* ExtensionActionManager::GetScriptBadge(
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const extensions::Extension& extension) const {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return GetOrCreateOrNull(&script_badges_, extension.id(),
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                           ActionInfo::TYPE_SCRIPT_BADGE,
182a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                           ActionInfo::GetScriptBadgeInfo(&extension),
183a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                           profile_);
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace extensions
187