Home | History | Annotate | Download | only in manifest_handlers
      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   virtual std::string name() const OVERRIDE;
     48 
     49   virtual std::string id() const OVERRIDE;
     50 
     51   virtual bool HasMessages() const OVERRIDE;
     52 
     53   virtual PermissionMessages GetMessages() const OVERRIDE;
     54 
     55   virtual bool FromValue(const base::Value* value) OVERRIDE;
     56 
     57   virtual scoped_ptr<base::Value> ToValue() const OVERRIDE;
     58 
     59   virtual ManifestPermission* Diff(
     60       const ManifestPermission* rhs) const OVERRIDE;
     61 
     62   virtual ManifestPermission* Union(
     63       const ManifestPermission* rhs) const OVERRIDE;
     64 
     65   virtual ManifestPermission* Intersect(
     66       const ManifestPermission* rhs) const OVERRIDE;
     67 
     68  private:
     69   scoped_ptr<const AutomationInfo> automation_info_;
     70 };
     71 
     72 std::string AutomationManifestPermission::name() const {
     73   return keys::kAutomation;
     74 }
     75 
     76 std::string AutomationManifestPermission::id() const {
     77   return keys::kAutomation;
     78 }
     79 
     80 bool AutomationManifestPermission::HasMessages() const {
     81   return GetMessages().size() > 0;
     82 }
     83 
     84 PermissionMessages AutomationManifestPermission::GetMessages() const {
     85   PermissionMessages messages;
     86   if (automation_info_->desktop) {
     87     messages.push_back(PermissionMessage(
     88         PermissionMessage::kFullAccess,
     89         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS)));
     90   } else if (automation_info_->matches.MatchesAllURLs()) {
     91     messages.push_back(PermissionMessage(
     92         PermissionMessage::kHostsAll,
     93         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
     94   } else {
     95     URLPatternSet regular_hosts;
     96     std::set<PermissionMessage> message_set;
     97     ExtensionsClient::Get()->FilterHostPermissions(
     98         automation_info_->matches, &regular_hosts, &message_set);
     99     messages.insert(messages.end(), message_set.begin(), message_set.end());
    100 
    101     std::set<std::string> hosts =
    102         permission_message_util::GetDistinctHosts(regular_hosts, true, true);
    103     if (!hosts.empty())
    104       messages.push_back(permission_message_util::CreateFromHostList(hosts));
    105   }
    106 
    107   return messages;
    108 }
    109 
    110 bool AutomationManifestPermission::FromValue(const base::Value* value) {
    111   base::string16 error;
    112   automation_info_.reset(AutomationInfo::FromValue(*value,
    113                                                    NULL /* install_warnings */,
    114                                                    &error).release());
    115   return error.empty();
    116 }
    117 
    118 scoped_ptr<base::Value> AutomationManifestPermission::ToValue() const {
    119   return AutomationInfo::ToValue(*automation_info_).Pass();
    120 }
    121 
    122 ManifestPermission* AutomationManifestPermission::Diff(
    123     const ManifestPermission* rhs) const {
    124   const AutomationManifestPermission* other =
    125       static_cast<const AutomationManifestPermission*>(rhs);
    126 
    127   bool desktop = automation_info_->desktop && !other->automation_info_->desktop;
    128   bool interact =
    129       automation_info_->interact && !other->automation_info_->interact;
    130   URLPatternSet matches;
    131   URLPatternSet::CreateDifference(
    132       automation_info_->matches, other->automation_info_->matches, &matches);
    133   return new AutomationManifestPermission(
    134       make_scoped_ptr(new const AutomationInfo(desktop, matches, interact)));
    135 }
    136 
    137 ManifestPermission* AutomationManifestPermission::Union(
    138     const ManifestPermission* rhs) const {
    139   const AutomationManifestPermission* other =
    140       static_cast<const AutomationManifestPermission*>(rhs);
    141 
    142   bool desktop = automation_info_->desktop || other->automation_info_->desktop;
    143   bool interact =
    144       automation_info_->interact || other->automation_info_->interact;
    145   URLPatternSet matches;
    146   URLPatternSet::CreateUnion(
    147       automation_info_->matches, other->automation_info_->matches, &matches);
    148   return new AutomationManifestPermission(
    149       make_scoped_ptr(new const AutomationInfo(desktop, matches, interact)));
    150 }
    151 
    152 ManifestPermission* AutomationManifestPermission::Intersect(
    153     const ManifestPermission* rhs) const {
    154   const AutomationManifestPermission* other =
    155       static_cast<const AutomationManifestPermission*>(rhs);
    156 
    157   bool desktop = automation_info_->desktop && other->automation_info_->desktop;
    158   bool interact =
    159       automation_info_->interact && other->automation_info_->interact;
    160   URLPatternSet matches;
    161   URLPatternSet::CreateIntersection(
    162       automation_info_->matches, other->automation_info_->matches, &matches);
    163   return new AutomationManifestPermission(
    164       make_scoped_ptr(new const AutomationInfo(desktop, matches, interact)));
    165 }
    166 
    167 AutomationHandler::AutomationHandler() {
    168 }
    169 
    170 AutomationHandler::~AutomationHandler() {
    171 }
    172 
    173 bool AutomationHandler::Parse(Extension* extension, base::string16* error) {
    174   const base::Value* automation = NULL;
    175   CHECK(extension->manifest()->Get(keys::kAutomation, &automation));
    176   std::vector<InstallWarning> install_warnings;
    177   scoped_ptr<AutomationInfo> info =
    178       AutomationInfo::FromValue(*automation, &install_warnings, error);
    179   if (!error->empty())
    180     return false;
    181 
    182   extension->AddInstallWarnings(install_warnings);
    183 
    184   if (!info)
    185     return true;
    186 
    187   extension->SetManifestData(keys::kAutomation, info.release());
    188   return true;
    189 }
    190 
    191 const std::vector<std::string> AutomationHandler::Keys() const {
    192   return SingleKey(keys::kAutomation);
    193 }
    194 
    195 ManifestPermission* AutomationHandler::CreatePermission() {
    196   return new AutomationManifestPermission(
    197       make_scoped_ptr(new const AutomationInfo));
    198 }
    199 
    200 ManifestPermission* AutomationHandler::CreateInitialRequiredPermission(
    201     const Extension* extension) {
    202   const AutomationInfo* info = AutomationInfo::Get(extension);
    203   if (info) {
    204     return new AutomationManifestPermission(
    205         make_scoped_ptr(new const AutomationInfo(
    206             info->desktop, info->matches, info->interact)));
    207   }
    208   return NULL;
    209 }
    210 
    211 // static
    212 const AutomationInfo* AutomationInfo::Get(const Extension* extension) {
    213   return static_cast<AutomationInfo*>(
    214       extension->GetManifestData(keys::kAutomation));
    215 }
    216 
    217 // static
    218 scoped_ptr<AutomationInfo> AutomationInfo::FromValue(
    219     const base::Value& value,
    220     std::vector<InstallWarning>* install_warnings,
    221     base::string16* error) {
    222   scoped_ptr<Automation> automation = Automation::FromValue(value, error);
    223   if (!automation)
    224     return scoped_ptr<AutomationInfo>();
    225 
    226   if (automation->as_boolean) {
    227     if (*automation->as_boolean)
    228       return make_scoped_ptr(new AutomationInfo());
    229     return scoped_ptr<AutomationInfo>();
    230   }
    231   const Automation::Object& automation_object = *automation->as_object;
    232 
    233   bool desktop = false;
    234   bool interact = false;
    235   if (automation_object.desktop && *automation_object.desktop) {
    236     desktop = true;
    237     interact = true;
    238     if (automation_object.interact && !*automation_object.interact) {
    239       // TODO(aboxhall): Do we want to allow this?
    240       install_warnings->push_back(
    241           InstallWarning(automation_errors::kErrorDesktopTrueInteractFalse));
    242     }
    243   } else if (automation_object.interact && *automation_object.interact) {
    244     interact = true;
    245   }
    246 
    247   URLPatternSet matches;
    248   bool specified_matches = false;
    249   if (automation_object.matches) {
    250     if (desktop) {
    251       install_warnings->push_back(
    252           InstallWarning(automation_errors::kErrorDesktopTrueMatchesSpecified));
    253     } else {
    254       specified_matches = true;
    255 
    256       for (std::vector<std::string>::iterator it =
    257                automation_object.matches->begin();
    258            it != automation_object.matches->end();
    259            ++it) {
    260         // TODO(aboxhall): Refactor common logic from content_scripts_handler,
    261         // manifest_url_handler and user_script.cc into a single location and
    262         // re-use here.
    263         URLPattern pattern(URLPattern::SCHEME_ALL &
    264                            ~URLPattern::SCHEME_CHROMEUI);
    265         URLPattern::ParseResult parse_result = pattern.Parse(*it);
    266 
    267         if (parse_result != URLPattern::PARSE_SUCCESS) {
    268           install_warnings->push_back(
    269               InstallWarning(ErrorUtils::FormatErrorMessage(
    270                   automation_errors::kErrorInvalidMatch,
    271                   *it,
    272                   URLPattern::GetParseResultString(parse_result))));
    273           continue;
    274         }
    275 
    276         matches.AddPattern(pattern);
    277       }
    278     }
    279   }
    280   if (specified_matches && matches.is_empty()) {
    281     install_warnings->push_back(
    282         InstallWarning(automation_errors::kErrorNoMatchesProvided));
    283   }
    284 
    285   return make_scoped_ptr(new AutomationInfo(desktop, matches, interact));
    286 }
    287 
    288 // static
    289 scoped_ptr<base::Value> AutomationInfo::ToValue(const AutomationInfo& info) {
    290   return AsManifestType(info)->ToValue().Pass();
    291 }
    292 
    293 // static
    294 scoped_ptr<Automation> AutomationInfo::AsManifestType(
    295     const AutomationInfo& info) {
    296   scoped_ptr<Automation> automation(new Automation);
    297   if (!info.desktop && !info.interact && info.matches.size() == 0) {
    298     automation->as_boolean.reset(new bool(true));
    299     return automation.Pass();
    300   }
    301 
    302   Automation::Object* as_object = new Automation::Object;
    303   as_object->desktop.reset(new bool(info.desktop));
    304   as_object->interact.reset(new bool(info.interact));
    305   if (info.matches.size() > 0) {
    306     as_object->matches.reset(info.matches.ToStringVector().release());
    307   }
    308   automation->as_object.reset(as_object);
    309   return automation.Pass();
    310 }
    311 
    312 AutomationInfo::AutomationInfo() : desktop(false), interact(false) {
    313 }
    314 
    315 AutomationInfo::AutomationInfo(bool desktop,
    316                                const URLPatternSet matches,
    317                                bool interact)
    318     : desktop(desktop), matches(matches), interact(interact) {
    319 }
    320 
    321 AutomationInfo::~AutomationInfo() {
    322 }
    323 
    324 }  // namespace extensions
    325