• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ActionInfoData(ActionInfo * info)33 ActionInfoData::ActionInfoData(ActionInfo* info) : action_info(info) {
34 }
35 
~ActionInfoData()36 ActionInfoData::~ActionInfoData() {
37 }
38 
GetActionInfo(const Extension * extension,const std::string & key)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 
ActionInfo()48 ActionInfo::ActionInfo() {
49 }
50 
~ActionInfo()51 ActionInfo::~ActionInfo() {
52 }
53 
54 // static
Load(const Extension * extension,const base::DictionaryValue * dict,base::string16 * error)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
GetBrowserActionInfo(const Extension * extension)189 const ActionInfo* ActionInfo::GetBrowserActionInfo(const Extension* extension) {
190   return GetActionInfo(extension, keys::kBrowserAction);
191 }
192 
GetPageActionInfo(const Extension * extension)193 const ActionInfo* ActionInfo::GetPageActionInfo(const Extension* extension) {
194   return GetActionInfo(extension, keys::kPageAction);
195 }
196 
197 // static
GetSystemIndicatorInfo(const Extension * extension)198 const ActionInfo* ActionInfo::GetSystemIndicatorInfo(
199     const Extension* extension) {
200   return GetActionInfo(extension, keys::kSystemIndicator);
201 }
202 
203 // static
SetBrowserActionInfo(Extension * extension,ActionInfo * info)204 void ActionInfo::SetBrowserActionInfo(Extension* extension, ActionInfo* info) {
205   extension->SetManifestData(keys::kBrowserAction,
206                              new ActionInfoData(info));
207 }
208 
209 // static
SetPageActionInfo(Extension * extension,ActionInfo * info)210 void ActionInfo::SetPageActionInfo(Extension* extension, ActionInfo* info) {
211   extension->SetManifestData(keys::kPageAction,
212                              new ActionInfoData(info));
213 }
214 
215 // static
SetSystemIndicatorInfo(Extension * extension,ActionInfo * info)216 void ActionInfo::SetSystemIndicatorInfo(Extension* extension,
217                                         ActionInfo* info) {
218   extension->SetManifestData(keys::kSystemIndicator, new ActionInfoData(info));
219 }
220 
221 // static
IsVerboseInstallMessage(const Extension * extension)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