Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / common / extensions / manifest_handlers / automation.cc
blobd247b242ee410b39e23433e3756311d79a5bcdfb
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 {
40 public:
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;
62 private:
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);
82 } else {
83 permissions.insert(APIPermission::kHostsAllReadOnly);
85 } else {
86 // Check if we get any additional permissions from FilterHostPermissions.
87 URLPatternSet regular_hosts;
88 ExtensionsClient::Get()->FilterHostPermissions(
89 automation_info_->matches, &regular_hosts, &permissions);
90 std::set<std::string> hosts =
91 permission_message_util::GetDistinctHosts(regular_hosts, true, true);
92 if (!hosts.empty()) {
93 permission_message_util::AddHostPermissions(
94 &permissions, hosts, automation_info_->interact
95 ? permission_message_util::kReadWrite
96 : permission_message_util::kReadOnly);
99 return permissions;
102 bool AutomationManifestPermission::FromValue(const base::Value* value) {
103 base::string16 error;
104 automation_info_.reset(AutomationInfo::FromValue(*value,
105 NULL /* install_warnings */,
106 &error).release());
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;
120 bool interact =
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;
134 bool interact =
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;
148 bool interact =
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);
168 if (!error->empty())
169 return false;
171 extension->AddInstallWarnings(install_warnings);
173 if (!info)
174 return true;
176 extension->SetManifestData(keys::kAutomation, info.release());
177 return true;
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);
192 if (info) {
193 return new AutomationManifestPermission(
194 make_scoped_ptr(new const AutomationInfo(
195 info->desktop, info->matches, info->interact)));
197 return NULL;
200 // static
201 const AutomationInfo* AutomationInfo::Get(const Extension* extension) {
202 return static_cast<AutomationInfo*>(
203 extension->GetManifestData(keys::kAutomation));
206 // static
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);
212 if (!automation)
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) {
225 desktop = true;
226 interact = true;
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) {
233 interact = true;
236 URLPatternSet matches;
237 bool specified_matches = false;
238 if (automation_object.matches) {
239 if (desktop) {
240 install_warnings->push_back(
241 InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified));
242 } else {
243 specified_matches = true;
245 for (std::vector<std::string>::iterator it =
246 automation_object.matches->begin();
247 it != automation_object.matches->end();
248 ++it) {
249 // TODO(aboxhall): Refactor common logic from content_scripts_handler,
250 // manifest_url_handler and user_script.cc into a single location and
251 // re-use here.
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,
260 *it,
261 URLPattern::GetParseResultString(parse_result))));
262 continue;
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));
277 // static
278 scoped_ptr<base::Value> AutomationInfo::ToValue(const AutomationInfo& info) {
279 return AsManifestType(info)->ToValue().Pass();
282 // static
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,
306 bool interact)
307 : desktop(desktop), matches(matches), interact(interact) {
310 AutomationInfo::~AutomationInfo() {
313 } // namespace extensions