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