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/string_number_conversions.h"
8#include "base/strings/utf_string_conversions.h"
9#include "chrome/common/extensions/api/manifest_types.h"
10#include "extensions/common/error_utils.h"
11#include "extensions/common/manifest_constants.h"
12#include "extensions/common/permissions/api_permission_set.h"
13#include "extensions/common/permissions/permissions_data.h"
14#include "extensions/common/url_pattern.h"
15
16namespace extensions {
17
18namespace automation_errors {
19const char kErrorDesktopTrueInteractFalse[] =
20    "Cannot specify interactive=false if desktop=true is specified; "
21    "interactive=false will be ignored.";
22const char kErrorDesktopTrueMatchesSpecified[] =
23    "Cannot specify matches for Automation if desktop=true is specified; "
24    "matches will be ignored.";
25const char kErrorInvalidMatch[] = "Invalid match pattern '*': *";
26const char kErrorNoMatchesProvided[] = "No valid match patterns provided.";
27}
28
29namespace errors = manifest_errors;
30namespace keys = extensions::manifest_keys;
31using api::manifest_types::Automation;
32
33AutomationHandler::AutomationHandler() {
34}
35
36AutomationHandler::~AutomationHandler() {
37}
38
39bool AutomationHandler::Parse(Extension* extension, base::string16* error) {
40  const base::Value* automation = NULL;
41  CHECK(extension->manifest()->Get(keys::kAutomation, &automation));
42  std::vector<InstallWarning> install_warnings;
43  scoped_ptr<AutomationInfo> info =
44      AutomationInfo::FromValue(*automation, &install_warnings, error);
45  if (!error->empty())
46    return false;
47
48  extension->AddInstallWarnings(install_warnings);
49
50  if (!info)
51    return true;
52
53  extension->SetManifestData(keys::kAutomation, info.release());
54  return true;
55}
56
57const std::vector<std::string> AutomationHandler::Keys() const {
58  return SingleKey(keys::kAutomation);
59}
60
61// static
62const AutomationInfo* AutomationInfo::Get(const Extension* extension) {
63  return static_cast<AutomationInfo*>(
64      extension->GetManifestData(keys::kAutomation));
65}
66
67// static
68scoped_ptr<AutomationInfo> AutomationInfo::FromValue(
69    const base::Value& value,
70    std::vector<InstallWarning>* install_warnings,
71    base::string16* error) {
72  scoped_ptr<Automation> automation = Automation::FromValue(value, error);
73  if (!automation)
74    return scoped_ptr<AutomationInfo>();
75
76  if (automation->as_boolean) {
77    if (*automation->as_boolean)
78      return make_scoped_ptr(new AutomationInfo());
79    return scoped_ptr<AutomationInfo>();
80  }
81  const Automation::Object& automation_object = *automation->as_object;
82
83  bool desktop = false;
84  bool interact = false;
85  if (automation_object.desktop && *automation_object.desktop) {
86    desktop = true;
87    interact = true;
88    if (automation_object.interact && !*automation_object.interact) {
89      // TODO(aboxhall): Do we want to allow this?
90      install_warnings->push_back(
91          InstallWarning(automation_errors::kErrorDesktopTrueInteractFalse));
92    }
93  } else if (automation_object.interact && *automation_object.interact) {
94    interact = true;
95  }
96
97  URLPatternSet matches;
98  bool specified_matches = false;
99  if (automation_object.matches) {
100    if (desktop) {
101      install_warnings->push_back(
102          InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified));
103    } else {
104      specified_matches = true;
105      for (std::vector<std::string>::iterator it =
106               automation_object.matches->begin();
107           it != automation_object.matches->end();
108           ++it) {
109        // TODO(aboxhall): Refactor common logic from content_scripts_handler,
110        // manifest_url_handler and user_script.cc into a single location and
111        // re-use here.
112        URLPattern pattern(URLPattern::SCHEME_ALL &
113                           ~URLPattern::SCHEME_CHROMEUI);
114        URLPattern::ParseResult parse_result = pattern.Parse(*it);
115        if (parse_result != URLPattern::PARSE_SUCCESS) {
116          install_warnings->push_back(
117              InstallWarning(ErrorUtils::FormatErrorMessage(
118                  automation_errors::kErrorInvalidMatch,
119                  *it,
120                  URLPattern::GetParseResultString(parse_result))));
121          continue;
122        }
123
124        matches.AddPattern(pattern);
125      }
126    }
127  }
128  if (specified_matches && matches.is_empty())
129    install_warnings->push_back(
130        InstallWarning(automation_errors::kErrorNoMatchesProvided));
131
132  return make_scoped_ptr(
133      new AutomationInfo(desktop, matches, interact, specified_matches));
134}
135
136AutomationInfo::AutomationInfo()
137    : desktop(false), interact(false), specified_matches(false) {
138}
139AutomationInfo::AutomationInfo(bool desktop,
140                               const URLPatternSet& matches,
141                               bool interact,
142                               bool specified_matches)
143    : desktop(desktop),
144      matches(matches),
145      interact(interact),
146      specified_matches(specified_matches) {
147}
148
149AutomationInfo::~AutomationInfo() {
150}
151
152}  // namespace extensions
153