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
= URLPatternSet::CreateDifference(
123 automation_info_
->matches
, other
->automation_info_
->matches
);
124 return new AutomationManifestPermission(
125 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
128 ManifestPermission
* AutomationManifestPermission::Union(
129 const ManifestPermission
* rhs
) const {
130 const AutomationManifestPermission
* other
=
131 static_cast<const AutomationManifestPermission
*>(rhs
);
133 bool desktop
= automation_info_
->desktop
|| other
->automation_info_
->desktop
;
135 automation_info_
->interact
|| other
->automation_info_
->interact
;
136 URLPatternSet matches
= URLPatternSet::CreateUnion(
137 automation_info_
->matches
, other
->automation_info_
->matches
);
138 return new AutomationManifestPermission(
139 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
142 ManifestPermission
* AutomationManifestPermission::Intersect(
143 const ManifestPermission
* rhs
) const {
144 const AutomationManifestPermission
* other
=
145 static_cast<const AutomationManifestPermission
*>(rhs
);
147 bool desktop
= automation_info_
->desktop
&& other
->automation_info_
->desktop
;
149 automation_info_
->interact
&& other
->automation_info_
->interact
;
150 URLPatternSet matches
= URLPatternSet::CreateIntersection(
151 automation_info_
->matches
, other
->automation_info_
->matches
);
152 return new AutomationManifestPermission(
153 make_scoped_ptr(new const AutomationInfo(desktop
, matches
, interact
)));
156 AutomationHandler::AutomationHandler() {
159 AutomationHandler::~AutomationHandler() {
162 bool AutomationHandler::Parse(Extension
* extension
, base::string16
* error
) {
163 const base::Value
* automation
= NULL
;
164 CHECK(extension
->manifest()->Get(keys::kAutomation
, &automation
));
165 std::vector
<InstallWarning
> install_warnings
;
166 scoped_ptr
<AutomationInfo
> info
=
167 AutomationInfo::FromValue(*automation
, &install_warnings
, error
);
171 extension
->AddInstallWarnings(install_warnings
);
176 extension
->SetManifestData(keys::kAutomation
, info
.release());
180 const std::vector
<std::string
> AutomationHandler::Keys() const {
181 return SingleKey(keys::kAutomation
);
184 ManifestPermission
* AutomationHandler::CreatePermission() {
185 return new AutomationManifestPermission(
186 make_scoped_ptr(new const AutomationInfo
));
189 ManifestPermission
* AutomationHandler::CreateInitialRequiredPermission(
190 const Extension
* extension
) {
191 const AutomationInfo
* info
= AutomationInfo::Get(extension
);
193 return new AutomationManifestPermission(
194 make_scoped_ptr(new const AutomationInfo(
195 info
->desktop
, info
->matches
, info
->interact
)));
201 const AutomationInfo
* AutomationInfo::Get(const Extension
* extension
) {
202 return static_cast<AutomationInfo
*>(
203 extension
->GetManifestData(keys::kAutomation
));
207 scoped_ptr
<AutomationInfo
> AutomationInfo::FromValue(
208 const base::Value
& value
,
209 std::vector
<InstallWarning
>* install_warnings
,
210 base::string16
* error
) {
211 scoped_ptr
<Automation
> automation
= Automation::FromValue(value
, error
);
213 return scoped_ptr
<AutomationInfo
>();
215 if (automation
->as_boolean
) {
216 if (*automation
->as_boolean
)
217 return make_scoped_ptr(new AutomationInfo());
218 return scoped_ptr
<AutomationInfo
>();
220 const Automation::Object
& automation_object
= *automation
->as_object
;
222 bool desktop
= false;
223 bool interact
= false;
224 if (automation_object
.desktop
&& *automation_object
.desktop
) {
227 if (automation_object
.interact
&& !*automation_object
.interact
) {
228 // TODO(aboxhall): Do we want to allow this?
229 install_warnings
->push_back(
230 InstallWarning(automation_errors::kErrorDesktopTrueInteractFalse
));
232 } else if (automation_object
.interact
&& *automation_object
.interact
) {
236 URLPatternSet matches
;
237 bool specified_matches
= false;
238 if (automation_object
.matches
) {
240 install_warnings
->push_back(
241 InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified
));
243 specified_matches
= true;
245 for (std::vector
<std::string
>::iterator it
=
246 automation_object
.matches
->begin();
247 it
!= automation_object
.matches
->end();
249 // TODO(aboxhall): Refactor common logic from content_scripts_handler,
250 // manifest_url_handler and user_script.cc into a single location and
252 URLPattern
pattern(URLPattern::SCHEME_ALL
&
253 ~URLPattern::SCHEME_CHROMEUI
);
254 URLPattern::ParseResult parse_result
= pattern
.Parse(*it
);
256 if (parse_result
!= URLPattern::PARSE_SUCCESS
) {
257 install_warnings
->push_back(
258 InstallWarning(ErrorUtils::FormatErrorMessage(
259 automation_errors::kErrorInvalidMatch
,
261 URLPattern::GetParseResultString(parse_result
))));
265 matches
.AddPattern(pattern
);
269 if (specified_matches
&& matches
.is_empty()) {
270 install_warnings
->push_back(
271 InstallWarning(automation_errors::kErrorNoMatchesProvided
));
274 return make_scoped_ptr(new AutomationInfo(desktop
, matches
, interact
));
278 scoped_ptr
<base::Value
> AutomationInfo::ToValue(const AutomationInfo
& info
) {
279 return AsManifestType(info
)->ToValue().Pass();
283 scoped_ptr
<Automation
> AutomationInfo::AsManifestType(
284 const AutomationInfo
& info
) {
285 scoped_ptr
<Automation
> automation(new Automation
);
286 if (!info
.desktop
&& !info
.interact
&& info
.matches
.size() == 0) {
287 automation
->as_boolean
.reset(new bool(true));
288 return automation
.Pass();
291 Automation::Object
* as_object
= new Automation::Object
;
292 as_object
->desktop
.reset(new bool(info
.desktop
));
293 as_object
->interact
.reset(new bool(info
.interact
));
294 if (info
.matches
.size() > 0) {
295 as_object
->matches
.reset(info
.matches
.ToStringVector().release());
297 automation
->as_object
.reset(as_object
);
298 return automation
.Pass();
301 AutomationInfo::AutomationInfo() : desktop(false), interact(false) {
304 AutomationInfo::AutomationInfo(bool desktop
,
305 const URLPatternSet matches
,
307 : desktop(desktop
), matches(matches
), interact(interact
) {
310 AutomationInfo::~AutomationInfo() {
313 } // namespace extensions