Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "extensions/common/features/simple_feature.h" | 5 #include "extensions/common/features/simple_feature.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
| 12 #include "base/sha1.h" | 12 #include "base/sha1.h" |
| 13 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
| 14 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" |
| 15 #include "base/strings/stringprintf.h" | 15 #include "base/strings/stringprintf.h" |
| 16 #include "extensions/common/switches.h" | 16 #include "extensions/common/switches.h" |
| 17 | 17 |
| 18 namespace extensions { | 18 namespace extensions { |
| 19 | 19 |
| 20 namespace { | 20 namespace { |
| 21 | 21 |
| 22 struct Mappings { | 22 struct Mappings { |
| 23 Mappings() { | 23 Mappings() { |
| 24 extension_types["extension"] = Manifest::TYPE_EXTENSION; | 24 extension_types["extension"].push_back(Manifest::TYPE_EXTENSION); |
|
not at google - send to devlin
2014/04/17 21:33:14
I'm not... super happy about these changes.
Yoyo Zhou
2014/04/17 21:53:12
Can you replace Manifest::Location with something
not at google - send to devlin
2014/04/17 21:59:31
I found the old way of doing that a bit awkward; e
Yoyo Zhou
2014/04/17 22:00:30
You don't have to expose it. Just pass in the Mani
| |
| 25 extension_types["theme"] = Manifest::TYPE_THEME; | 25 extension_types["theme"].push_back(Manifest::TYPE_THEME); |
| 26 extension_types["legacy_packaged_app"] = Manifest::TYPE_LEGACY_PACKAGED_APP; | 26 extension_types["legacy_packaged_app"].push_back( |
| 27 extension_types["hosted_app"] = Manifest::TYPE_HOSTED_APP; | 27 Manifest::TYPE_LEGACY_PACKAGED_APP); |
| 28 extension_types["platform_app"] = Manifest::TYPE_PLATFORM_APP; | 28 extension_types["hosted_app"].push_back(Manifest::TYPE_HOSTED_APP); |
| 29 extension_types["shared_module"] = Manifest::TYPE_SHARED_MODULE; | 29 extension_types["platform_app"].push_back(Manifest::TYPE_PLATFORM_APP); |
| 30 extension_types["shared_module"].push_back(Manifest::TYPE_SHARED_MODULE); | |
| 30 | 31 |
| 31 contexts["blessed_extension"] = Feature::BLESSED_EXTENSION_CONTEXT; | 32 contexts["blessed_extension"].push_back(Feature::BLESSED_EXTENSION_CONTEXT); |
| 32 contexts["unblessed_extension"] = Feature::UNBLESSED_EXTENSION_CONTEXT; | 33 contexts["unblessed_extension"].push_back( |
| 33 contexts["content_script"] = Feature::CONTENT_SCRIPT_CONTEXT; | 34 Feature::UNBLESSED_EXTENSION_CONTEXT); |
| 34 contexts["web_page"] = Feature::WEB_PAGE_CONTEXT; | 35 contexts["content_script"].push_back(Feature::CONTENT_SCRIPT_CONTEXT); |
| 35 contexts["blessed_web_page"] = Feature::BLESSED_WEB_PAGE_CONTEXT; | 36 contexts["web_page"].push_back(Feature::WEB_PAGE_CONTEXT); |
| 37 contexts["blessed_web_page"].push_back(Feature::BLESSED_WEB_PAGE_CONTEXT); | |
| 36 | 38 |
| 37 locations["component"] = Feature::COMPONENT_LOCATION; | 39 locations["component"].push_back(Manifest::COMPONENT); |
| 40 locations["component"].push_back(Manifest::EXTERNAL_COMPONENT); | |
| 41 locations["policy"].push_back(Manifest::EXTERNAL_POLICY_DOWNLOAD); | |
| 42 locations["policy"].push_back(Manifest::EXTERNAL_POLICY); | |
| 38 | 43 |
| 39 platforms["chromeos"] = Feature::CHROMEOS_PLATFORM; | 44 platforms["chromeos"].push_back(Feature::CHROMEOS_PLATFORM); |
| 40 platforms["linux"] = Feature::LINUX_PLATFORM; | 45 platforms["linux"].push_back(Feature::LINUX_PLATFORM); |
| 41 platforms["mac"] = Feature::MACOSX_PLATFORM; | 46 platforms["mac"].push_back(Feature::MACOSX_PLATFORM); |
| 42 platforms["win"] = Feature::WIN_PLATFORM; | 47 platforms["win"].push_back(Feature::WIN_PLATFORM); |
| 43 } | 48 } |
| 44 | 49 |
| 45 std::map<std::string, Manifest::Type> extension_types; | 50 std::map<std::string, std::vector<Manifest::Type> > extension_types; |
| 46 std::map<std::string, Feature::Context> contexts; | 51 std::map<std::string, std::vector<Feature::Context> > contexts; |
| 47 std::map<std::string, Feature::Location> locations; | 52 std::map<std::string, std::vector<Manifest::Location> > locations; |
| 48 std::map<std::string, Feature::Platform> platforms; | 53 std::map<std::string, std::vector<Feature::Platform> > platforms; |
| 49 }; | 54 }; |
| 50 | 55 |
| 51 base::LazyInstance<Mappings> g_mappings = LAZY_INSTANCE_INITIALIZER; | 56 base::LazyInstance<Mappings> g_mappings = LAZY_INSTANCE_INITIALIZER; |
| 52 | 57 |
| 53 // TODO(aa): Can we replace all this manual parsing with JSON schema stuff? | 58 // TODO(aa): Can we replace all this manual parsing with JSON schema stuff? |
| 54 | 59 |
| 55 void ParseSet(const base::DictionaryValue* value, | 60 void ParseSet(const base::DictionaryValue* value, |
| 56 const std::string& property, | 61 const std::string& property, |
| 57 std::set<std::string>* set) { | 62 std::set<std::string>* set) { |
| 58 const base::ListValue* list_value = NULL; | 63 const base::ListValue* list_value = NULL; |
| 59 if (!value->GetList(property, &list_value)) | 64 if (!value->GetList(property, &list_value)) |
| 60 return; | 65 return; |
| 61 | 66 |
| 62 set->clear(); | 67 set->clear(); |
| 63 for (size_t i = 0; i < list_value->GetSize(); ++i) { | 68 for (size_t i = 0; i < list_value->GetSize(); ++i) { |
| 64 std::string str_val; | 69 std::string str_val; |
| 65 CHECK(list_value->GetString(i, &str_val)) << property << " " << i; | 70 CHECK(list_value->GetString(i, &str_val)) << property << " " << i; |
| 66 set->insert(str_val); | 71 set->insert(str_val); |
| 67 } | 72 } |
| 68 } | 73 } |
| 69 | 74 |
| 70 template<typename T> | 75 template <typename T> |
| 71 void ParseEnum(const std::string& string_value, | 76 void ParseEnumHelper(const std::string& string_value, |
| 72 T* enum_value, | 77 const std::map<std::string, std::vector<T> >& mapping, |
| 73 const std::map<std::string, T>& mapping) { | 78 std::set<T>* enum_set) { |
| 74 typename std::map<std::string, T>::const_iterator iter = | 79 typename std::map<std::string, std::vector<T> >::const_iterator iter = |
| 75 mapping.find(string_value); | 80 mapping.find(string_value); |
| 76 CHECK(iter != mapping.end()) << string_value; | 81 CHECK(iter != mapping.end()) << string_value; |
| 77 *enum_value = iter->second; | 82 enum_set->insert(iter->second.begin(), iter->second.end()); |
| 78 } | 83 } |
| 79 | 84 |
| 80 template<typename T> | 85 template <typename T> |
| 81 void ParseEnum(const base::DictionaryValue* value, | 86 void ParseEnum(const std::string& string_value, |
| 82 const std::string& property, | 87 const std::map<std::string, std::vector<T> >& mapping, |
| 83 T* enum_value, | 88 std::set<T>* enum_set) { |
| 84 const std::map<std::string, T>& mapping) { | 89 enum_set->clear(); |
| 85 std::string string_value; | 90 ParseEnumHelper(string_value, mapping, enum_set); |
| 86 if (!value->GetString(property, &string_value)) | |
| 87 return; | |
| 88 | |
| 89 ParseEnum(string_value, enum_value, mapping); | |
| 90 } | 91 } |
| 91 | 92 |
| 92 template<typename T> | 93 template <typename T> |
| 93 void ParseEnumSet(const base::DictionaryValue* value, | 94 void ParseEnumSet(const base::DictionaryValue* value, |
| 94 const std::string& property, | 95 const std::string& property, |
| 95 std::set<T>* enum_set, | 96 const std::map<std::string, std::vector<T> >& mapping, |
| 96 const std::map<std::string, T>& mapping) { | 97 std::set<T>* enum_set) { |
| 97 if (!value->HasKey(property)) | 98 if (!value->HasKey(property)) |
| 98 return; | 99 return; |
| 99 | 100 |
| 100 enum_set->clear(); | 101 enum_set->clear(); |
| 101 | 102 |
| 102 std::string property_string; | 103 std::string property_string; |
| 103 if (value->GetString(property, &property_string)) { | 104 if (value->GetString(property, &property_string)) { |
| 104 if (property_string == "all") { | 105 if (property_string == "all") { |
| 105 for (typename std::map<std::string, T>::const_iterator j = | 106 for (typename std::map<std::string, std::vector<T> >::const_iterator j = |
| 106 mapping.begin(); j != mapping.end(); ++j) { | 107 mapping.begin(); |
| 107 enum_set->insert(j->second); | 108 j != mapping.end(); |
| 109 ++j) { | |
| 110 enum_set->insert(j->second.begin(), j->second.end()); | |
| 108 } | 111 } |
| 109 } | 112 } |
| 110 return; | 113 return; |
| 111 } | 114 } |
| 112 | 115 |
| 113 std::set<std::string> string_set; | 116 std::set<std::string> string_set; |
| 114 ParseSet(value, property, &string_set); | 117 ParseSet(value, property, &string_set); |
| 115 for (std::set<std::string>::iterator iter = string_set.begin(); | 118 for (std::set<std::string>::iterator iter = string_set.begin(); |
| 116 iter != string_set.end(); ++iter) { | 119 iter != string_set.end(); ++iter) { |
| 117 T enum_value = static_cast<T>(0); | 120 ParseEnumHelper(*iter, mapping, enum_set); |
| 118 ParseEnum(*iter, &enum_value, mapping); | |
| 119 enum_set->insert(enum_value); | |
| 120 } | 121 } |
| 121 } | 122 } |
| 122 | 123 |
| 123 void ParseURLPatterns(const base::DictionaryValue* value, | 124 void ParseURLPatterns(const base::DictionaryValue* value, |
| 124 const std::string& key, | 125 const std::string& key, |
| 125 URLPatternSet* set) { | 126 URLPatternSet* set) { |
| 126 const base::ListValue* matches = NULL; | 127 const base::ListValue* matches = NULL; |
| 127 if (value->GetList(key, &matches)) { | 128 if (value->GetList(key, &matches)) { |
| 128 set->ClearPatterns(); | 129 set->ClearPatterns(); |
| 129 for (size_t i = 0; i < matches->GetSize(); ++i) { | 130 for (size_t i = 0; i < matches->GetSize(); ++i) { |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 208 | 209 |
| 209 std::string HashExtensionId(const std::string& extension_id) { | 210 std::string HashExtensionId(const std::string& extension_id) { |
| 210 const std::string id_hash = base::SHA1HashString(extension_id); | 211 const std::string id_hash = base::SHA1HashString(extension_id); |
| 211 DCHECK(id_hash.length() == base::kSHA1Length); | 212 DCHECK(id_hash.length() == base::kSHA1Length); |
| 212 return base::HexEncode(id_hash.c_str(), id_hash.length()); | 213 return base::HexEncode(id_hash.c_str(), id_hash.length()); |
| 213 } | 214 } |
| 214 | 215 |
| 215 } // namespace | 216 } // namespace |
| 216 | 217 |
| 217 SimpleFeature::SimpleFeature() | 218 SimpleFeature::SimpleFeature() |
| 218 : location_(UNSPECIFIED_LOCATION), | 219 : min_manifest_version_(0), max_manifest_version_(0), has_parent_(false) { |
| 219 min_manifest_version_(0), | 220 } |
| 220 max_manifest_version_(0), | |
| 221 has_parent_(false) {} | |
| 222 | 221 |
| 223 SimpleFeature::~SimpleFeature() {} | 222 SimpleFeature::~SimpleFeature() {} |
| 224 | 223 |
| 225 void SimpleFeature::AddFilter(scoped_ptr<SimpleFeatureFilter> filter) { | 224 void SimpleFeature::AddFilter(scoped_ptr<SimpleFeatureFilter> filter) { |
| 226 filters_.push_back(make_linked_ptr(filter.release())); | 225 filters_.push_back(make_linked_ptr(filter.release())); |
| 227 } | 226 } |
| 228 | 227 |
| 229 std::string SimpleFeature::Parse(const base::DictionaryValue* value) { | 228 std::string SimpleFeature::Parse(const base::DictionaryValue* value) { |
| 230 ParseURLPatterns(value, "matches", &matches_); | 229 ParseURLPatterns(value, "matches", &matches_); |
| 231 ParseSet(value, "whitelist", &whitelist_); | 230 ParseSet(value, "whitelist", &whitelist_); |
| 232 ParseSet(value, "dependencies", &dependencies_); | 231 ParseSet(value, "dependencies", &dependencies_); |
| 233 ParseEnumSet<Manifest::Type>(value, "extension_types", &extension_types_, | 232 ParseEnumSet<Manifest::Type>(value, |
| 234 g_mappings.Get().extension_types); | 233 "extension_types", |
| 235 ParseEnumSet<Context>(value, "contexts", &contexts_, | 234 g_mappings.Get().extension_types, |
| 236 g_mappings.Get().contexts); | 235 &extension_types_); |
| 237 ParseEnum<Location>(value, "location", &location_, | 236 // Only a single value is supported for "location", but it expands to |
| 238 g_mappings.Get().locations); | 237 // multiple possible values. |
| 239 ParseEnumSet<Platform>(value, "platforms", &platforms_, | 238 std::string location; |
| 240 g_mappings.Get().platforms); | 239 if (value->GetStringWithoutPathExpansion("location", &location)) { |
| 240 ParseEnum<Manifest::Location>( | |
| 241 location, g_mappings.Get().locations, &locations_); | |
| 242 } | |
| 243 ParseEnumSet<Context>( | |
| 244 value, "contexts", g_mappings.Get().contexts, &contexts_); | |
| 245 ParseEnumSet<Platform>( | |
| 246 value, "platforms", g_mappings.Get().platforms, &platforms_); | |
| 241 value->GetInteger("min_manifest_version", &min_manifest_version_); | 247 value->GetInteger("min_manifest_version", &min_manifest_version_); |
| 242 value->GetInteger("max_manifest_version", &max_manifest_version_); | 248 value->GetInteger("max_manifest_version", &max_manifest_version_); |
| 243 | 249 |
| 244 no_parent_ = false; | 250 no_parent_ = false; |
| 245 value->GetBoolean("noparent", &no_parent_); | 251 value->GetBoolean("noparent", &no_parent_); |
| 246 | 252 |
| 247 if (matches_.is_empty() && contexts_.count(WEB_PAGE_CONTEXT) != 0) { | 253 if (matches_.is_empty() && contexts_.count(WEB_PAGE_CONTEXT) != 0) { |
| 248 return name() + ": Allowing web_page contexts requires supplying a value " + | 254 return name() + ": Allowing web_page contexts requires supplying a value " + |
| 249 "for matches."; | 255 "for matches."; |
| 250 } | 256 } |
| 251 | 257 |
| 252 for (FilterList::iterator filter_iter = filters_.begin(); | 258 for (FilterList::iterator filter_iter = filters_.begin(); |
| 253 filter_iter != filters_.end(); | 259 filter_iter != filters_.end(); |
| 254 ++filter_iter) { | 260 ++filter_iter) { |
| 255 std::string result = (*filter_iter)->Parse(value); | 261 std::string result = (*filter_iter)->Parse(value); |
| 256 if (!result.empty()) { | 262 if (!result.empty()) { |
| 257 return result; | 263 return result; |
| 258 } | 264 } |
| 259 } | 265 } |
| 260 | 266 |
| 261 return std::string(); | 267 return std::string(); |
| 262 } | 268 } |
| 263 | 269 |
| 264 Feature::Availability SimpleFeature::IsAvailableToManifest( | 270 Feature::Availability SimpleFeature::IsAvailableToManifest( |
| 265 const std::string& extension_id, | 271 const std::string& extension_id, |
| 266 Manifest::Type type, | 272 Manifest::Type type, |
| 267 Location location, | 273 Manifest::Location location, |
| 268 int manifest_version, | 274 int manifest_version, |
| 269 Platform platform) const { | 275 Platform platform) const { |
| 270 // Check extension type first to avoid granting platform app permissions | |
| 271 // to component extensions. | |
| 272 // HACK(kalman): user script -> extension. Solve this in a more generic way | |
| 273 // when we compile feature files. | |
| 274 Manifest::Type type_to_check = (type == Manifest::TYPE_USER_SCRIPT) ? | |
| 275 Manifest::TYPE_EXTENSION : type; | |
| 276 if (!extension_types_.empty() && | 276 if (!extension_types_.empty() && |
| 277 extension_types_.find(type_to_check) == extension_types_.end()) { | 277 extension_types_.find(type) == extension_types_.end()) { |
| 278 return CreateAvailability(INVALID_TYPE, type); | 278 return CreateAvailability(INVALID_TYPE, type); |
| 279 } | 279 } |
| 280 | 280 |
| 281 // Component extensions can access any feature. | 281 // Component extensions can access any feature. |
| 282 if (location == COMPONENT_LOCATION) | 282 if (location == Manifest::COMPONENT || |
| 283 location == Manifest::EXTERNAL_COMPONENT) { | |
| 283 return CreateAvailability(IS_AVAILABLE, type); | 284 return CreateAvailability(IS_AVAILABLE, type); |
| 285 } | |
| 284 | 286 |
| 285 if (!whitelist_.empty()) { | 287 if (!whitelist_.empty()) { |
| 286 if (!IsIdInWhitelist(extension_id)) { | 288 if (!IsIdInWhitelist(extension_id)) { |
| 287 // TODO(aa): This is gross. There should be a better way to test the | 289 // TODO(aa): This is gross. There should be a better way to test the |
| 288 // whitelist. | 290 // whitelist. |
| 289 CommandLine* command_line = CommandLine::ForCurrentProcess(); | 291 CommandLine* command_line = CommandLine::ForCurrentProcess(); |
| 290 if (!command_line->HasSwitch(switches::kWhitelistedExtensionID)) | 292 if (!command_line->HasSwitch(switches::kWhitelistedExtensionID)) |
| 291 return CreateAvailability(NOT_FOUND_IN_WHITELIST, type); | 293 return CreateAvailability(NOT_FOUND_IN_WHITELIST, type); |
| 292 | 294 |
| 293 std::string whitelist_switch_value = | 295 std::string whitelist_switch_value = |
| 294 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 296 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( |
| 295 switches::kWhitelistedExtensionID); | 297 switches::kWhitelistedExtensionID); |
| 296 if (extension_id != whitelist_switch_value) | 298 if (extension_id != whitelist_switch_value) |
| 297 return CreateAvailability(NOT_FOUND_IN_WHITELIST, type); | 299 return CreateAvailability(NOT_FOUND_IN_WHITELIST, type); |
| 298 } | 300 } |
| 299 } | 301 } |
| 300 | 302 |
| 301 if (location_ != UNSPECIFIED_LOCATION && location_ != location) | 303 if (!locations_.empty() && locations_.find(location) == locations_.end()) |
|
Yoyo Zhou
2014/04/17 21:53:12
Change this test to be something like location_cla
not at google - send to devlin
2014/04/17 23:18:11
done sort of. I went back to how the code was befo
| |
| 302 return CreateAvailability(INVALID_LOCATION, type); | 304 return CreateAvailability(INVALID_LOCATION, type); |
| 303 | 305 |
| 304 if (!platforms_.empty() && | 306 if (!platforms_.empty() && |
| 305 platforms_.find(platform) == platforms_.end()) | 307 platforms_.find(platform) == platforms_.end()) |
| 306 return CreateAvailability(INVALID_PLATFORM, type); | 308 return CreateAvailability(INVALID_PLATFORM, type); |
| 307 | 309 |
| 308 if (min_manifest_version_ != 0 && manifest_version < min_manifest_version_) | 310 if (min_manifest_version_ != 0 && manifest_version < min_manifest_version_) |
| 309 return CreateAvailability(INVALID_MIN_MANIFEST_VERSION, type); | 311 return CreateAvailability(INVALID_MIN_MANIFEST_VERSION, type); |
| 310 | 312 |
| 311 if (max_manifest_version_ != 0 && manifest_version > max_manifest_version_) | 313 if (max_manifest_version_ != 0 && manifest_version > max_manifest_version_) |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 322 | 324 |
| 323 return CreateAvailability(IS_AVAILABLE, type); | 325 return CreateAvailability(IS_AVAILABLE, type); |
| 324 } | 326 } |
| 325 | 327 |
| 326 Feature::Availability SimpleFeature::IsAvailableToContext( | 328 Feature::Availability SimpleFeature::IsAvailableToContext( |
| 327 const Extension* extension, | 329 const Extension* extension, |
| 328 SimpleFeature::Context context, | 330 SimpleFeature::Context context, |
| 329 const GURL& url, | 331 const GURL& url, |
| 330 SimpleFeature::Platform platform) const { | 332 SimpleFeature::Platform platform) const { |
| 331 if (extension) { | 333 if (extension) { |
| 332 Availability result = IsAvailableToManifest( | 334 Availability result = IsAvailableToManifest(extension->id(), |
| 333 extension->id(), | 335 extension->GetType(), |
| 334 extension->GetType(), | 336 extension->location(), |
| 335 ConvertLocation(extension->location()), | 337 extension->manifest_version(), |
| 336 extension->manifest_version(), | 338 platform); |
| 337 platform); | |
| 338 if (!result.is_available()) | 339 if (!result.is_available()) |
| 339 return result; | 340 return result; |
| 340 } | 341 } |
| 341 | 342 |
| 342 if (!contexts_.empty() && contexts_.find(context) == contexts_.end()) | 343 if (!contexts_.empty() && contexts_.find(context) == contexts_.end()) |
| 343 return CreateAvailability(INVALID_CONTEXT, context); | 344 return CreateAvailability(INVALID_CONTEXT, context); |
| 344 | 345 |
| 345 if (!matches_.is_empty() && !matches_.MatchesURL(url)) | 346 if (!matches_.is_empty() && !matches_.MatchesURL(url)) |
| 346 return CreateAvailability(INVALID_URL, url); | 347 return CreateAvailability(INVALID_URL, url); |
| 347 | 348 |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 473 | 474 |
| 474 if (whitelist.find(extension_id) != whitelist.end() || | 475 if (whitelist.find(extension_id) != whitelist.end() || |
| 475 whitelist.find(HashExtensionId(extension_id)) != whitelist.end()) { | 476 whitelist.find(HashExtensionId(extension_id)) != whitelist.end()) { |
| 476 return true; | 477 return true; |
| 477 } | 478 } |
| 478 | 479 |
| 479 return false; | 480 return false; |
| 480 } | 481 } |
| 481 | 482 |
| 482 } // namespace extensions | 483 } // namespace extensions |
| OLD | NEW |