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.
5 #include "chrome/common/extensions/api/extension_action/action_info.h"
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"
16 namespace extensions
{
18 namespace errors
= manifest_errors
;
19 namespace keys
= manifest_keys
;
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 ~ActionInfoData() override
;
29 // The action associated with the BrowserAction.
30 scoped_ptr
<ActionInfo
> action_info
;
33 ActionInfoData::ActionInfoData(ActionInfo
* info
) : action_info(info
) {
36 ActionInfoData::~ActionInfoData() {
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
;
48 ActionInfo::ActionInfo() {
51 ActionInfo::~ActionInfo() {
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());
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();
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
>();
74 result
->default_icon
.Add(extension_misc::EXTENSION_ICON_ACTION
, path
);
78 if (dict
->HasKey(keys::kPageActionId
)) {
79 if (!dict
->GetString(keys::kPageActionId
, &id
)) {
80 *error
= base::ASCIIToUTF16(errors::kInvalidPageActionId
);
81 return scoped_ptr
<ActionInfo
>();
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(
100 extension_misc::kNumExtensionActionIconSizes
,
101 &result
->default_icon
,
103 return scoped_ptr
<ActionInfo
>();
105 } else if (dict
->GetString(keys::kPageActionDefaultIcon
, &default_icon
) &&
106 manifest_handler_helpers::NormalizeAndValidatePath(
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
,
115 *error
= base::ASCIIToUTF16(errors::kInvalidPageActionIconPath
);
116 return scoped_ptr
<ActionInfo
>();
120 // Read the page action title from |default_title| if present, |name| if not
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
>();
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
>();
135 // Read the action's |popup| (optional).
136 const char* popup_key
= NULL
;
137 if (dict
->HasKey(keys::kPageActionDefaultPopup
))
138 popup_key
= keys::kPageActionDefaultPopup
;
140 if (extension
->manifest_version() == 1 &&
141 dict
->HasKey(keys::kPageActionPopup
)) {
143 *error
= ErrorUtils::FormatErrorMessageUTF16(
144 errors::kInvalidPageActionOldAndNewKeys
,
145 keys::kPageActionDefaultPopup
,
146 keys::kPageActionPopup
);
147 return scoped_ptr
<ActionInfo
>();
149 popup_key
= keys::kPageActionPopup
;
153 const base::DictionaryValue
* popup
= NULL
;
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
>();
166 *error
= base::ASCIIToUTF16(errors::kInvalidPageActionPopup
);
167 return scoped_ptr
<ActionInfo
>();
170 if (!url_str
.empty()) {
171 // An empty string is treated as having no popup.
172 result
->default_popup_url
= Extension::GetResourceURL(extension
->url(),
174 if (!result
->default_popup_url
.is_valid()) {
175 *error
= ErrorUtils::FormatErrorMessageUTF16(
176 errors::kInvalidPageActionPopupPath
, url_str
);
177 return scoped_ptr
<ActionInfo
>();
180 DCHECK(result
->default_popup_url
.is_empty())
181 << "Shouldn't be possible for the popup to be set.";
185 return result
.Pass();
189 const ActionInfo
* ActionInfo::GetBrowserActionInfo(const Extension
* extension
) {
190 return GetActionInfo(extension
, keys::kBrowserAction
);
193 const ActionInfo
* ActionInfo::GetPageActionInfo(const Extension
* extension
) {
194 return GetActionInfo(extension
, keys::kPageAction
);
198 const ActionInfo
* ActionInfo::GetSystemIndicatorInfo(
199 const Extension
* extension
) {
200 return GetActionInfo(extension
, keys::kSystemIndicator
);
204 void ActionInfo::SetBrowserActionInfo(Extension
* extension
, ActionInfo
* info
) {
205 extension
->SetManifestData(keys::kBrowserAction
,
206 new ActionInfoData(info
));
210 void ActionInfo::SetPageActionInfo(Extension
* extension
, ActionInfo
* info
) {
211 extension
->SetManifestData(keys::kPageAction
,
212 new ActionInfoData(info
));
216 void ActionInfo::SetSystemIndicatorInfo(Extension
* extension
,
218 extension
->SetManifestData(keys::kSystemIndicator
, new ActionInfoData(info
));
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());
229 } // namespace extensions