Home | History | Annotate | Download | only in extensions
      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/profiles/profile.h"
     10 #include "components/keyed_service/content/browser_context_dependency_manager.h"
     11 #include "extensions/browser/extension_registry.h"
     12 #include "extensions/browser/extension_system.h"
     13 #include "extensions/browser/extensions_browser_client.h"
     14 #include "extensions/common/constants.h"
     15 #include "extensions/common/manifest_handlers/icons_handler.h"
     16 
     17 namespace extensions {
     18 
     19 namespace {
     20 
     21 // BrowserContextKeyedServiceFactory for ExtensionActionManager.
     22 class ExtensionActionManagerFactory : public BrowserContextKeyedServiceFactory {
     23  public:
     24   // BrowserContextKeyedServiceFactory implementation:
     25   static ExtensionActionManager* GetForBrowserContext(
     26       content::BrowserContext* context) {
     27     return static_cast<ExtensionActionManager*>(
     28         GetInstance()->GetServiceForBrowserContext(context, true));
     29   }
     30 
     31   static ExtensionActionManagerFactory* GetInstance();
     32 
     33  private:
     34   friend struct DefaultSingletonTraits<ExtensionActionManagerFactory>;
     35 
     36   ExtensionActionManagerFactory()
     37       : BrowserContextKeyedServiceFactory(
     38           "ExtensionActionManager",
     39           BrowserContextDependencyManager::GetInstance()) {
     40   }
     41 
     42   virtual KeyedService* BuildServiceInstanceFor(
     43       content::BrowserContext* profile) const OVERRIDE {
     44     return new ExtensionActionManager(static_cast<Profile*>(profile));
     45   }
     46 
     47   virtual content::BrowserContext* GetBrowserContextToUse(
     48       content::BrowserContext* context) const OVERRIDE {
     49     return ExtensionsBrowserClient::Get()->GetOriginalContext(context);
     50   }
     51 };
     52 
     53 ExtensionActionManagerFactory*
     54 ExtensionActionManagerFactory::GetInstance() {
     55   return Singleton<ExtensionActionManagerFactory>::get();
     56 }
     57 
     58 }  // namespace
     59 
     60 ExtensionActionManager::ExtensionActionManager(Profile* profile)
     61     : profile_(profile), extension_registry_observer_(this) {
     62   CHECK_EQ(profile, profile->GetOriginalProfile())
     63       << "Don't instantiate this with an incognito profile.";
     64   extension_registry_observer_.Add(ExtensionRegistry::Get(profile_));
     65 }
     66 
     67 ExtensionActionManager::~ExtensionActionManager() {
     68   // Don't assert that the ExtensionAction maps are empty because Extensions are
     69   // sometimes (only in tests?) not unloaded before the Profile is destroyed.
     70 }
     71 
     72 ExtensionActionManager* ExtensionActionManager::Get(
     73     content::BrowserContext* context) {
     74   return ExtensionActionManagerFactory::GetForBrowserContext(context);
     75 }
     76 
     77 void ExtensionActionManager::OnExtensionUnloaded(
     78     content::BrowserContext* browser_context,
     79     const Extension* extension,
     80     UnloadedExtensionInfo::Reason reason) {
     81   page_actions_.erase(extension->id());
     82   browser_actions_.erase(extension->id());
     83   system_indicators_.erase(extension->id());
     84 }
     85 
     86 namespace {
     87 
     88 // Loads resources missing from |action| (i.e. title, icons) from the "icons"
     89 // key of |extension|'s manifest.
     90 void PopulateMissingValues(const Extension& extension,
     91                            ExtensionAction* action) {
     92   // If the title is missing from |action|, set it to |extension|'s name.
     93   if (action->GetTitle(ExtensionAction::kDefaultTabId).empty())
     94     action->SetTitle(ExtensionAction::kDefaultTabId, extension.name());
     95 
     96   scoped_ptr<ExtensionIconSet> default_icon(new ExtensionIconSet());
     97   if (action->default_icon())
     98     *default_icon = *action->default_icon();
     99 
    100   const ExtensionIconSet& extension_icons = IconsInfo::GetIcons(&extension);
    101   std::string largest_icon = extension_icons.Get(
    102       extension_misc::EXTENSION_ICON_GIGANTOR,
    103       ExtensionIconSet::MATCH_SMALLER);
    104 
    105   if (!largest_icon.empty()) {
    106     int largest_icon_size = extension_icons.GetIconSizeFromPath(largest_icon);
    107     // Replace any missing extension action icons with the largest icon
    108     // retrieved from |extension|'s manifest so long as the largest icon is
    109     // larger than the current key.
    110     for (int i = extension_misc::kNumExtensionActionIconSizes - 1;
    111          i >= 0; --i) {
    112       int size = extension_misc::kExtensionActionIconSizes[i].size;
    113       if (default_icon->Get(size, ExtensionIconSet::MATCH_BIGGER).empty()
    114           && largest_icon_size > size) {
    115         default_icon->Add(size, largest_icon);
    116         break;
    117       }
    118     }
    119     action->set_default_icon(default_icon.Pass());
    120   }
    121 }
    122 
    123 // Returns map[extension_id] if that entry exists. Otherwise, if
    124 // action_info!=NULL, creates an ExtensionAction from it, fills in the map, and
    125 // returns that.  Otherwise (action_info==NULL), returns NULL.
    126 ExtensionAction* GetOrCreateOrNull(
    127     std::map<std::string, linked_ptr<ExtensionAction> >* map,
    128     const Extension& extension,
    129     ActionInfo::Type action_type,
    130     const ActionInfo* action_info,
    131     Profile* profile) {
    132   std::map<std::string, linked_ptr<ExtensionAction> >::const_iterator it =
    133       map->find(extension.id());
    134   if (it != map->end())
    135     return it->second.get();
    136   if (!action_info)
    137     return NULL;
    138 
    139   // Only create action info for enabled extensions.
    140   // This avoids bugs where actions are recreated just after being removed
    141   // in response to OnExtensionUnloaded().
    142   if (!ExtensionRegistry::Get(profile)
    143       ->enabled_extensions().Contains(extension.id())) {
    144     return NULL;
    145   }
    146 
    147   linked_ptr<ExtensionAction> action(new ExtensionAction(
    148       extension.id(), action_type, *action_info));
    149   (*map)[extension.id()] = action;
    150   PopulateMissingValues(extension, action.get());
    151   return action.get();
    152 }
    153 
    154 }  // namespace
    155 
    156 ExtensionAction* ExtensionActionManager::GetPageAction(
    157     const Extension& extension) const {
    158   return GetOrCreateOrNull(&page_actions_, extension,
    159                            ActionInfo::TYPE_PAGE,
    160                            ActionInfo::GetPageActionInfo(&extension),
    161                            profile_);
    162 }
    163 
    164 ExtensionAction* ExtensionActionManager::GetBrowserAction(
    165     const Extension& extension) const {
    166   return GetOrCreateOrNull(&browser_actions_, extension,
    167                            ActionInfo::TYPE_BROWSER,
    168                            ActionInfo::GetBrowserActionInfo(&extension),
    169                            profile_);
    170 }
    171 
    172 scoped_ptr<ExtensionAction> ExtensionActionManager::GetBestFitAction(
    173     const Extension& extension,
    174     ActionInfo::Type type) const {
    175   const ActionInfo* info = ActionInfo::GetBrowserActionInfo(&extension);
    176   if (!info)
    177     info = ActionInfo::GetPageActionInfo(&extension);
    178 
    179   // Create a new ExtensionAction of |type| with |extension|'s ActionInfo.
    180   // If no ActionInfo exists for |extension|, create and return a new action
    181   // with a blank ActionInfo.
    182   // Populate any missing values from |extension|'s manifest.
    183   scoped_ptr<ExtensionAction> new_action(new ExtensionAction(
    184       extension.id(), type, info ? *info : ActionInfo()));
    185   PopulateMissingValues(extension, new_action.get());
    186   return new_action.Pass();
    187 }
    188 
    189 ExtensionAction* ExtensionActionManager::GetSystemIndicator(
    190     const Extension& extension) const {
    191   // If it does not already exist, create the SystemIndicatorManager for the
    192   // given profile.  This could return NULL if the system indicator area is
    193   // unavailable on the current system.  If so, return NULL to signal that
    194   // the system indicator area is unusable.
    195   if (!SystemIndicatorManagerFactory::GetForProfile(profile_))
    196     return NULL;
    197 
    198   return GetOrCreateOrNull(&system_indicators_, extension,
    199                            ActionInfo::TYPE_SYSTEM_INDICATOR,
    200                            ActionInfo::GetSystemIndicatorInfo(&extension),
    201                            profile_);
    202 }
    203 
    204 ExtensionAction* ExtensionActionManager::GetExtensionAction(
    205     const Extension& extension) const {
    206   ExtensionAction* action = GetBrowserAction(extension);
    207   return action ? action : GetPageAction(extension);
    208 }
    209 
    210 }  // namespace extensions
    211