Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / chrome / common / extensions / manifest_handlers / automation.cc
blob242f40f704f734223972a92abb00d4e829eb741b
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;
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;
135 bool interact =
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;
150 bool interact =
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);
171 if (!error->empty())
172 return false;
174 extension->AddInstallWarnings(install_warnings);
176 if (!info)
177 return true;
179 extension->SetManifestData(keys::kAutomation, info.release());
180 return true;
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);
195 if (info) {
196 return new AutomationManifestPermission(
197 make_scoped_ptr(new const AutomationInfo(
198 info->desktop, info->matches, info->interact)));
200 return NULL;
203 // static
204 const AutomationInfo* AutomationInfo::Get(const Extension* extension) {
205 return static_cast<AutomationInfo*>(
206 extension->GetManifestData(keys::kAutomation));
209 // static
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);
215 if (!automation)
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) {
228 desktop = true;
229 interact = true;
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) {
236 interact = true;
239 URLPatternSet matches;
240 bool specified_matches = false;
241 if (automation_object.matches) {
242 if (desktop) {
243 install_warnings->push_back(
244 InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified));
245 } else {
246 specified_matches = true;
248 for (std::vector<std::string>::iterator it =
249 automation_object.matches->begin();
250 it != automation_object.matches->end();
251 ++it) {
252 // TODO(aboxhall): Refactor common logic from content_scripts_handler,
253 // manifest_url_handler and user_script.cc into a single location and
254 // re-use here.
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,
263 *it,
264 URLPattern::GetParseResultString(parse_result))));
265 continue;
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));
280 // static
281 scoped_ptr<base::Value> AutomationInfo::ToValue(const AutomationInfo& info) {
282 return AsManifestType(info)->ToValue().Pass();
285 // static
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,
309 bool interact)
310 : desktop(desktop), matches(matches), interact(interact) {
313 AutomationInfo::~AutomationInfo() {
316 } // namespace extensions