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 permissions
.insert(APIPermission::kFullAccess
);
88 } else if (automation_info_
->matches
.MatchesAllURLs()) {
89 if (automation_info_
->interact
) {
90 permissions
.insert(APIPermission::kHostsAll
);
92 permissions
.insert(APIPermission::kHostsAllReadOnly
);
95 // Check if we get any additional permissions from FilterHostPermissions.
96 URLPatternSet regular_hosts
;
97 ExtensionsClient::Get()->FilterHostPermissions(
98 automation_info_
->matches
, ®ular_hosts
, &permissions
);
99 std::set
<std::string
> hosts
=
100 permission_message_util::GetDistinctHosts(regular_hosts
, true, true);
101 if (!hosts
.empty()) {
102 permission_message_util::AddHostPermissions(
103 &permissions
, hosts
, automation_info_
->interact
104 ? permission_message_util::kReadWrite
105 : permission_message_util::kReadOnly
);
111 PermissionMessages
AutomationManifestPermission::GetMessages() const {
112 // When modifying this function, be careful to modify the functionality in
113 // GetPermissions() above as well.
114 PermissionMessages messages
;
115 if (automation_info_
->desktop
) {
116 messages
.push_back(PermissionMessage(
117 PermissionMessage::kFullAccess
,
118 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS
)));
119 } else if (automation_info_
->matches
.MatchesAllURLs()) {
120 if (automation_info_
->interact
) {
121 messages
.push_back(PermissionMessage(
122 PermissionMessage::kHostsAll
,
123 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS
)));
125 messages
.push_back(PermissionMessage(
126 PermissionMessage::kHostsAllReadOnly
,
127 l10n_util::GetStringUTF16(
128 IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS_READ_ONLY
)));
131 URLPatternSet regular_hosts
;
132 std::set
<PermissionMessage
> message_set
;
133 ExtensionsClient::Get()->FilterHostPermissions(
134 automation_info_
->matches
, ®ular_hosts
, &message_set
);
135 messages
.insert(messages
.end(), message_set
.begin(), message_set
.end());
137 std::set
<std::string
> hosts
=
138 permission_message_util::GetDistinctHosts(regular_hosts
, true, true);
139 if (!hosts
.empty()) {
140 messages
.push_back(permission_message_util::CreateFromHostList(
142 automation_info_
->interact
? permission_message_util::kReadWrite
143 : permission_message_util::kReadOnly
));
150 bool AutomationManifestPermission::FromValue(const base::Value
* value
) {
151 base::string16 error
;
152 automation_info_
.reset(AutomationInfo::FromValue(*value
,
153 NULL
/* install_warnings */,
155 return error
.empty();
158 scoped_ptr
<base::Value
> AutomationManifestPermission::ToValue() const {
159 return AutomationInfo::ToValue(*automation_info_
).Pass();
162 ManifestPermission
* AutomationManifestPermission::Diff(
163 const ManifestPermission
* rhs
) const {
164 const AutomationManifestPermission
* other
=
165 static_cast<const AutomationManifestPermission
*>(rhs
);
167 bool desktop
= automation_info_
->desktop
&& !other
->automation_info_
->desktop
;
169 automation_info_
->interact
&& !other
->automation_info_
->interact
;
170 URLPatternSet matches
;
171 URLPatternSet::CreateDifference(
172 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
173 return new AutomationManifestPermission(
174 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
177 ManifestPermission
* AutomationManifestPermission::Union(
178 const ManifestPermission
* rhs
) const {
179 const AutomationManifestPermission
* other
=
180 static_cast<const AutomationManifestPermission
*>(rhs
);
182 bool desktop
= automation_info_
->desktop
|| other
->automation_info_
->desktop
;
184 automation_info_
->interact
|| other
->automation_info_
->interact
;
185 URLPatternSet matches
;
186 URLPatternSet::CreateUnion(
187 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
188 return new AutomationManifestPermission(
189 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
192 ManifestPermission
* AutomationManifestPermission::Intersect(
193 const ManifestPermission
* rhs
) const {
194 const AutomationManifestPermission
* other
=
195 static_cast<const AutomationManifestPermission
*>(rhs
);
197 bool desktop
= automation_info_
->desktop
&& other
->automation_info_
->desktop
;
199 automation_info_
->interact
&& other
->automation_info_
->interact
;
200 URLPatternSet matches
;
201 URLPatternSet::CreateIntersection(
202 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
203 return new AutomationManifestPermission(
204 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
207 AutomationHandler::AutomationHandler() {
210 AutomationHandler::~AutomationHandler() {
213 bool AutomationHandler::Parse(Extension
* extension
, base::string16
* error
) {
214 const base::Value
* automation
= NULL
;
215 CHECK(extension
->manifest()->Get(keys::kAutomation
, &automation
));
216 std::vector
<InstallWarning
> install_warnings
;
217 scoped_ptr
<AutomationInfo
> info
=
218 AutomationInfo::FromValue(*automation
, &install_warnings
, error
);
222 extension
->AddInstallWarnings(install_warnings
);
227 extension
->SetManifestData(keys::kAutomation
, info
.release());
231 const std::vector
<std::string
> AutomationHandler::Keys() const {
232 return SingleKey(keys::kAutomation
);
235 ManifestPermission
* AutomationHandler::CreatePermission() {
236 return new AutomationManifestPermission(
237 make_scoped_ptr(new const AutomationInfo
));
240 ManifestPermission
* AutomationHandler::CreateInitialRequiredPermission(
241 const Extension
* extension
) {
242 const AutomationInfo
* info
= AutomationInfo::Get(extension
);
244 return new AutomationManifestPermission(
245 make_scoped_ptr(new const AutomationInfo(
246 info
->desktop
, info
->matches
, info
->interact
)));
252 const AutomationInfo
* AutomationInfo::Get(const Extension
* extension
) {
253 return static_cast<AutomationInfo
*>(
254 extension
->GetManifestData(keys::kAutomation
));
258 scoped_ptr
<AutomationInfo
> AutomationInfo::FromValue(
259 const base::Value
& value
,
260 std::vector
<InstallWarning
>* install_warnings
,
261 base::string16
* error
) {
262 scoped_ptr
<Automation
> automation
= Automation::FromValue(value
, error
);
264 return scoped_ptr
<AutomationInfo
>();
266 if (automation
->as_boolean
) {
267 if (*automation
->as_boolean
)
268 return make_scoped_ptr(new AutomationInfo());
269 return scoped_ptr
<AutomationInfo
>();
271 const Automation::Object
& automation_object
= *automation
->as_object
;
273 bool desktop
= false;
274 bool interact
= false;
275 if (automation_object
.desktop
&& *automation_object
.desktop
) {
278 if (automation_object
.interact
&& !*automation_object
.interact
) {
279 // TODO(aboxhall): Do we want to allow this?
280 install_warnings
->push_back(
281 InstallWarning(automation_errors::kErrorDesktopTrueInteractFalse
));
283 } else if (automation_object
.interact
&& *automation_object
.interact
) {
287 URLPatternSet matches
;
288 bool specified_matches
= false;
289 if (automation_object
.matches
) {
291 install_warnings
->push_back(
292 InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified
));
294 specified_matches
= true;
296 for (std::vector
<std::string
>::iterator it
=
297 automation_object
.matches
->begin();
298 it
!= automation_object
.matches
->end();
300 // TODO(aboxhall): Refactor common logic from content_scripts_handler,
301 // manifest_url_handler and user_script.cc into a single location and
303 URLPattern
pattern(URLPattern::SCHEME_ALL
&
304 ~URLPattern::SCHEME_CHROMEUI
);
305 URLPattern::ParseResult parse_result
= pattern
.Parse(*it
);
307 if (parse_result
!= URLPattern::PARSE_SUCCESS
) {
308 install_warnings
->push_back(
309 InstallWarning(ErrorUtils::FormatErrorMessage(
310 automation_errors::kErrorInvalidMatch
,
312 URLPattern::GetParseResultString(parse_result
))));
316 matches
.AddPattern(pattern
);
320 if (specified_matches
&& matches
.is_empty()) {
321 install_warnings
->push_back(
322 InstallWarning(automation_errors::kErrorNoMatchesProvided
));
325 return make_scoped_ptr(new AutomationInfo(desktop
, matches
, interact
));
329 scoped_ptr
<base::Value
> AutomationInfo::ToValue(const AutomationInfo
& info
) {
330 return AsManifestType(info
)->ToValue().Pass();
334 scoped_ptr
<Automation
> AutomationInfo::AsManifestType(
335 const AutomationInfo
& info
) {
336 scoped_ptr
<Automation
> automation(new Automation
);
337 if (!info
.desktop
&& !info
.interact
&& info
.matches
.size() == 0) {
338 automation
->as_boolean
.reset(new bool(true));
339 return automation
.Pass();
342 Automation::Object
* as_object
= new Automation::Object
;
343 as_object
->desktop
.reset(new bool(info
.desktop
));
344 as_object
->interact
.reset(new bool(info
.interact
));
345 if (info
.matches
.size() > 0) {
346 as_object
->matches
.reset(info
.matches
.ToStringVector().release());
348 automation
->as_object
.reset(as_object
);
349 return automation
.Pass();
352 AutomationInfo::AutomationInfo() : desktop(false), interact(false) {
355 AutomationInfo::AutomationInfo(bool desktop
,
356 const URLPatternSet matches
,
358 : desktop(desktop
), matches(matches
), interact(interact
) {
361 AutomationInfo::~AutomationInfo() {
364 } // namespace extensions