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, ®ular_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