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 if (!manifest_handler_helpers::LoadIconsFromDictionary( 95 icons_value, 96 extension_misc::kExtensionActionIconSizes, 97 extension_misc::kNumExtensionActionIconSizes, 98 &result->default_icon, 99 error)) { 100 return scoped_ptr<ActionInfo>(); 101 } 102 } else if (dict->GetString(keys::kPageActionDefaultIcon, &default_icon) && 103 manifest_handler_helpers::NormalizeAndValidatePath( 104 &default_icon)) { 105 result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, 106 default_icon); 107 } else { 108 *error = base::ASCIIToUTF16(errors::kInvalidPageActionIconPath); 109 return scoped_ptr<ActionInfo>(); 110 } 111 } 112 113 // Read the page action title from |default_title| if present, |name| if not 114 // (both optional). 115 if (dict->HasKey(keys::kPageActionDefaultTitle)) { 116 if (!dict->GetString(keys::kPageActionDefaultTitle, 117 &result->default_title)) { 118 *error = base::ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); 119 return scoped_ptr<ActionInfo>(); 120 } 121 } else if (extension->manifest_version() == 1 && dict->HasKey(keys::kName)) { 122 if (!dict->GetString(keys::kName, &result->default_title)) { 123 *error = base::ASCIIToUTF16(errors::kInvalidPageActionName); 124 return scoped_ptr<ActionInfo>(); 125 } 126 } 127 128 // Read the action's |popup| (optional). 129 const char* popup_key = NULL; 130 if (dict->HasKey(keys::kPageActionDefaultPopup)) 131 popup_key = keys::kPageActionDefaultPopup; 132 133 if (extension->manifest_version() == 1 && 134 dict->HasKey(keys::kPageActionPopup)) { 135 if (popup_key) { 136 *error = ErrorUtils::FormatErrorMessageUTF16( 137 errors::kInvalidPageActionOldAndNewKeys, 138 keys::kPageActionDefaultPopup, 139 keys::kPageActionPopup); 140 return scoped_ptr<ActionInfo>(); 141 } 142 popup_key = keys::kPageActionPopup; 143 } 144 145 if (popup_key) { 146 const base::DictionaryValue* popup = NULL; 147 std::string url_str; 148 149 if (dict->GetString(popup_key, &url_str)) { 150 // On success, |url_str| is set. Nothing else to do. 151 } else if (extension->manifest_version() == 1 && 152 dict->GetDictionary(popup_key, &popup)) { 153 if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) { 154 *error = ErrorUtils::FormatErrorMessageUTF16( 155 errors::kInvalidPageActionPopupPath, "<missing>"); 156 return scoped_ptr<ActionInfo>(); 157 } 158 } else { 159 *error = base::ASCIIToUTF16(errors::kInvalidPageActionPopup); 160 return scoped_ptr<ActionInfo>(); 161 } 162 163 if (!url_str.empty()) { 164 // An empty string is treated as having no popup. 165 result->default_popup_url = Extension::GetResourceURL(extension->url(), 166 url_str); 167 if (!result->default_popup_url.is_valid()) { 168 *error = ErrorUtils::FormatErrorMessageUTF16( 169 errors::kInvalidPageActionPopupPath, url_str); 170 return scoped_ptr<ActionInfo>(); 171 } 172 } else { 173 DCHECK(result->default_popup_url.is_empty()) 174 << "Shouldn't be possible for the popup to be set."; 175 } 176 } 177 178 return result.Pass(); 179 } 180 181 // static 182 const ActionInfo* ActionInfo::GetBrowserActionInfo(const Extension* extension) { 183 return GetActionInfo(extension, keys::kBrowserAction); 184 } 185 186 const ActionInfo* ActionInfo::GetPageActionInfo(const Extension* extension) { 187 return GetActionInfo(extension, keys::kPageAction); 188 } 189 190 // static 191 const ActionInfo* ActionInfo::GetSystemIndicatorInfo( 192 const Extension* extension) { 193 return GetActionInfo(extension, keys::kSystemIndicator); 194 } 195 196 // static 197 void ActionInfo::SetBrowserActionInfo(Extension* extension, ActionInfo* info) { 198 extension->SetManifestData(keys::kBrowserAction, 199 new ActionInfoData(info)); 200 } 201 202 // static 203 void ActionInfo::SetPageActionInfo(Extension* extension, ActionInfo* info) { 204 extension->SetManifestData(keys::kPageAction, 205 new ActionInfoData(info)); 206 } 207 208 // static 209 void ActionInfo::SetSystemIndicatorInfo(Extension* extension, 210 ActionInfo* info) { 211 extension->SetManifestData(keys::kSystemIndicator, new ActionInfoData(info)); 212 } 213 214 // static 215 bool ActionInfo::IsVerboseInstallMessage(const Extension* extension) { 216 const ActionInfo* page_action_info = GetPageActionInfo(extension); 217 return page_action_info && 218 (CommandsInfo::GetPageActionCommand(extension) || 219 !page_action_info->default_icon.empty()); 220 } 221 222 } // namespace extensions 223