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/extensions/api/system_indicator/system_indicator_manager_factory.h"
8#include "chrome/browser/extensions/extension_action.h"
9#include "chrome/browser/extensions/extension_service.h"
10#include "chrome/browser/profiles/profile.h"
11#include "components/keyed_service/content/browser_context_dependency_manager.h"
12#include "extensions/browser/extension_registry.h"
13#include "extensions/browser/extension_system.h"
14#include "extensions/browser/extensions_browser_client.h"
15
16namespace extensions {
17
18namespace {
19
20// BrowserContextKeyedServiceFactory for ExtensionActionManager.
21class ExtensionActionManagerFactory : public BrowserContextKeyedServiceFactory {
22 public:
23  // BrowserContextKeyedServiceFactory implementation:
24  static ExtensionActionManager* GetForProfile(Profile* profile) {
25    return static_cast<ExtensionActionManager*>(
26        GetInstance()->GetServiceForBrowserContext(profile, true));
27  }
28
29  static ExtensionActionManagerFactory* GetInstance();
30
31 private:
32  friend struct DefaultSingletonTraits<ExtensionActionManagerFactory>;
33
34  ExtensionActionManagerFactory()
35      : BrowserContextKeyedServiceFactory(
36          "ExtensionActionManager",
37          BrowserContextDependencyManager::GetInstance()) {
38  }
39
40  virtual KeyedService* BuildServiceInstanceFor(
41      content::BrowserContext* profile) const OVERRIDE {
42    return new ExtensionActionManager(static_cast<Profile*>(profile));
43  }
44
45  virtual content::BrowserContext* GetBrowserContextToUse(
46      content::BrowserContext* context) const OVERRIDE {
47    return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
48  }
49};
50
51ExtensionActionManagerFactory*
52ExtensionActionManagerFactory::GetInstance() {
53  return Singleton<ExtensionActionManagerFactory>::get();
54}
55
56}  // namespace
57
58ExtensionActionManager::ExtensionActionManager(Profile* profile)
59    : profile_(profile), extension_registry_observer_(this) {
60  CHECK_EQ(profile, profile->GetOriginalProfile())
61      << "Don't instantiate this with an incognito profile.";
62  extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
63}
64
65ExtensionActionManager::~ExtensionActionManager() {
66  // Don't assert that the ExtensionAction maps are empty because Extensions are
67  // sometimes (only in tests?) not unloaded before the Profile is destroyed.
68}
69
70ExtensionActionManager* ExtensionActionManager::Get(Profile* profile) {
71  return ExtensionActionManagerFactory::GetForProfile(profile);
72}
73
74void ExtensionActionManager::OnExtensionUnloaded(
75    content::BrowserContext* browser_context,
76    const Extension* extension,
77    UnloadedExtensionInfo::Reason reason) {
78  page_actions_.erase(extension->id());
79  browser_actions_.erase(extension->id());
80  system_indicators_.erase(extension->id());
81}
82
83namespace {
84
85// Returns map[extension_id] if that entry exists. Otherwise, if
86// action_info!=NULL, creates an ExtensionAction from it, fills in the map, and
87// returns that.  Otherwise (action_info==NULL), returns NULL.
88ExtensionAction* GetOrCreateOrNull(
89    std::map<std::string, linked_ptr<ExtensionAction> >* map,
90    const std::string& extension_id,
91    ActionInfo::Type action_type,
92    const ActionInfo* action_info,
93    Profile* profile) {
94  std::map<std::string, linked_ptr<ExtensionAction> >::const_iterator it =
95      map->find(extension_id);
96  if (it != map->end())
97    return it->second.get();
98  if (!action_info)
99    return NULL;
100
101  // Only create action info for enabled extensions.
102  // This avoids bugs where actions are recreated just after being removed
103  // in response to OnExtensionUnloaded().
104  ExtensionService* service =
105      ExtensionSystem::Get(profile)->extension_service();
106  if (!service->GetExtensionById(extension_id, false))
107    return NULL;
108
109  linked_ptr<ExtensionAction> action(new ExtensionAction(
110      extension_id, action_type, *action_info));
111  (*map)[extension_id] = action;
112  return action.get();
113}
114
115}  // namespace
116
117ExtensionAction* ExtensionActionManager::GetPageAction(
118    const extensions::Extension& extension) const {
119  return GetOrCreateOrNull(&page_actions_, extension.id(),
120                           ActionInfo::TYPE_PAGE,
121                           ActionInfo::GetPageActionInfo(&extension),
122                           profile_);
123}
124
125ExtensionAction* ExtensionActionManager::GetBrowserAction(
126    const extensions::Extension& extension) const {
127  return GetOrCreateOrNull(&browser_actions_, extension.id(),
128                           ActionInfo::TYPE_BROWSER,
129                           ActionInfo::GetBrowserActionInfo(&extension),
130                           profile_);
131}
132
133ExtensionAction* ExtensionActionManager::GetSystemIndicator(
134    const extensions::Extension& extension) const {
135  // If it does not already exist, create the SystemIndicatorManager for the
136  // given profile.  This could return NULL if the system indicator area is
137  // unavailable on the current system.  If so, return NULL to signal that
138  // the system indicator area is unusable.
139  if (!extensions::SystemIndicatorManagerFactory::GetForProfile(profile_))
140    return NULL;
141
142  return GetOrCreateOrNull(&system_indicators_, extension.id(),
143                           ActionInfo::TYPE_SYSTEM_INDICATOR,
144                           ActionInfo::GetSystemIndicatorInfo(&extension),
145                           profile_);
146}
147
148}  // namespace extensions
149