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 <memory>
8
9 #include "base/strings/utf_string_conversions.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 constexpr char kEnabled[] = "enabled";
24 constexpr char kDisabled[] = "disabled";
25
26 // The manifest data container for the ActionInfos for BrowserActions and
27 // PageActions.
28 struct ActionInfoData : public Extension::ManifestData {
29 explicit ActionInfoData(std::unique_ptr<ActionInfo> action_info);
30 ~ActionInfoData() override;
31
32 // The action associated with the BrowserAction.
33 std::unique_ptr<ActionInfo> action_info;
34 };
35
ActionInfoData(std::unique_ptr<ActionInfo> info)36 ActionInfoData::ActionInfoData(std::unique_ptr<ActionInfo> info)
37 : action_info(std::move(info)) {}
38
~ActionInfoData()39 ActionInfoData::~ActionInfoData() {
40 }
41
GetActionInfo(const Extension * extension,const std::string & key)42 static const ActionInfo* GetActionInfo(const Extension* extension,
43 const std::string& key) {
44 ActionInfoData* data = static_cast<ActionInfoData*>(
45 extension->GetManifestData(key));
46 return data ? data->action_info.get() : NULL;
47 }
48
49 } // namespace
50
ActionInfo(Type type)51 ActionInfo::ActionInfo(Type type) : type(type), synthesized(false) {
52 switch (type) {
53 case TYPE_PAGE:
54 default_state = STATE_DISABLED;
55 break;
56 case TYPE_BROWSER:
57 case TYPE_ACTION:
58 default_state = STATE_ENABLED;
59 break;
60 }
61 }
62
63 ActionInfo::ActionInfo(const ActionInfo& other) = default;
64
~ActionInfo()65 ActionInfo::~ActionInfo() {
66 }
67
68 // static
Load(const Extension * extension,Type type,const base::DictionaryValue * dict,base::string16 * error)69 std::unique_ptr<ActionInfo> ActionInfo::Load(const Extension* extension,
70 Type type,
71 const base::DictionaryValue* dict,
72 base::string16* error) {
73 auto result = std::make_unique<ActionInfo>(type);
74
75 // Read the page action |default_icon| (optional).
76 // The |default_icon| value can be either dictionary {icon size -> icon path}
77 // or non empty string value.
78 if (dict->HasKey(keys::kActionDefaultIcon)) {
79 const base::DictionaryValue* icons_value = NULL;
80 std::string default_icon;
81 if (dict->GetDictionary(keys::kActionDefaultIcon, &icons_value)) {
82 if (!manifest_handler_helpers::LoadIconsFromDictionary(
83 icons_value, &result->default_icon, error)) {
84 return nullptr;
85 }
86 } else if (dict->GetString(keys::kActionDefaultIcon, &default_icon) &&
87 manifest_handler_helpers::NormalizeAndValidatePath(
88 &default_icon)) {
89 // Choose the most optimistic (highest) icon density regardless of the
90 // actual icon resolution, whatever that happens to be. Code elsewhere
91 // knows how to scale down to 19.
92 result->default_icon.Add(extension_misc::EXTENSION_ICON_GIGANTOR,
93 default_icon);
94 } else {
95 *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultIcon);
96 return nullptr;
97 }
98 }
99
100 // Read the page action title from |default_title| if present, |name| if not
101 // (both optional).
102 if (dict->HasKey(keys::kActionDefaultTitle)) {
103 if (!dict->GetString(keys::kActionDefaultTitle, &result->default_title)) {
104 *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultTitle);
105 return nullptr;
106 }
107 }
108
109 // Read the action's default popup (optional).
110 if (dict->HasKey(keys::kActionDefaultPopup)) {
111 std::string url_str;
112 if (!dict->GetString(keys::kActionDefaultPopup, &url_str)) {
113 *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultPopup);
114 return nullptr;
115 }
116
117 if (!url_str.empty()) {
118 // An empty string is treated as having no popup.
119 result->default_popup_url = Extension::GetResourceURL(extension->url(),
120 url_str);
121 if (!result->default_popup_url.is_valid()) {
122 *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultPopup);
123 return nullptr;
124 }
125 } else {
126 DCHECK(result->default_popup_url.is_empty())
127 << "Shouldn't be possible for the popup to be set.";
128 }
129 }
130
131 if (dict->HasKey(keys::kActionDefaultState)) {
132 // The default_state key is only valid for TYPE_ACTION; throw an error for
133 // others.
134 if (type != TYPE_ACTION) {
135 *error = base::ASCIIToUTF16(errors::kDefaultStateShouldNotBeSet);
136 return nullptr;
137 }
138
139 std::string default_state;
140 if (!dict->GetString(keys::kActionDefaultState, &default_state) ||
141 !(default_state == kEnabled || default_state == kDisabled)) {
142 *error = base::ASCIIToUTF16(errors::kInvalidActionDefaultState);
143 return nullptr;
144 }
145 result->default_state = default_state == kEnabled
146 ? ActionInfo::STATE_ENABLED
147 : ActionInfo::STATE_DISABLED;
148 }
149
150 return result;
151 }
152
153 // static
GetAnyActionInfo(const Extension * extension)154 const ActionInfo* ActionInfo::GetAnyActionInfo(const Extension* extension) {
155 // TODO(devlin): Since all actions are mutually exclusive, we can store
156 // them all under the same key. For now, we don't do that because some callers
157 // need to differentiate between action types.
158 const ActionInfo* info = GetActionInfo(extension, keys::kBrowserAction);
159 if (info)
160 return info;
161 info = GetActionInfo(extension, keys::kPageAction);
162 if (info)
163 return info;
164 return GetActionInfo(extension, keys::kAction);
165 }
166
167 // static
GetExtensionActionInfo(const Extension * extension)168 const ActionInfo* ActionInfo::GetExtensionActionInfo(
169 const Extension* extension) {
170 return GetActionInfo(extension, keys::kAction);
171 }
172
173 // static
GetBrowserActionInfo(const Extension * extension)174 const ActionInfo* ActionInfo::GetBrowserActionInfo(const Extension* extension) {
175 return GetActionInfo(extension, keys::kBrowserAction);
176 }
177
GetPageActionInfo(const Extension * extension)178 const ActionInfo* ActionInfo::GetPageActionInfo(const Extension* extension) {
179 return GetActionInfo(extension, keys::kPageAction);
180 }
181
182 // static
SetExtensionActionInfo(Extension * extension,std::unique_ptr<ActionInfo> info)183 void ActionInfo::SetExtensionActionInfo(Extension* extension,
184 std::unique_ptr<ActionInfo> info) {
185 extension->SetManifestData(keys::kAction,
186 std::make_unique<ActionInfoData>(std::move(info)));
187 }
188
189 // static
SetBrowserActionInfo(Extension * extension,std::unique_ptr<ActionInfo> info)190 void ActionInfo::SetBrowserActionInfo(Extension* extension,
191 std::unique_ptr<ActionInfo> info) {
192 extension->SetManifestData(keys::kBrowserAction,
193 std::make_unique<ActionInfoData>(std::move(info)));
194 }
195
196 // static
SetPageActionInfo(Extension * extension,std::unique_ptr<ActionInfo> info)197 void ActionInfo::SetPageActionInfo(Extension* extension,
198 std::unique_ptr<ActionInfo> info) {
199 extension->SetManifestData(keys::kPageAction,
200 std::make_unique<ActionInfoData>(std::move(info)));
201 }
202
203 } // namespace extensions
204