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_util.h"
16 #include "extensions/common/permissions/permissions_data.h"
17 #include "extensions/common/url_pattern.h"
18 #include "ipc/ipc_message.h"
19 #include "ipc/ipc_message_utils.h"
20 #include "ui/base/l10n/l10n_util.h"
22 namespace extensions
{
24 namespace automation_errors
{
25 const char kErrorDesktopTrueInteractFalse
[] =
26 "Cannot specify interactive=false if desktop=true is specified; "
27 "interactive=false will be ignored.";
28 const char kErrorDesktopTrueMatchesSpecified
[] =
29 "Cannot specify matches for Automation if desktop=true is specified; "
30 "matches will be ignored.";
31 const char kErrorInvalidMatch
[] = "Invalid match pattern '*': *";
32 const char kErrorNoMatchesProvided
[] = "No valid match patterns provided.";
35 namespace errors
= manifest_errors
;
36 namespace keys
= extensions::manifest_keys
;
37 using api::manifest_types::Automation
;
39 class AutomationManifestPermission
: public ManifestPermission
{
41 explicit AutomationManifestPermission(
42 scoped_ptr
<const AutomationInfo
> automation_info
)
43 : automation_info_(automation_info
.Pass()) {}
45 // extensions::ManifestPermission overrides.
46 std::string
name() const override
;
48 std::string
id() const override
;
50 PermissionIDSet
GetPermissions() const override
;
52 bool FromValue(const base::Value
* value
) override
;
54 scoped_ptr
<base::Value
> ToValue() const override
;
56 ManifestPermission
* Diff(const ManifestPermission
* rhs
) const override
;
58 ManifestPermission
* Union(const ManifestPermission
* rhs
) const override
;
60 ManifestPermission
* Intersect(const ManifestPermission
* rhs
) const override
;
63 scoped_ptr
<const AutomationInfo
> automation_info_
;
66 std::string
AutomationManifestPermission::name() const {
67 return keys::kAutomation
;
70 std::string
AutomationManifestPermission::id() const {
71 return keys::kAutomation
;
74 PermissionIDSet
AutomationManifestPermission::GetPermissions() const {
75 // Meant to mimic the behavior of GetMessages().
76 PermissionIDSet permissions
;
77 if (automation_info_
->desktop
) {
78 permissions
.insert(APIPermission::kFullAccess
);
79 } else if (automation_info_
->matches
.MatchesAllURLs()) {
80 if (automation_info_
->interact
) {
81 permissions
.insert(APIPermission::kHostsAll
);
83 permissions
.insert(APIPermission::kHostsAllReadOnly
);
86 // Check if we get any additional permissions from FilterHostPermissions.
87 URLPatternSet regular_hosts
;
88 ExtensionsClient::Get()->FilterHostPermissions(
89 automation_info_
->matches
, ®ular_hosts
, &permissions
);
90 std::set
<std::string
> hosts
=
91 permission_message_util::GetDistinctHosts(regular_hosts
, true, true);
93 permission_message_util::AddHostPermissions(
94 &permissions
, hosts
, automation_info_
->interact
95 ? permission_message_util::kReadWrite
96 : permission_message_util::kReadOnly
);
102 bool AutomationManifestPermission::FromValue(const base::Value
* value
) {
103 base::string16 error
;
104 automation_info_
.reset(AutomationInfo::FromValue(*value
,
105 NULL
/* install_warnings */,
107 return error
.empty();
110 scoped_ptr
<base::Value
> AutomationManifestPermission::ToValue() const {
111 return AutomationInfo::ToValue(*automation_info_
).Pass();
114 ManifestPermission
* AutomationManifestPermission::Diff(
115 const ManifestPermission
* rhs
) const {
116 const AutomationManifestPermission
* other
=
117 static_cast<const AutomationManifestPermission
*>(rhs
);
119 bool desktop
= automation_info_
->desktop
&& !other
->automation_info_
->desktop
;
121 automation_info_
->interact
&& !other
->automation_info_
->interact
;
122 URLPatternSet matches
;
123 URLPatternSet::CreateDifference(
124 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
125 return new AutomationManifestPermission(
126 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
129 ManifestPermission
* AutomationManifestPermission::Union(
130 const ManifestPermission
* rhs
) const {
131 const AutomationManifestPermission
* other
=
132 static_cast<const AutomationManifestPermission
*>(rhs
);
134 bool desktop
= automation_info_
->desktop
|| other
->automation_info_
->desktop
;
136 automation_info_
->interact
|| other
->automation_info_
->interact
;
137 URLPatternSet matches
;
138 URLPatternSet::CreateUnion(
139 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
140 return new AutomationManifestPermission(
141 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
144 ManifestPermission
* AutomationManifestPermission::Intersect(
145 const ManifestPermission
* rhs
) const {
146 const AutomationManifestPermission
* other
=
147 static_cast<const AutomationManifestPermission
*>(rhs
);
149 bool desktop
= automation_info_
->desktop
&& other
->automation_info_
->desktop
;
151 automation_info_
->interact
&& other
->automation_info_
->interact
;
152 URLPatternSet matches
;
153 URLPatternSet::CreateIntersection(
154 automation_info_
->matches
, other
->automation_info_
->matches
, &matches
);
155 return new AutomationManifestPermission(
156 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
159 AutomationHandler::AutomationHandler() {
162 AutomationHandler::~AutomationHandler() {
165 bool AutomationHandler::Parse(Extension
* extension
, base::string16
* error
) {
166 const base::Value
* automation
= NULL
;
167 CHECK(extension
->manifest()->Get(keys::kAutomation
, &automation
));
168 std::vector
<InstallWarning
> install_warnings
;
169 scoped_ptr
<AutomationInfo
> info
=
170 AutomationInfo::FromValue(*automation
, &install_warnings
, error
);
174 extension
->AddInstallWarnings(install_warnings
);
179 extension
->SetManifestData(keys::kAutomation
, info
.release());
183 const std::vector
<std::string
> AutomationHandler::Keys() const {
184 return SingleKey(keys::kAutomation
);
187 ManifestPermission
* AutomationHandler::CreatePermission() {
188 return new AutomationManifestPermission(
189 make_scoped_ptr(new const AutomationInfo
));
192 ManifestPermission
* AutomationHandler::CreateInitialRequiredPermission(
193 const Extension
* extension
) {
194 const AutomationInfo
* info
= AutomationInfo::Get(extension
);
196 return new AutomationManifestPermission(
197 make_scoped_ptr(new const AutomationInfo(
198 info
->desktop
, info
->matches
, info
->interact
)));
204 const AutomationInfo
* AutomationInfo::Get(const Extension
* extension
) {
205 return static_cast<AutomationInfo
*>(
206 extension
->GetManifestData(keys::kAutomation
));
210 scoped_ptr
<AutomationInfo
> AutomationInfo::FromValue(
211 const base::Value
& value
,
212 std::vector
<InstallWarning
>* install_warnings
,
213 base::string16
* error
) {
214 scoped_ptr
<Automation
> automation
= Automation::FromValue(value
, error
);
216 return scoped_ptr
<AutomationInfo
>();
218 if (automation
->as_boolean
) {
219 if (*automation
->as_boolean
)
220 return make_scoped_ptr(new AutomationInfo());
221 return scoped_ptr
<AutomationInfo
>();
223 const Automation::Object
& automation_object
= *automation
->as_object
;
225 bool desktop
= false;
226 bool interact
= false;
227 if (automation_object
.desktop
&& *automation_object
.desktop
) {
230 if (automation_object
.interact
&& !*automation_object
.interact
) {
231 // TODO(aboxhall): Do we want to allow this?
232 install_warnings
->push_back(
233 InstallWarning(automation_errors::kErrorDesktopTrueInteractFalse
));
235 } else if (automation_object
.interact
&& *automation_object
.interact
) {
239 URLPatternSet matches
;
240 bool specified_matches
= false;
241 if (automation_object
.matches
) {
243 install_warnings
->push_back(
244 InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified
));
246 specified_matches
= true;
248 for (std::vector
<std::string
>::iterator it
=
249 automation_object
.matches
->begin();
250 it
!= automation_object
.matches
->end();
252 // TODO(aboxhall): Refactor common logic from content_scripts_handler,
253 // manifest_url_handler and user_script.cc into a single location and
255 URLPattern
pattern(URLPattern::SCHEME_ALL
&
256 ~URLPattern::SCHEME_CHROMEUI
);
257 URLPattern::ParseResult parse_result
= pattern
.Parse(*it
);
259 if (parse_result
!= URLPattern::PARSE_SUCCESS
) {
260 install_warnings
->push_back(
261 InstallWarning(ErrorUtils::FormatErrorMessage(
262 automation_errors::kErrorInvalidMatch
,
264 URLPattern::GetParseResultString(parse_result
))));
268 matches
.AddPattern(pattern
);
272 if (specified_matches
&& matches
.is_empty()) {
273 install_warnings
->push_back(
274 InstallWarning(automation_errors::kErrorNoMatchesProvided
));
277 return make_scoped_ptr(new AutomationInfo(desktop
, matches
, interact
));
281 scoped_ptr
<base::Value
> AutomationInfo::ToValue(const AutomationInfo
& info
) {
282 return AsManifestType(info
)->ToValue().Pass();
286 scoped_ptr
<Automation
> AutomationInfo::AsManifestType(
287 const AutomationInfo
& info
) {
288 scoped_ptr
<Automation
> automation(new Automation
);
289 if (!info
.desktop
&& !info
.interact
&& info
.matches
.size() == 0) {
290 automation
->as_boolean
.reset(new bool(true));
291 return automation
.Pass();
294 Automation::Object
* as_object
= new Automation::Object
;
295 as_object
->desktop
.reset(new bool(info
.desktop
));
296 as_object
->interact
.reset(new bool(info
.interact
));
297 if (info
.matches
.size() > 0) {
298 as_object
->matches
.reset(info
.matches
.ToStringVector().release());
300 automation
->as_object
.reset(as_object
);
301 return automation
.Pass();
304 AutomationInfo::AutomationInfo() : desktop(false), interact(false) {
307 AutomationInfo::AutomationInfo(bool desktop
,
308 const URLPatternSet matches
,
310 : desktop(desktop
), matches(matches
), interact(interact
) {
313 AutomationInfo::~AutomationInfo() {
316 } // namespace extensions