Home | History | Annotate | Download | only in extension_action
      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/common/extensions/api/extension_action/action_info.h"
      6 
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "chrome/common/extensions/api/commands/commands_handler.h"
     10 #include "extensions/common/constants.h"
     11 #include "extensions/common/error_utils.h"
     12 #include "extensions/common/extension.h"
     13 #include "extensions/common/manifest_constants.h"
     14 #include "extensions/common/manifest_handler_helpers.h"
     15 
     16 namespace extensions {
     17 
     18 namespace errors = manifest_errors;
     19 namespace keys = manifest_keys;
     20 
     21 namespace {
     22 
     23 // The manifest data container for the ActionInfos for BrowserActions,
     24 // PageActions and SystemIndicators.
     25 struct ActionInfoData : public Extension::ManifestData {
     26   explicit ActionInfoData(ActionInfo* action_info);
     27   virtual ~ActionInfoData();
     28 
     29   // The action associated with the BrowserAction.
     30   scoped_ptr<ActionInfo> action_info;
     31 };
     32 
     33 ActionInfoData::ActionInfoData(ActionInfo* info) : action_info(info) {
     34 }
     35 
     36 ActionInfoData::~ActionInfoData() {
     37 }
     38 
     39 static const ActionInfo* GetActionInfo(const Extension* extension,
     40                                        const std::string& key) {
     41   ActionInfoData* data = static_cast<ActionInfoData*>(
     42       extension->GetManifestData(key));
     43   return data ? data->action_info.get() : NULL;
     44 }
     45 
     46 }  // namespace
     47 
     48 ActionInfo::ActionInfo() {
     49 }
     50 
     51 ActionInfo::~ActionInfo() {
     52 }
     53 
     54 // static
     55 scoped_ptr<ActionInfo> ActionInfo::Load(const Extension* extension,
     56                                         const base::DictionaryValue* dict,
     57                                         base::string16* error) {
     58   scoped_ptr<ActionInfo> result(new ActionInfo());
     59 
     60   if (extension->manifest_version() == 1) {
     61     // kPageActionIcons is obsolete, and used by very few extensions. Continue
     62     // loading it, but only take the first icon as the default_icon path.
     63     const base::ListValue* icons = NULL;
     64     if (dict->HasKey(keys::kPageActionIcons) &&
     65         dict->GetList(keys::kPageActionIcons, &icons)) {
     66       base::ListValue::const_iterator iter = icons->begin();
     67       std::string path;
     68       if (iter == icons->end() ||
     69           !(*iter)->GetAsString(&path) ||
     70           !manifest_handler_helpers::NormalizeAndValidatePath(&path)) {
     71         *error = base::ASCIIToUTF16(errors::kInvalidPageActionIconPath);
     72         return scoped_ptr<ActionInfo>();
     73       }
     74       result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, path);
     75     }
     76 
     77     std::string id;
     78     if (dict->HasKey(keys::kPageActionId)) {
     79       if (!dict->GetString(keys::kPageActionId, &id)) {
     80         *error = base::ASCIIToUTF16(errors::kInvalidPageActionId);
     81         return scoped_ptr<ActionInfo>();
     82       }
     83       result->id = id;
     84     }
     85   }
     86 
     87   // Read the page action |default_icon| (optional).
     88   // The |default_icon| value can be either dictionary {icon size -> icon path}
     89   // or non empty string value.
     90   if (dict->HasKey(keys::kPageActionDefaultIcon)) {
     91     const base::DictionaryValue* icons_value = NULL;
     92     std::string default_icon;
     93     if (dict->GetDictionary(keys::kPageActionDefaultIcon, &icons_value)) {
     94       int icon_sizes[extension_misc::kNumExtensionActionIconSizes];
     95       for (size_t i = 0u; i < extension_misc::kNumExtensionActionIconSizes; ++i)
     96         icon_sizes[i] = extension_misc::kExtensionActionIconSizes[i].size;
     97       if (!manifest_handler_helpers::LoadIconsFromDictionary(
     98               icons_value,
     99               icon_sizes,
    100               extension_misc::kNumExtensionActionIconSizes,
    101               &result->default_icon,
    102               error)) {
    103         return scoped_ptr<ActionInfo>();
    104       }
    105     } else if (dict->GetString(keys::kPageActionDefaultIcon, &default_icon) &&
    106                manifest_handler_helpers::NormalizeAndValidatePath(
    107                    &default_icon)) {
    108       // Choose the most optimistic (highest) icon density - e.g. 38 not 19 -
    109       // regardless of the actual icon resolution, whatever that happens to be.
    110       // Code elsewhere knows how to scale 38 down to 19.
    111       result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION *
    112                                    extension_misc::kNumExtensionActionIconSizes,
    113                                default_icon);
    114     } else {
    115       *error = base::ASCIIToUTF16(errors::kInvalidPageActionIconPath);
    116       return scoped_ptr<ActionInfo>();
    117     }
    118   }
    119 
    120   // Read the page action title from |default_title| if present, |name| if not
    121   // (both optional).
    122   if (dict->HasKey(keys::kPageActionDefaultTitle)) {
    123     if (!dict->GetString(keys::kPageActionDefaultTitle,
    124                          &result->default_title)) {
    125       *error = base::ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle);
    126       return scoped_ptr<ActionInfo>();
    127     }
    128   } else if (extension->manifest_version() == 1 && dict->HasKey(keys::kName)) {
    129     if (!dict->GetString(keys::kName, &result->default_title)) {
    130       *error = base::ASCIIToUTF16(errors::kInvalidPageActionName);
    131       return scoped_ptr<ActionInfo>();
    132     }
    133   }
    134 
    135   // Read the action's |popup| (optional).
    136   const char* popup_key = NULL;
    137   if (dict->HasKey(keys::kPageActionDefaultPopup))
    138     popup_key = keys::kPageActionDefaultPopup;
    139 
    140   if (extension->manifest_version() == 1 &&
    141       dict->HasKey(keys::kPageActionPopup)) {
    142     if (popup_key) {
    143       *error = ErrorUtils::FormatErrorMessageUTF16(
    144           errors::kInvalidPageActionOldAndNewKeys,
    145           keys::kPageActionDefaultPopup,
    146           keys::kPageActionPopup);
    147       return scoped_ptr<ActionInfo>();
    148     }
    149     popup_key = keys::kPageActionPopup;
    150   }
    151 
    152   if (popup_key) {
    153     const base::DictionaryValue* popup = NULL;
    154     std::string url_str;
    155 
    156     if (dict->GetString(popup_key, &url_str)) {
    157       // On success, |url_str| is set.  Nothing else to do.
    158     } else if (extension->manifest_version() == 1 &&
    159                dict->GetDictionary(popup_key, &popup)) {
    160       if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) {
    161         *error = ErrorUtils::FormatErrorMessageUTF16(
    162             errors::kInvalidPageActionPopupPath, "<missing>");
    163         return scoped_ptr<ActionInfo>();
    164       }
    165     } else {
    166       *error = base::ASCIIToUTF16(errors::kInvalidPageActionPopup);
    167       return scoped_ptr<ActionInfo>();
    168     }
    169 
    170     if (!url_str.empty()) {
    171       // An empty string is treated as having no popup.
    172       result->default_popup_url = Extension::GetResourceURL(extension->url(),
    173                                                             url_str);
    174       if (!result->default_popup_url.is_valid()) {
    175         *error = ErrorUtils::FormatErrorMessageUTF16(
    176             errors::kInvalidPageActionPopupPath, url_str);
    177         return scoped_ptr<ActionInfo>();
    178       }
    179     } else {
    180       DCHECK(result->default_popup_url.is_empty())
    181           << "Shouldn't be possible for the popup to be set.";
    182     }
    183   }
    184 
    185   return result.Pass();
    186 }
    187 
    188 // static
    189 const ActionInfo* ActionInfo::GetBrowserActionInfo(const Extension* extension) {
    190   return GetActionInfo(extension, keys::kBrowserAction);
    191 }
    192 
    193 const ActionInfo* ActionInfo::GetPageActionInfo(const Extension* extension) {
    194   return GetActionInfo(extension, keys::kPageAction);
    195 }
    196 
    197 // static
    198 const ActionInfo* ActionInfo::GetSystemIndicatorInfo(
    199     const Extension* extension) {
    200   return GetActionInfo(extension, keys::kSystemIndicator);
    201 }
    202 
    203 // static
    204 void ActionInfo::SetBrowserActionInfo(Extension* extension, ActionInfo* info) {
    205   extension->SetManifestData(keys::kBrowserAction,
    206                              new ActionInfoData(info));
    207 }
    208 
    209 // static
    210 void ActionInfo::SetPageActionInfo(Extension* extension, ActionInfo* info) {
    211   extension->SetManifestData(keys::kPageAction,
    212                              new ActionInfoData(info));
    213 }
    214 
    215 // static
    216 void ActionInfo::SetSystemIndicatorInfo(Extension* extension,
    217                                         ActionInfo* info) {
    218   extension->SetManifestData(keys::kSystemIndicator, new ActionInfoData(info));
    219 }
    220 
    221 // static
    222 bool ActionInfo::IsVerboseInstallMessage(const Extension* extension) {
    223   const ActionInfo* page_action_info = GetPageActionInfo(extension);
    224   return page_action_info &&
    225       (CommandsInfo::GetPageActionCommand(extension) ||
    226        !page_action_info->default_icon.empty());
    227 }
    228 
    229 }  // namespace extensions
    230