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