Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/common/extensions/api/extension_api.h" | 5 #include "chrome/common/extensions/api/extension_api.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| 11 #include "base/json/json_reader.h" | 11 #include "base/json/json_reader.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/string_split.h" | 13 #include "base/string_split.h" |
| 14 #include "base/string_util.h" | 14 #include "base/string_util.h" |
| 15 #include "base/values.h" | 15 #include "base/values.h" |
| 16 #include "chrome/common/extensions/extension.h" | 16 #include "chrome/common/extensions/extension.h" |
| 17 #include "chrome/common/extensions/extension_permission_set.h" | 17 #include "chrome/common/extensions/extension_permission_set.h" |
| 18 #include "googleurl/src/gurl.h" | |
| 18 #include "grit/common_resources.h" | 19 #include "grit/common_resources.h" |
| 19 #include "ui/base/resource/resource_bundle.h" | 20 #include "ui/base/resource/resource_bundle.h" |
| 20 | 21 |
| 21 namespace extensions { | 22 namespace extensions { |
| 22 | 23 |
| 23 namespace { | 24 namespace { |
| 24 | 25 |
| 25 // Adds any APIs listed in "dependencies" found in |schema| but not in | |
| 26 // |reference| to |out|. | |
| 27 void GetMissingDependencies( | |
| 28 const DictionaryValue& schema, | |
| 29 const ExtensionAPI::SchemaMap& reference, | |
| 30 std::set<std::string>* out) { | |
| 31 ListValue* dependencies = NULL; | |
| 32 if (!schema.GetList("dependencies", &dependencies)) | |
| 33 return; | |
| 34 for (size_t i = 0; i < dependencies->GetSize(); ++i) { | |
| 35 std::string api_name; | |
| 36 if (dependencies->GetString(i, &api_name) && !reference.count(api_name)) | |
| 37 out->insert(api_name); | |
| 38 } | |
| 39 } | |
| 40 | |
| 41 // Returns whether the list at |name_space_node|.|child_kind| contains any | 26 // Returns whether the list at |name_space_node|.|child_kind| contains any |
| 42 // children with an { "unprivileged": true } property. | 27 // children with an { "unprivileged": true } property. |
| 43 bool HasUnprivilegedChild(const DictionaryValue* name_space_node, | 28 bool HasUnprivilegedChild(const DictionaryValue* name_space_node, |
| 44 const std::string& child_kind) { | 29 const std::string& child_kind) { |
| 45 ListValue* child_list = NULL; | 30 ListValue* child_list = NULL; |
| 46 name_space_node->GetList(child_kind, &child_list); | 31 name_space_node->GetList(child_kind, &child_list); |
| 47 if (!child_list) | 32 if (!child_list) |
| 48 return false; | 33 return false; |
| 49 | 34 |
| 50 for (size_t i = 0; i < child_list->GetSize(); ++i) { | 35 for (size_t i = 0; i < child_list->GetSize(); ++i) { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 88 loaded->Remove(loaded->GetSize() - 1, &value); | 73 loaded->Remove(loaded->GetSize() - 1, &value); |
| 89 CHECK(value->IsType(Value::TYPE_DICTIONARY)); | 74 CHECK(value->IsType(Value::TYPE_DICTIONARY)); |
| 90 const DictionaryValue* schema = static_cast<const DictionaryValue*>(value); | 75 const DictionaryValue* schema = static_cast<const DictionaryValue*>(value); |
| 91 CHECK(schema->GetString("namespace", &schema_namespace)); | 76 CHECK(schema->GetString("namespace", &schema_namespace)); |
| 92 schemas_[schema_namespace] = linked_ptr<const DictionaryValue>(schema); | 77 schemas_[schema_namespace] = linked_ptr<const DictionaryValue>(schema); |
| 93 } | 78 } |
| 94 } | 79 } |
| 95 | 80 |
| 96 ExtensionAPI::ExtensionAPI() { | 81 ExtensionAPI::ExtensionAPI() { |
| 97 static int kJsonApiResourceIds[] = { | 82 static int kJsonApiResourceIds[] = { |
| 83 IDR_EXTENSION_API_JSON_APP, | |
| 98 IDR_EXTENSION_API_JSON_BOOKMARKS, | 84 IDR_EXTENSION_API_JSON_BOOKMARKS, |
| 99 IDR_EXTENSION_API_JSON_BROWSERACTION, | 85 IDR_EXTENSION_API_JSON_BROWSERACTION, |
| 100 IDR_EXTENSION_API_JSON_BROWSING_DATA, | 86 IDR_EXTENSION_API_JSON_BROWSING_DATA, |
| 101 IDR_EXTENSION_API_JSON_CHROMEAUTHPRIVATE, | 87 IDR_EXTENSION_API_JSON_CHROMEAUTHPRIVATE, |
| 102 IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE, | 88 IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE, |
| 103 IDR_EXTENSION_API_JSON_CHROMEPRIVATE, | 89 IDR_EXTENSION_API_JSON_CHROMEPRIVATE, |
| 104 IDR_EXTENSION_API_JSON_CONTENTSETTINGS, | 90 IDR_EXTENSION_API_JSON_CONTENTSETTINGS, |
| 105 IDR_EXTENSION_API_JSON_CONTEXTMENUS, | 91 IDR_EXTENSION_API_JSON_CONTEXTMENUS, |
| 106 IDR_EXTENSION_API_JSON_COOKIES, | 92 IDR_EXTENSION_API_JSON_COOKIES, |
| 107 IDR_EXTENSION_API_JSON_DEBUGGER, | 93 IDR_EXTENSION_API_JSON_DEBUGGER, |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 170 continue; | 156 continue; |
| 171 } | 157 } |
| 172 | 158 |
| 173 // Only need to look at functions/events; even though there are unprivileged | 159 // Only need to look at functions/events; even though there are unprivileged |
| 174 // properties (e.g. in extensions), access to those never reaches C++ land. | 160 // properties (e.g. in extensions), access to those never reaches C++ land. |
| 175 if (HasUnprivilegedChild(it->second.get(), "functions") || | 161 if (HasUnprivilegedChild(it->second.get(), "functions") || |
| 176 HasUnprivilegedChild(it->second.get(), "events")) { | 162 HasUnprivilegedChild(it->second.get(), "events")) { |
| 177 partially_unprivileged_apis_.insert(it->first); | 163 partially_unprivileged_apis_.insert(it->first); |
| 178 } | 164 } |
| 179 } | 165 } |
| 166 | |
| 167 // Populate |url_matching_apis_|. | |
| 168 for (SchemaMap::const_iterator it = schemas_.begin(); | |
| 169 it != schemas_.end(); ++it) { | |
| 170 ListValue* matches = NULL; | |
| 171 { | |
| 172 Value* matches_value = NULL; | |
| 173 if (!it->second->Get("matches", &matches_value)) | |
| 174 continue; | |
| 175 CHECK_EQ(Value::TYPE_LIST, matches_value->GetType()); | |
| 176 matches = static_cast<ListValue*>(matches_value); | |
| 177 } | |
| 178 URLPatternSet pattern_set; | |
| 179 for (size_t i = 0; i < matches->GetSize(); ++i) { | |
| 180 std::string pattern; | |
| 181 CHECK(matches->GetString(i, &pattern)); | |
| 182 pattern_set.AddPattern( | |
| 183 URLPattern(UserScript::kValidUserScriptSchemes, pattern)); | |
| 184 } | |
| 185 url_matching_apis_[it->first] = pattern_set; | |
| 186 } | |
| 180 } | 187 } |
| 181 | 188 |
| 182 ExtensionAPI::~ExtensionAPI() { | 189 ExtensionAPI::~ExtensionAPI() { |
| 183 } | 190 } |
| 184 | 191 |
| 185 bool ExtensionAPI::IsPrivileged(const std::string& full_name) const { | 192 bool ExtensionAPI::IsPrivileged(const std::string& full_name) const { |
| 186 std::string api_name; | 193 std::string api_name; |
| 187 std::string child_name; | 194 std::string child_name; |
| 188 | 195 |
| 189 { | 196 { |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 211 | 218 |
| 212 if (partially_unprivileged_apis_.count(api_name)) { | 219 if (partially_unprivileged_apis_.count(api_name)) { |
| 213 const DictionaryValue* schema = GetSchema(api_name); | 220 const DictionaryValue* schema = GetSchema(api_name); |
| 214 return IsChildNamePrivileged(schema, "functions", child_name) && | 221 return IsChildNamePrivileged(schema, "functions", child_name) && |
| 215 IsChildNamePrivileged(schema, "events", child_name); | 222 IsChildNamePrivileged(schema, "events", child_name); |
| 216 } | 223 } |
| 217 | 224 |
| 218 return true; | 225 return true; |
| 219 } | 226 } |
| 220 | 227 |
| 221 DictionaryValue* ExtensionAPI::FindListItem( | |
| 222 const base::ListValue* list, | |
| 223 const std::string& property_name, | |
| 224 const std::string& property_value) const { | |
| 225 for (size_t i = 0; i < list->GetSize(); ++i) { | |
| 226 DictionaryValue* item = NULL; | |
| 227 CHECK(list->GetDictionary(i, &item)) | |
| 228 << property_value << "/" << property_name; | |
| 229 std::string value; | |
| 230 if (item->GetString(property_name, &value) && value == property_value) | |
| 231 return item; | |
| 232 } | |
| 233 | |
| 234 return NULL; | |
| 235 } | |
| 236 | |
| 237 bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node, | 228 bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node, |
| 238 const std::string& child_kind, | 229 const std::string& child_kind, |
| 239 const std::string& child_name) const { | 230 const std::string& child_name) const { |
| 240 ListValue* child_list = NULL; | 231 ListValue* child_list = NULL; |
| 241 name_space_node->GetList(child_kind, &child_list); | 232 name_space_node->GetList(child_kind, &child_list); |
| 242 if (!child_list) | 233 if (!child_list) |
| 243 return true; | 234 return true; |
| 244 | 235 |
| 245 bool unprivileged = false; | 236 bool unprivileged = false; |
| 246 DictionaryValue* child = FindListItem(child_list, "name", child_name); | 237 DictionaryValue* child = FindListItem(child_list, "name", child_name); |
| 247 if (!child || !child->GetBoolean("unprivileged", &unprivileged)) | 238 if (!child || !child->GetBoolean("unprivileged", &unprivileged)) |
| 248 return true; | 239 return true; |
| 249 | 240 |
| 250 return !unprivileged; | 241 return !unprivileged; |
| 251 } | 242 } |
| 252 | 243 |
| 244 DictionaryValue* ExtensionAPI::FindListItem( | |
| 245 const base::ListValue* list, | |
|
Aaron Boodman
2012/03/06 01:17:21
Why move? Just makes things hard to review.
not at google - send to devlin
2012/03/06 03:53:00
Dunno. Moved back.
Oh, because I have a thing abo
| |
| 246 const std::string& property_name, | |
| 247 const std::string& property_value) const { | |
| 248 for (size_t i = 0; i < list->GetSize(); ++i) { | |
| 249 DictionaryValue* item = NULL; | |
| 250 CHECK(list->GetDictionary(i, &item)) | |
| 251 << property_value << "/" << property_name; | |
| 252 std::string value; | |
| 253 if (item->GetString(property_name, &value) && value == property_value) | |
| 254 return item; | |
| 255 } | |
| 256 | |
| 257 return NULL; | |
| 258 } | |
| 259 | |
| 253 const base::DictionaryValue* ExtensionAPI::GetSchema( | 260 const base::DictionaryValue* ExtensionAPI::GetSchema( |
| 254 const std::string& api_name) const { | 261 const std::string& api_name) const { |
| 255 SchemaMap::const_iterator maybe_schema = schemas_.find(api_name); | 262 SchemaMap::const_iterator maybe_schema = schemas_.find(api_name); |
| 256 return maybe_schema != schemas_.end() ? maybe_schema->second.get() : NULL; | 263 return maybe_schema != schemas_.end() ? maybe_schema->second.get() : NULL; |
| 257 } | 264 } |
| 258 | 265 |
| 259 void ExtensionAPI::GetSchemasForExtension(const Extension& extension, | 266 scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext( |
| 260 GetSchemasFilter filter, | 267 Feature::Context context, |
| 261 SchemaMap* out) const { | 268 const Extension* extension, |
| 262 // Check both required_permissions and optional_permissions since we need | 269 const GURL& url) const { |
| 263 // to return all schemas that might be needed. | 270 scoped_ptr<std::set<std::string> > result(new std::set<std::string>()); |
| 264 GetSchemasForPermissions(*extension.required_permission_set(), filter, out); | |
| 265 GetSchemasForPermissions(*extension.optional_permission_set(), filter, out); | |
| 266 | 271 |
| 267 // Note that dependency resolution might introduce APIs outside of the filter | 272 switch (context) { |
| 268 // (for example, "extensions" has unprivileged componenents but relies on | 273 case Feature::UNSPECIFIED_CONTEXT: |
| 269 // "tabs" which doesn't). It doesn't matter because schema_generated_bindings | 274 break; |
| 270 // does individual function/event based checking anyway, but it's a shame. | 275 |
| 271 ResolveDependencies(out); | 276 case Feature::PRIVILEGED_CONTEXT: |
| 277 // Availability is determined by the permissions of the extension. | |
| 278 CHECK(extension); | |
| 279 InsertAllowedAPIs(extension, result.get()); | |
| 280 ResolveDependencies(result.get()); | |
| 281 break; | |
| 282 | |
| 283 case Feature::UNPRIVILEGED_CONTEXT: | |
| 284 case Feature::CONTENT_SCRIPT_CONTEXT: | |
| 285 // Availability is determined by the permissions of the extension | |
| 286 // (but only those APIs that are unprivileged). | |
| 287 CHECK(extension); | |
| 288 InsertAllowedAPIs(extension, result.get()); | |
| 289 // Resolving dependencies before removing unprivileged APIs means that | |
| 290 // some unprivilged APIs may have unrealised dependencies. Too bad! | |
| 291 ResolveDependencies(result.get()); | |
| 292 RemovePrivilegedAPIs(result.get()); | |
|
koz (OOO until 15th September)
2012/03/06 04:06:58
This logic is beautiful ;_;
not at google - send to devlin
2012/03/06 11:36:46
Cheers ;_;
| |
| 293 break; | |
| 294 | |
| 295 case Feature::WEB_PAGE_CONTEXT: | |
| 296 // Availablility is determined by the url. | |
| 297 CHECK(url.is_valid()); | |
| 298 for (std::map<std::string, URLPatternSet>::const_iterator i = | |
|
koz (OOO until 15th September)
2012/03/06 04:06:58
tiny nit: I feel like this part should be lifted i
not at google - send to devlin
2012/03/06 11:36:46
Cool, looks better.
| |
| 299 url_matching_apis_.begin(); i != url_matching_apis_.end(); ++i) { | |
| 300 if (i->second.MatchesURL(url)) | |
| 301 result->insert(i->first); | |
| 302 } | |
| 303 break; | |
| 304 } | |
| 305 | |
| 306 return result.Pass(); | |
| 272 } | 307 } |
| 273 | 308 |
| 274 void ExtensionAPI::ResolveDependencies(SchemaMap* out) const { | 309 void ExtensionAPI::InsertAllowedAPIs( |
| 275 std::set<std::string> missing_dependencies; | 310 const Extension* extension, std::set<std::string>* out) const { |
| 276 for (SchemaMap::const_iterator i = out->begin(); i != out->end(); ++i) | 311 for (SchemaMap::const_iterator i = schemas_.begin(); i != schemas_.end(); |
| 277 GetMissingDependencies(*i->second, *out, &missing_dependencies); | 312 ++i) { |
| 278 | 313 if (extension->required_permission_set()->HasAnyAccessToAPI(i->first) || |
| 279 while (missing_dependencies.size()) { | 314 extension->optional_permission_set()->HasAnyAccessToAPI(i->first)) { |
| 280 std::string api_name = *missing_dependencies.begin(); | 315 out->insert(i->first); |
| 281 missing_dependencies.erase(api_name); | 316 } |
| 282 linked_ptr<const DictionaryValue> schema = schemas_.find(api_name)->second; | |
| 283 (*out)[api_name] = schema; | |
| 284 GetMissingDependencies(*schema, *out, &missing_dependencies); | |
| 285 } | 317 } |
| 286 } | 318 } |
| 287 | 319 |
| 288 void ExtensionAPI::GetDefaultSchemas(GetSchemasFilter filter, | 320 void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) const { |
| 289 SchemaMap* out) const { | 321 std::set<std::string> missing_dependencies; |
| 290 scoped_refptr<ExtensionPermissionSet> default_permissions( | 322 for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i) |
| 291 new ExtensionPermissionSet()); | 323 GetMissingDependencies(*i, *out, &missing_dependencies); |
| 292 GetSchemasForPermissions(*default_permissions, filter, out); | |
| 293 ResolveDependencies(out); | |
| 294 } | |
| 295 | 324 |
| 296 void ExtensionAPI::GetSchemasForPermissions( | 325 while (missing_dependencies.size()) { |
| 297 const ExtensionPermissionSet& permissions, | 326 std::string next = *missing_dependencies.begin(); |
| 298 GetSchemasFilter filter, | 327 missing_dependencies.erase(next); |
| 299 SchemaMap* out) const { | 328 out->insert(next); |
| 300 for (SchemaMap::const_iterator it = schemas_.begin(); it != schemas_.end(); | 329 GetMissingDependencies(next, *out, &missing_dependencies); |
| 301 ++it) { | |
| 302 if (filter == ONLY_UNPRIVILEGED && IsWholeAPIPrivileged(it->first)) | |
| 303 continue; | |
| 304 if (permissions.HasAnyAccessToAPI(it->first)) | |
| 305 (*out)[it->first] = it->second; | |
| 306 } | 330 } |
| 307 } | 331 } |
| 308 | 332 |
| 309 bool ExtensionAPI::IsWholeAPIPrivileged(const std::string& api_name) const { | 333 void ExtensionAPI::GetMissingDependencies( |
| 310 return !completely_unprivileged_apis_.count(api_name) && | 334 const std::string& api_name, |
| 311 !partially_unprivileged_apis_.count(api_name); | 335 const std::set<std::string>& reference, |
| 336 std::set<std::string>* out) const { | |
| 337 const base::DictionaryValue* schema = GetSchema(api_name); | |
| 338 CHECK(schema) << "Schema for " << api_name << " not found"; | |
| 339 | |
| 340 ListValue* dependencies = NULL; | |
| 341 if (!schema->GetList("dependencies", &dependencies)) | |
| 342 return; | |
| 343 | |
| 344 for (size_t i = 0; i < dependencies->GetSize(); ++i) { | |
| 345 std::string api_name; | |
| 346 if (dependencies->GetString(i, &api_name) && !reference.count(api_name)) | |
| 347 out->insert(api_name); | |
| 348 } | |
| 349 } | |
| 350 | |
| 351 void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) const { | |
| 352 std::set<std::string> privileged_apis; | |
| 353 for (std::set<std::string>::iterator i = apis->begin(); i != apis->end(); | |
| 354 ++i) { | |
| 355 if (!completely_unprivileged_apis_.count(*i) && | |
| 356 !partially_unprivileged_apis_.count(*i)) { | |
| 357 privileged_apis.insert(*i); | |
| 358 } | |
| 359 } | |
| 360 for (std::set<std::string>::iterator i = privileged_apis.begin(); | |
| 361 i != privileged_apis.end(); ++i) { | |
| 362 apis->erase(*i); | |
| 363 } | |
| 312 } | 364 } |
| 313 | 365 |
| 314 } // namespace extensions | 366 } // namespace extensions |
| OLD | NEW |