1 // Copyright 2014 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/manifest_handlers/automation.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/common/extensions/api/manifest_types.h"
9 #include "chrome/grit/generated_resources.h"
10 #include "extensions/common/error_utils.h"
11 #include "extensions/common/extensions_client.h"
12 #include "extensions/common/manifest_constants.h"
13 #include "extensions/common/permissions/api_permission_set.h"
14 #include "extensions/common/permissions/manifest_permission.h"
15 #include "extensions/common/permissions/permission_message.h"
16 #include "extensions/common/permissions/permission_message_util.h"
17 #include "extensions/common/permissions/permissions_data.h"
18 #include "extensions/common/url_pattern.h"
19 #include "ipc/ipc_message.h"
20 #include "ipc/ipc_message_utils.h"
21 #include "ui/base/l10n/l10n_util.h"
23 namespace extensions
{
25 namespace automation_errors
{
26 const char kErrorDesktopTrueInteractFalse
[] =
27 "Cannot specify interactive=false if desktop=true is specified; "
28 "interactive=false will be ignored.";
29 const char kErrorDesktopTrueMatchesSpecified
[] =
30 "Cannot specify matches for Automation if desktop=true is specified; "
31 "matches will be ignored.";
32 const char kErrorInvalidMatch
[] = "Invalid match pattern '*': *";
33 const char kErrorNoMatchesProvided
[] = "No valid match patterns provided.";
36 namespace errors
= manifest_errors
;
37 namespace keys
= extensions::manifest_keys
;
38 using api::manifest_types::Automation
;
40 class AutomationManifestPermission
: public ManifestPermission
{
42 explicit AutomationManifestPermission(
43 scoped_ptr
<const AutomationInfo
> automation_info
)
44 : automation_info_(automation_info
.Pass()) {}
46 // extensions::ManifestPermission overrides.
47 std::string
name() const override
;
49 std::string
id() const override
;
51 PermissionIDSet
GetPermissions() const override
;
53 bool HasMessages() const override
;
55 PermissionMessages
GetMessages() const override
;
57 bool FromValue(const base::Value
* value
) override
;
59 scoped_ptr
<base::Value
> ToValue() const override
;
61 ManifestPermission
* Diff(const ManifestPermission
* rhs
) const override
;
63 ManifestPermission
* Union(const ManifestPermission
* rhs
) const override
;
65 ManifestPermission
* Intersect(const ManifestPermission
* rhs
) const override
;
68 scoped_ptr
<const AutomationInfo
> automation_info_
;
71 std::string
AutomationManifestPermission::name() const {
72 return keys::kAutomation
;
75 std::string
AutomationManifestPermission::id() const {
76 return keys::kAutomation
;
79 bool AutomationManifestPermission::HasMessages() const {
80 return GetMessages().size() > 0;
83 PermissionIDSet
AutomationManifestPermission::GetPermissions() const {
84 // Meant to mimic the behavior of GetMessages().
85 PermissionIDSet permissions
;
86 if (automation_info_
->desktop
) {
87 // TODO(sashab): Add the rule
88 // kFullAccess -> IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS
89 // to ChromePermissionMessageProvider, when it exists.
90 permissions
.insert(APIPermission::kFullAccess
);
91 } else if (automation_info_
->matches
.MatchesAllURLs()) {
92 if (automation_info_
->interact
) {
93 permissions
.insert(APIPermission::kHostsAll
);
94 // TODO(sashab): Add the rule
95 // kHostsAll -> IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS
96 // to ChromePermissionMessageProvider, when it exists.
98 permissions
.insert(APIPermission::kHostsAllReadOnly
);
99 // TODO(sashab): Add the rule
100 // kHostsAllReadOnly -> IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS_READ_ONLY
101 // to ChromePermissionMessageProvider, when it exists.
104 // Check if we get any additional permissions from FilterHostPermissions.
105 URLPatternSet regular_hosts
;
106 ExtensionsClient::Get()->FilterHostPermissions(
107 automation_info_
->matches
, ®ular_hosts
, &permissions
);
108 std::set
<std::string
> hosts
=
109 permission_message_util::GetDistinctHosts(regular_hosts
, true, true);
110 if (!hosts
.empty()) {
111 permission_message_util::AddHostPermissions(
112 &permissions
, hosts
, automation_info_
->interact
113 ? permission_message_util::kReadWrite
114 : permission_message_util::kReadOnly
);
120 PermissionMessages
AutomationManifestPermission::GetMessages() const {
121 // When modifying this function, be careful to modify the functionality in
122 // GetPermissions() above as well.
123 PermissionMessages messages
;
124 if (automation_info_
->desktop
) {
125 messages
.push_back(PermissionMessage(
126 PermissionMessage::kFullAccess
,
127 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS
)));
128 } else if (automation_info_
->matches
.MatchesAllURLs()) {
129 if (automation_info_
->interact
) {
130 messages
.push_back(PermissionMessage(
131 PermissionMessage::kHostsAll
,
132 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS
)));
134 messages
.push_back(PermissionMessage(
135 PermissionMessage::kHostsAllReadOnly
,
136 l10n_util::GetStringUTF16(
137 IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS_READ_ONLY
)));
140 URLPatternSet regular_hosts
;
141 std::set
<PermissionMessage
> message_set
;
142 ExtensionsClient::Get()->FilterHostPermissions(
143 automation_info_
->matches
, ®ular_hosts
, &message_set
);
144 messages
.insert(messages
.end(), message_set
.begin(), message_set
.end());
146 std::set
<std::string
> hosts
=
147 permission_message_util::GetDistinctHosts(regular_hosts
, true, true);
148 if (!hosts
.empty()) {
149 messages
.push_back(permission_message_util::CreateFromHostList(
151 automation_info_
->interact
? permission_message_util::kReadWrite
152 : permission_message_util::kReadOnly
));
159 bool AutomationManifestPermission::FromValue(const base::Value
* value
) {
160 base::string16 error
;
161 automation_info_
.reset(AutomationInfo::FromValue(*value
,
162 NULL
/* install_warnings */,
164 return error
.empty();
167 scoped_ptr
<base::Value
> AutomationManifestPermission::ToValue() const {
168 return AutomationInfo::ToValue(*automation_info_
).Pass();
171 ManifestPermission
* AutomationManifestPermission::Diff(
172 const ManifestPermission
* rhs
) const {
173 const AutomationManifestPermission
* other
=
174 static_cast<const AutomationManifestPermission
*>(rhs
);
176 bool desktop
= automation_info_
->desktop
&& !other
->automation_info_
->desktop
;
178 automation_info_
->interact
&& !other
->automation_info_
->interact
;
179 URLPatternSet matches
;
180 URLPatternSet::CreateDifference(
181 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
182 return new AutomationManifestPermission(
183 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
186 ManifestPermission
* AutomationManifestPermission::Union(
187 const ManifestPermission
* rhs
) const {
188 const AutomationManifestPermission
* other
=
189 static_cast<const AutomationManifestPermission
*>(rhs
);
191 bool desktop
= automation_info_
->desktop
|| other
->automation_info_
->desktop
;
193 automation_info_
->interact
|| other
->automation_info_
->interact
;
194 URLPatternSet matches
;
195 URLPatternSet::CreateUnion(
196 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
197 return new AutomationManifestPermission(
198 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
201 ManifestPermission
* AutomationManifestPermission::Intersect(
202 const ManifestPermission
* rhs
) const {
203 const AutomationManifestPermission
* other
=
204 static_cast<const AutomationManifestPermission
*>(rhs
);
206 bool desktop
= automation_info_
->desktop
&& other
->automation_info_
->desktop
;
208 automation_info_
->interact
&& other
->automation_info_
->interact
;
209 URLPatternSet matches
;
210 URLPatternSet::CreateIntersection(
211 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
212 return new AutomationManifestPermission(
213 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
216 AutomationHandler::AutomationHandler() {
219 AutomationHandler::~AutomationHandler() {
222 bool AutomationHandler::Parse(Extension
* extension
, base::string16
* error
) {
223 const base::Value
* automation
= NULL
;
224 CHECK(extension
->manifest()->Get(keys::kAutomation
, &automation
));
225 std::vector
<InstallWarning
> install_warnings
;
226 scoped_ptr
<AutomationInfo
> info
=
227 AutomationInfo::FromValue(*automation
, &install_warnings
, error
);
231 extension
->AddInstallWarnings(install_warnings
);
236 extension
->SetManifestData(keys::kAutomation
, info
.release());
240 const std::vector
<std::string
> AutomationHandler::Keys() const {
241 return SingleKey(keys::kAutomation
);
244 ManifestPermission
* AutomationHandler::CreatePermission() {
245 return new AutomationManifestPermission(
246 make_scoped_ptr(new const AutomationInfo
));
249 ManifestPermission
* AutomationHandler::CreateInitialRequiredPermission(
250 const Extension
* extension
) {
251 const AutomationInfo
* info
= AutomationInfo::Get(extension
);
253 return new AutomationManifestPermission(
254 make_scoped_ptr(new const AutomationInfo(
255 info
->desktop
, info
->matches
, info
->interact
)));
261 const AutomationInfo
* AutomationInfo::Get(const Extension
* extension
) {
262 return static_cast<AutomationInfo
*>(
263 extension
->GetManifestData(keys::kAutomation
));
267 scoped_ptr
<AutomationInfo
> AutomationInfo::FromValue(
268 const base::Value
& value
,
269 std::vector
<InstallWarning
>* install_warnings
,
270 base::string16
* error
) {
271 scoped_ptr
<Automation
> automation
= Automation::FromValue(value
, error
);
273 return scoped_ptr
<AutomationInfo
>();
275 if (automation
->as_boolean
) {
276 if (*automation
->as_boolean
)
277 return make_scoped_ptr(new AutomationInfo());
278 return scoped_ptr
<AutomationInfo
>();
280 const Automation::Object
& automation_object
= *automation
->as_object
;
282 bool desktop
= false;
283 bool interact
= false;
284 if (automation_object
.desktop
&& *automation_object
.desktop
) {
287 if (automation_object
.interact
&& !*automation_object
.interact
) {
288 // TODO(aboxhall): Do we want to allow this?
289 install_warnings
->push_back(
290 InstallWarning(automation_errors::kErrorDesktopTrueInteractFalse
));
292 } else if (automation_object
.interact
&& *automation_object
.interact
) {
296 URLPatternSet matches
;
297 bool specified_matches
= false;
298 if (automation_object
.matches
) {
300 install_warnings
->push_back(
301 InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified
));
303 specified_matches
= true;
305 for (std::vector
<std::string
>::iterator it
=
306 automation_object
.matches
->begin();
307 it
!= automation_object
.matches
->end();
309 // TODO(aboxhall): Refactor common logic from content_scripts_handler,
310 // manifest_url_handler and user_script.cc into a single location and
312 URLPattern
pattern(URLPattern::SCHEME_ALL
&
313 ~URLPattern::SCHEME_CHROMEUI
);
314 URLPattern::ParseResult parse_result
= pattern
.Parse(*it
);
316 if (parse_result
!= URLPattern::PARSE_SUCCESS
) {
317 install_warnings
->push_back(
318 InstallWarning(ErrorUtils::FormatErrorMessage(
319 automation_errors::kErrorInvalidMatch
,
321 URLPattern::GetParseResultString(parse_result
))));
325 matches
.AddPattern(pattern
);
329 if (specified_matches
&& matches
.is_empty()) {
330 install_warnings
->push_back(
331 InstallWarning(automation_errors::kErrorNoMatchesProvided
));
334 return make_scoped_ptr(new AutomationInfo(desktop
, matches
, interact
));
338 scoped_ptr
<base::Value
> AutomationInfo::ToValue(const AutomationInfo
& info
) {
339 return AsManifestType(info
)->ToValue().Pass();
343 scoped_ptr
<Automation
> AutomationInfo::AsManifestType(
344 const AutomationInfo
& info
) {
345 scoped_ptr
<Automation
> automation(new Automation
);
346 if (!info
.desktop
&& !info
.interact
&& info
.matches
.size() == 0) {
347 automation
->as_boolean
.reset(new bool(true));
348 return automation
.Pass();
351 Automation::Object
* as_object
= new Automation::Object
;
352 as_object
->desktop
.reset(new bool(info
.desktop
));
353 as_object
->interact
.reset(new bool(info
.interact
));
354 if (info
.matches
.size() > 0) {
355 as_object
->matches
.reset(info
.matches
.ToStringVector().release());
357 automation
->as_object
.reset(as_object
);
358 return automation
.Pass();
361 AutomationInfo::AutomationInfo() : desktop(false), interact(false) {
364 AutomationInfo::AutomationInfo(bool desktop
,
365 const URLPatternSet matches
,
367 : desktop(desktop
), matches(matches
), interact(interact
) {
370 AutomationInfo::~AutomationInfo() {
373 } // namespace extensions