Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / common / extensions / manifest_handlers / automation.cc
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.
4
5 #include "chrome/common/extensions/manifest_handlers/automation.h"
6
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"
22
23 namespace extensions {
24
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.";
34 }
35
36 namespace errors = manifest_errors;
37 namespace keys = extensions::manifest_keys;
38 using api::manifest_types::Automation;
39
40 class AutomationManifestPermission : public ManifestPermission {
41  public:
42   explicit AutomationManifestPermission(
43       scoped_ptr<const AutomationInfo> automation_info)
44       : automation_info_(automation_info.Pass()) {}
45
46   // extensions::ManifestPermission overrides.
47   std::string name() const override;
48
49   std::string id() const override;
50
51   bool HasMessages() const override;
52
53   PermissionMessages GetMessages() const override;
54
55   bool FromValue(const base::Value* value) override;
56
57   scoped_ptr<base::Value> ToValue() const override;
58
59   ManifestPermission* Diff(const ManifestPermission* rhs) const override;
60
61   ManifestPermission* Union(const ManifestPermission* rhs) const override;
62
63   ManifestPermission* Intersect(const ManifestPermission* rhs) const override;
64
65  private:
66   scoped_ptr<const AutomationInfo> automation_info_;
67 };
68
69 std::string AutomationManifestPermission::name() const {
70   return keys::kAutomation;
71 }
72
73 std::string AutomationManifestPermission::id() const {
74   return keys::kAutomation;
75 }
76
77 bool AutomationManifestPermission::HasMessages() const {
78   return GetMessages().size() > 0;
79 }
80
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)));
92     } else {
93       messages.push_back(PermissionMessage(
94           PermissionMessage::kHostsAllReadOnly,
95           l10n_util::GetStringUTF16(
96               IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS_READ_ONLY)));
97     }
98   } else {
99     URLPatternSet regular_hosts;
100     std::set<PermissionMessage> message_set;
101     ExtensionsClient::Get()->FilterHostPermissions(
102         automation_info_->matches, &regular_hosts, &message_set);
103     messages.insert(messages.end(), message_set.begin(), message_set.end());
104
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(
109           hosts,
110           automation_info_->interact ? permission_message_util::kReadWrite
111                                      : permission_message_util::kReadOnly));
112     }
113   }
114
115   return messages;
116 }
117
118 bool AutomationManifestPermission::FromValue(const base::Value* value) {
119   base::string16 error;
120   automation_info_.reset(AutomationInfo::FromValue(*value,
121                                                    NULL /* install_warnings */,
122                                                    &error).release());
123   return error.empty();
124 }
125
126 scoped_ptr<base::Value> AutomationManifestPermission::ToValue() const {
127   return AutomationInfo::ToValue(*automation_info_).Pass();
128 }
129
130 ManifestPermission* AutomationManifestPermission::Diff(
131     const ManifestPermission* rhs) const {
132   const AutomationManifestPermission* other =
133       static_cast<const AutomationManifestPermission*>(rhs);
134
135   bool desktop = automation_info_->desktop && !other->automation_info_->desktop;
136   bool interact =
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)));
143 }
144
145 ManifestPermission* AutomationManifestPermission::Union(
146     const ManifestPermission* rhs) const {
147   const AutomationManifestPermission* other =
148       static_cast<const AutomationManifestPermission*>(rhs);
149
150   bool desktop = automation_info_->desktop || other->automation_info_->desktop;
151   bool interact =
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)));
158 }
159
160 ManifestPermission* AutomationManifestPermission::Intersect(
161     const ManifestPermission* rhs) const {
162   const AutomationManifestPermission* other =
163       static_cast<const AutomationManifestPermission*>(rhs);
164
165   bool desktop = automation_info_->desktop && other->automation_info_->desktop;
166   bool interact =
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)));
173 }
174
175 AutomationHandler::AutomationHandler() {
176 }
177
178 AutomationHandler::~AutomationHandler() {
179 }
180
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);
187   if (!error->empty())
188     return false;
189
190   extension->AddInstallWarnings(install_warnings);
191
192   if (!info)
193     return true;
194
195   extension->SetManifestData(keys::kAutomation, info.release());
196   return true;
197 }
198
199 const std::vector<std::string> AutomationHandler::Keys() const {
200   return SingleKey(keys::kAutomation);
201 }
202
203 ManifestPermission* AutomationHandler::CreatePermission() {
204   return new AutomationManifestPermission(
205       make_scoped_ptr(new const AutomationInfo));
206 }
207
208 ManifestPermission* AutomationHandler::CreateInitialRequiredPermission(
209     const Extension* extension) {
210   const AutomationInfo* info = AutomationInfo::Get(extension);
211   if (info) {
212     return new AutomationManifestPermission(
213         make_scoped_ptr(new const AutomationInfo(
214             info->desktop, info->matches, info->interact)));
215   }
216   return NULL;
217 }
218
219 // static
220 const AutomationInfo* AutomationInfo::Get(const Extension* extension) {
221   return static_cast<AutomationInfo*>(
222       extension->GetManifestData(keys::kAutomation));
223 }
224
225 // static
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);
231   if (!automation)
232     return scoped_ptr<AutomationInfo>();
233
234   if (automation->as_boolean) {
235     if (*automation->as_boolean)
236       return make_scoped_ptr(new AutomationInfo());
237     return scoped_ptr<AutomationInfo>();
238   }
239   const Automation::Object& automation_object = *automation->as_object;
240
241   bool desktop = false;
242   bool interact = false;
243   if (automation_object.desktop && *automation_object.desktop) {
244     desktop = true;
245     interact = true;
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));
250     }
251   } else if (automation_object.interact && *automation_object.interact) {
252     interact = true;
253   }
254
255   URLPatternSet matches;
256   bool specified_matches = false;
257   if (automation_object.matches) {
258     if (desktop) {
259       install_warnings->push_back(
260           InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified));
261     } else {
262       specified_matches = true;
263
264       for (std::vector<std::string>::iterator it =
265                automation_object.matches->begin();
266            it != automation_object.matches->end();
267            ++it) {
268         // TODO(aboxhall): Refactor common logic from content_scripts_handler,
269         // manifest_url_handler and user_script.cc into a single location and
270         // re-use here.
271         URLPattern pattern(URLPattern::SCHEME_ALL &
272                            ~URLPattern::SCHEME_CHROMEUI);
273         URLPattern::ParseResult parse_result = pattern.Parse(*it);
274
275         if (parse_result != URLPattern::PARSE_SUCCESS) {
276           install_warnings->push_back(
277               InstallWarning(ErrorUtils::FormatErrorMessage(
278                   automation_errors::kErrorInvalidMatch,
279                   *it,
280                   URLPattern::GetParseResultString(parse_result))));
281           continue;
282         }
283
284         matches.AddPattern(pattern);
285       }
286     }
287   }
288   if (specified_matches && matches.is_empty()) {
289     install_warnings->push_back(
290         InstallWarning(automation_errors::kErrorNoMatchesProvided));
291   }
292
293   return make_scoped_ptr(new AutomationInfo(desktop, matches, interact));
294 }
295
296 // static
297 scoped_ptr<base::Value> AutomationInfo::ToValue(const AutomationInfo& info) {
298   return AsManifestType(info)->ToValue().Pass();
299 }
300
301 // static
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();
308   }
309
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());
315   }
316   automation->as_object.reset(as_object);
317   return automation.Pass();
318 }
319
320 AutomationInfo::AutomationInfo() : desktop(false), interact(false) {
321 }
322
323 AutomationInfo::AutomationInfo(bool desktop,
324                                const URLPatternSet matches,
325                                bool interact)
326     : desktop(desktop), matches(matches), interact(interact) {
327 }
328
329 AutomationInfo::~AutomationInfo() {
330 }
331
332 }  // namespace extensions