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 bool HasMessages() const override;
53 PermissionMessages GetMessages() const override;
55 bool FromValue(const base::Value* value) override;
57 scoped_ptr<base::Value> ToValue() const override;
59 ManifestPermission* Diff(const ManifestPermission* rhs) const override;
61 ManifestPermission* Union(const ManifestPermission* rhs) const override;
63 ManifestPermission* Intersect(const ManifestPermission* rhs) const override;
66 scoped_ptr<const AutomationInfo> automation_info_;
69 std::string AutomationManifestPermission::name() const {
70 return keys::kAutomation;
73 std::string AutomationManifestPermission::id() const {
74 return keys::kAutomation;
77 bool AutomationManifestPermission::HasMessages() const {
78 return GetMessages().size() > 0;
81 PermissionMessages AutomationManifestPermission::GetMessages() const {
82 PermissionMessages messages;
83 if (automation_info_->desktop) {
84 messages.push_back(PermissionMessage(
85 PermissionMessage::kFullAccess,
86 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS)));
87 } else if (automation_info_->matches.MatchesAllURLs()) {
88 if (automation_info_->interact) {
89 messages.push_back(PermissionMessage(
90 PermissionMessage::kHostsAll,
91 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
93 messages.push_back(PermissionMessage(
94 PermissionMessage::kHostsAllReadOnly,
95 l10n_util::GetStringUTF16(
96 IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS_READ_ONLY)));
99 URLPatternSet regular_hosts;
100 std::set<PermissionMessage> message_set;
101 ExtensionsClient::Get()->FilterHostPermissions(
102 automation_info_->matches, ®ular_hosts, &message_set);
103 messages.insert(messages.end(), message_set.begin(), message_set.end());
105 std::set<std::string> hosts =
106 permission_message_util::GetDistinctHosts(regular_hosts, true, true);
107 if (!hosts.empty()) {
108 messages.push_back(permission_message_util::CreateFromHostList(
110 automation_info_->interact ? permission_message_util::kReadWrite
111 : permission_message_util::kReadOnly));
118 bool AutomationManifestPermission::FromValue(const base::Value* value) {
119 base::string16 error;
120 automation_info_.reset(AutomationInfo::FromValue(*value,
121 NULL /* install_warnings */,
123 return error.empty();
126 scoped_ptr<base::Value> AutomationManifestPermission::ToValue() const {
127 return AutomationInfo::ToValue(*automation_info_).Pass();
130 ManifestPermission* AutomationManifestPermission::Diff(
131 const ManifestPermission* rhs) const {
132 const AutomationManifestPermission* other =
133 static_cast<const AutomationManifestPermission*>(rhs);
135 bool desktop = automation_info_->desktop && !other->automation_info_->desktop;
137 automation_info_->interact && !other->automation_info_->interact;
138 URLPatternSet matches;
139 URLPatternSet::CreateDifference(
140 automation_info_->matches, other->automation_info_->matches, &matches);
141 return new AutomationManifestPermission(
142 make_scoped_ptr(new const AutomationInfo(desktop, matches, interact)));
145 ManifestPermission* AutomationManifestPermission::Union(
146 const ManifestPermission* rhs) const {
147 const AutomationManifestPermission* other =
148 static_cast<const AutomationManifestPermission*>(rhs);
150 bool desktop = automation_info_->desktop || other->automation_info_->desktop;
152 automation_info_->interact || other->automation_info_->interact;
153 URLPatternSet matches;
154 URLPatternSet::CreateUnion(
155 automation_info_->matches, other->automation_info_->matches, &matches);
156 return new AutomationManifestPermission(
157 make_scoped_ptr(new const AutomationInfo(desktop, matches, interact)));
160 ManifestPermission* AutomationManifestPermission::Intersect(
161 const ManifestPermission* rhs) const {
162 const AutomationManifestPermission* other =
163 static_cast<const AutomationManifestPermission*>(rhs);
165 bool desktop = automation_info_->desktop && other->automation_info_->desktop;
167 automation_info_->interact && other->automation_info_->interact;
168 URLPatternSet matches;
169 URLPatternSet::CreateIntersection(
170 automation_info_->matches, other->automation_info_->matches, &matches);
171 return new AutomationManifestPermission(
172 make_scoped_ptr(new const AutomationInfo(desktop, matches, interact)));
175 AutomationHandler::AutomationHandler() {
178 AutomationHandler::~AutomationHandler() {
181 bool AutomationHandler::Parse(Extension* extension, base::string16* error) {
182 const base::Value* automation = NULL;
183 CHECK(extension->manifest()->Get(keys::kAutomation, &automation));
184 std::vector<InstallWarning> install_warnings;
185 scoped_ptr<AutomationInfo> info =
186 AutomationInfo::FromValue(*automation, &install_warnings, error);
190 extension->AddInstallWarnings(install_warnings);
195 extension->SetManifestData(keys::kAutomation, info.release());
199 const std::vector<std::string> AutomationHandler::Keys() const {
200 return SingleKey(keys::kAutomation);
203 ManifestPermission* AutomationHandler::CreatePermission() {
204 return new AutomationManifestPermission(
205 make_scoped_ptr(new const AutomationInfo));
208 ManifestPermission* AutomationHandler::CreateInitialRequiredPermission(
209 const Extension* extension) {
210 const AutomationInfo* info = AutomationInfo::Get(extension);
212 return new AutomationManifestPermission(
213 make_scoped_ptr(new const AutomationInfo(
214 info->desktop, info->matches, info->interact)));
220 const AutomationInfo* AutomationInfo::Get(const Extension* extension) {
221 return static_cast<AutomationInfo*>(
222 extension->GetManifestData(keys::kAutomation));
226 scoped_ptr<AutomationInfo> AutomationInfo::FromValue(
227 const base::Value& value,
228 std::vector<InstallWarning>* install_warnings,
229 base::string16* error) {
230 scoped_ptr<Automation> automation = Automation::FromValue(value, error);
232 return scoped_ptr<AutomationInfo>();
234 if (automation->as_boolean) {
235 if (*automation->as_boolean)
236 return make_scoped_ptr(new AutomationInfo());
237 return scoped_ptr<AutomationInfo>();
239 const Automation::Object& automation_object = *automation->as_object;
241 bool desktop = false;
242 bool interact = false;
243 if (automation_object.desktop && *automation_object.desktop) {
246 if (automation_object.interact && !*automation_object.interact) {
247 // TODO(aboxhall): Do we want to allow this?
248 install_warnings->push_back(
249 InstallWarning(automation_errors::kErrorDesktopTrueInteractFalse));
251 } else if (automation_object.interact && *automation_object.interact) {
255 URLPatternSet matches;
256 bool specified_matches = false;
257 if (automation_object.matches) {
259 install_warnings->push_back(
260 InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified));
262 specified_matches = true;
264 for (std::vector<std::string>::iterator it =
265 automation_object.matches->begin();
266 it != automation_object.matches->end();
268 // TODO(aboxhall): Refactor common logic from content_scripts_handler,
269 // manifest_url_handler and user_script.cc into a single location and
271 URLPattern pattern(URLPattern::SCHEME_ALL &
272 ~URLPattern::SCHEME_CHROMEUI);
273 URLPattern::ParseResult parse_result = pattern.Parse(*it);
275 if (parse_result != URLPattern::PARSE_SUCCESS) {
276 install_warnings->push_back(
277 InstallWarning(ErrorUtils::FormatErrorMessage(
278 automation_errors::kErrorInvalidMatch,
280 URLPattern::GetParseResultString(parse_result))));
284 matches.AddPattern(pattern);
288 if (specified_matches && matches.is_empty()) {
289 install_warnings->push_back(
290 InstallWarning(automation_errors::kErrorNoMatchesProvided));
293 return make_scoped_ptr(new AutomationInfo(desktop, matches, interact));
297 scoped_ptr<base::Value> AutomationInfo::ToValue(const AutomationInfo& info) {
298 return AsManifestType(info)->ToValue().Pass();
302 scoped_ptr<Automation> AutomationInfo::AsManifestType(
303 const AutomationInfo& info) {
304 scoped_ptr<Automation> automation(new Automation);
305 if (!info.desktop && !info.interact && info.matches.size() == 0) {
306 automation->as_boolean.reset(new bool(true));
307 return automation.Pass();
310 Automation::Object* as_object = new Automation::Object;
311 as_object->desktop.reset(new bool(info.desktop));
312 as_object->interact.reset(new bool(info.interact));
313 if (info.matches.size() > 0) {
314 as_object->matches.reset(info.matches.ToStringVector().release());
316 automation->as_object.reset(as_object);
317 return automation.Pass();
320 AutomationInfo::AutomationInfo() : desktop(false), interact(false) {
323 AutomationInfo::AutomationInfo(bool desktop,
324 const URLPatternSet matches,
326 : desktop(desktop), matches(matches), interact(interact) {
329 AutomationInfo::~AutomationInfo() {
332 } // namespace extensions