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