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.h" 11 #include "chrome/common/extensions/extension_manifest_constants.h" 12 #include "chrome/common/extensions/manifest_handler_helpers.h" 13 #include "extensions/common/error_utils.h" 14 15 namespace errors = extension_manifest_errors; 16 namespace keys = extension_manifest_keys; 17 18 namespace extensions { 19 20 namespace { 21 22 // The manifest data container for the ActionInfos for BrowserActions and 23 // ScriptBadges. 24 struct ActionInfoData : public Extension::ManifestData { 25 explicit ActionInfoData(ActionInfo* action_info); 26 virtual ~ActionInfoData(); 27 28 // The action associated with the BrowserAction or ScriptBadge. 29 // This is never NULL for ScriptBadge. 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 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 for (base::ListValue::const_iterator iter = icons->begin(); 67 iter != icons->end(); ++iter) { 68 std::string path; 69 if (!(*iter)->GetAsString(&path) || 70 !manifest_handler_helpers::NormalizeAndValidatePath(&path)) { 71 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); 72 return scoped_ptr<ActionInfo>(); 73 } 74 75 result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, path); 76 break; 77 } 78 } 79 80 std::string id; 81 if (dict->HasKey(keys::kPageActionId)) { 82 if (!dict->GetString(keys::kPageActionId, &id)) { 83 *error = ASCIIToUTF16(errors::kInvalidPageActionId); 84 return scoped_ptr<ActionInfo>(); 85 } 86 result->id = id; 87 } 88 } 89 90 // Read the page action |default_icon| (optional). 91 // The |default_icon| value can be either dictionary {icon size -> icon path} 92 // or non empty string value. 93 if (dict->HasKey(keys::kPageActionDefaultIcon)) { 94 const DictionaryValue* icons_value = NULL; 95 std::string default_icon; 96 if (dict->GetDictionary(keys::kPageActionDefaultIcon, &icons_value)) { 97 if (!manifest_handler_helpers::LoadIconsFromDictionary( 98 icons_value, 99 extension_misc::kExtensionActionIconSizes, 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 result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, 109 default_icon); 110 } else { 111 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); 112 return scoped_ptr<ActionInfo>(); 113 } 114 } 115 116 // Read the page action title from |default_title| if present, |name| if not 117 // (both optional). 118 if (dict->HasKey(keys::kPageActionDefaultTitle)) { 119 if (!dict->GetString(keys::kPageActionDefaultTitle, 120 &result->default_title)) { 121 *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); 122 return scoped_ptr<ActionInfo>(); 123 } 124 } else if (extension->manifest_version() == 1 && dict->HasKey(keys::kName)) { 125 if (!dict->GetString(keys::kName, &result->default_title)) { 126 *error = ASCIIToUTF16(errors::kInvalidPageActionName); 127 return scoped_ptr<ActionInfo>(); 128 } 129 } 130 131 // Read the action's |popup| (optional). 132 const char* popup_key = NULL; 133 if (dict->HasKey(keys::kPageActionDefaultPopup)) 134 popup_key = keys::kPageActionDefaultPopup; 135 136 if (extension->manifest_version() == 1 && 137 dict->HasKey(keys::kPageActionPopup)) { 138 if (popup_key) { 139 *error = ErrorUtils::FormatErrorMessageUTF16( 140 errors::kInvalidPageActionOldAndNewKeys, 141 keys::kPageActionDefaultPopup, 142 keys::kPageActionPopup); 143 return scoped_ptr<ActionInfo>(); 144 } 145 popup_key = keys::kPageActionPopup; 146 } 147 148 if (popup_key) { 149 const DictionaryValue* popup = NULL; 150 std::string url_str; 151 152 if (dict->GetString(popup_key, &url_str)) { 153 // On success, |url_str| is set. Nothing else to do. 154 } else if (extension->manifest_version() == 1 && 155 dict->GetDictionary(popup_key, &popup)) { 156 if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) { 157 *error = ErrorUtils::FormatErrorMessageUTF16( 158 errors::kInvalidPageActionPopupPath, "<missing>"); 159 return scoped_ptr<ActionInfo>(); 160 } 161 } else { 162 *error = ASCIIToUTF16(errors::kInvalidPageActionPopup); 163 return scoped_ptr<ActionInfo>(); 164 } 165 166 if (!url_str.empty()) { 167 // An empty string is treated as having no popup. 168 result->default_popup_url = Extension::GetResourceURL(extension->url(), 169 url_str); 170 if (!result->default_popup_url.is_valid()) { 171 *error = ErrorUtils::FormatErrorMessageUTF16( 172 errors::kInvalidPageActionPopupPath, url_str); 173 return scoped_ptr<ActionInfo>(); 174 } 175 } else { 176 DCHECK(result->default_popup_url.is_empty()) 177 << "Shouldn't be possible for the popup to be set."; 178 } 179 } 180 181 return result.Pass(); 182 } 183 184 // static 185 const ActionInfo* ActionInfo::GetBrowserActionInfo(const Extension* extension) { 186 return GetActionInfo(extension, keys::kBrowserAction); 187 } 188 189 const ActionInfo* ActionInfo::GetPageActionInfo(const Extension* extension) { 190 return GetActionInfo(extension, keys::kPageAction); 191 } 192 193 // static 194 const ActionInfo* ActionInfo::GetScriptBadgeInfo(const Extension* extension) { 195 return GetActionInfo(extension, keys::kScriptBadge); 196 } 197 198 // static 199 const ActionInfo* ActionInfo::GetSystemIndicatorInfo( 200 const Extension* extension) { 201 return GetActionInfo(extension, keys::kSystemIndicator); 202 } 203 204 // static 205 void ActionInfo::SetBrowserActionInfo(Extension* extension, ActionInfo* info) { 206 extension->SetManifestData(keys::kBrowserAction, 207 new ActionInfoData(info)); 208 } 209 210 // static 211 void ActionInfo::SetPageActionInfo(Extension* extension, ActionInfo* info) { 212 extension->SetManifestData(keys::kPageAction, 213 new ActionInfoData(info)); 214 } 215 216 // static 217 void ActionInfo::SetScriptBadgeInfo(Extension* extension, ActionInfo* info) { 218 extension->SetManifestData(keys::kScriptBadge, 219 new ActionInfoData(info)); 220 } 221 222 // static 223 void ActionInfo::SetSystemIndicatorInfo(Extension* extension, 224 ActionInfo* info) { 225 extension->SetManifestData(keys::kSystemIndicator, new ActionInfoData(info)); 226 } 227 228 // static 229 bool ActionInfo::IsVerboseInstallMessage(const Extension* extension) { 230 const ActionInfo* page_action_info = GetPageActionInfo(extension); 231 return page_action_info && 232 (CommandsInfo::GetPageActionCommand(extension) || 233 !page_action_info->default_icon.empty()); 234 } 235 236 } // namespace extensions 237