| 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" | |
| 19 #include "grit/common_resources.h" | 18 #include "grit/common_resources.h" |
| 20 #include "ui/base/resource/resource_bundle.h" | 19 #include "ui/base/resource/resource_bundle.h" |
| 21 | 20 |
| 22 namespace extensions { | 21 namespace extensions { |
| 23 | 22 |
| 24 namespace { | 23 namespace { |
| 25 | 24 |
| 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 |
| 26 // Returns whether the list at |name_space_node|.|child_kind| contains any | 41 // Returns whether the list at |name_space_node|.|child_kind| contains any |
| 27 // children with an { "unprivileged": true } property. | 42 // children with an { "unprivileged": true } property. |
| 28 bool HasUnprivilegedChild(const DictionaryValue* name_space_node, | 43 bool HasUnprivilegedChild(const DictionaryValue* name_space_node, |
| 29 const std::string& child_kind) { | 44 const std::string& child_kind) { |
| 30 ListValue* child_list = NULL; | 45 ListValue* child_list = NULL; |
| 31 name_space_node->GetList(child_kind, &child_list); | 46 name_space_node->GetList(child_kind, &child_list); |
| 32 if (!child_list) | 47 if (!child_list) |
| 33 return false; | 48 return false; |
| 34 | 49 |
| 35 for (size_t i = 0; i < child_list->GetSize(); ++i) { | 50 for (size_t i = 0; i < child_list->GetSize(); ++i) { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 loaded->Remove(loaded->GetSize() - 1, &value); | 88 loaded->Remove(loaded->GetSize() - 1, &value); |
| 74 CHECK(value->IsType(Value::TYPE_DICTIONARY)); | 89 CHECK(value->IsType(Value::TYPE_DICTIONARY)); |
| 75 const DictionaryValue* schema = static_cast<const DictionaryValue*>(value); | 90 const DictionaryValue* schema = static_cast<const DictionaryValue*>(value); |
| 76 CHECK(schema->GetString("namespace", &schema_namespace)); | 91 CHECK(schema->GetString("namespace", &schema_namespace)); |
| 77 schemas_[schema_namespace] = linked_ptr<const DictionaryValue>(schema); | 92 schemas_[schema_namespace] = linked_ptr<const DictionaryValue>(schema); |
| 78 } | 93 } |
| 79 } | 94 } |
| 80 | 95 |
| 81 ExtensionAPI::ExtensionAPI() { | 96 ExtensionAPI::ExtensionAPI() { |
| 82 static int kJsonApiResourceIds[] = { | 97 static int kJsonApiResourceIds[] = { |
| 83 IDR_EXTENSION_API_JSON_APP, | |
| 84 IDR_EXTENSION_API_JSON_BOOKMARKS, | 98 IDR_EXTENSION_API_JSON_BOOKMARKS, |
| 85 IDR_EXTENSION_API_JSON_BROWSERACTION, | 99 IDR_EXTENSION_API_JSON_BROWSERACTION, |
| 86 IDR_EXTENSION_API_JSON_BROWSING_DATA, | 100 IDR_EXTENSION_API_JSON_BROWSING_DATA, |
| 87 IDR_EXTENSION_API_JSON_CHROMEAUTHPRIVATE, | 101 IDR_EXTENSION_API_JSON_CHROMEAUTHPRIVATE, |
| 88 IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE, | 102 IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE, |
| 89 IDR_EXTENSION_API_JSON_CHROMEPRIVATE, | 103 IDR_EXTENSION_API_JSON_CHROMEPRIVATE, |
| 90 IDR_EXTENSION_API_JSON_CONTENTSETTINGS, | 104 IDR_EXTENSION_API_JSON_CONTENTSETTINGS, |
| 91 IDR_EXTENSION_API_JSON_CONTEXTMENUS, | 105 IDR_EXTENSION_API_JSON_CONTEXTMENUS, |
| 92 IDR_EXTENSION_API_JSON_COOKIES, | 106 IDR_EXTENSION_API_JSON_COOKIES, |
| 93 IDR_EXTENSION_API_JSON_DEBUGGER, | 107 IDR_EXTENSION_API_JSON_DEBUGGER, |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 157 continue; | 171 continue; |
| 158 } | 172 } |
| 159 | 173 |
| 160 // Only need to look at functions/events; even though there are unprivileged | 174 // Only need to look at functions/events; even though there are unprivileged |
| 161 // properties (e.g. in extensions), access to those never reaches C++ land. | 175 // properties (e.g. in extensions), access to those never reaches C++ land. |
| 162 if (HasUnprivilegedChild(it->second.get(), "functions") || | 176 if (HasUnprivilegedChild(it->second.get(), "functions") || |
| 163 HasUnprivilegedChild(it->second.get(), "events")) { | 177 HasUnprivilegedChild(it->second.get(), "events")) { |
| 164 partially_unprivileged_apis_.insert(it->first); | 178 partially_unprivileged_apis_.insert(it->first); |
| 165 } | 179 } |
| 166 } | 180 } |
| 167 | |
| 168 // Populate |url_matching_apis_|. | |
| 169 for (SchemaMap::const_iterator it = schemas_.begin(); | |
| 170 it != schemas_.end(); ++it) { | |
| 171 ListValue* matches = NULL; | |
| 172 { | |
| 173 Value* matches_value = NULL; | |
| 174 if (!it->second->Get("matches", &matches_value)) | |
| 175 continue; | |
| 176 CHECK_EQ(Value::TYPE_LIST, matches_value->GetType()); | |
| 177 matches = static_cast<ListValue*>(matches_value); | |
| 178 } | |
| 179 URLPatternSet pattern_set; | |
| 180 for (size_t i = 0; i < matches->GetSize(); ++i) { | |
| 181 std::string pattern; | |
| 182 CHECK(matches->GetString(i, &pattern)); | |
| 183 pattern_set.AddPattern( | |
| 184 URLPattern(UserScript::kValidUserScriptSchemes, pattern)); | |
| 185 } | |
| 186 url_matching_apis_[it->first] = pattern_set; | |
| 187 } | |
| 188 } | 181 } |
| 189 | 182 |
| 190 ExtensionAPI::~ExtensionAPI() { | 183 ExtensionAPI::~ExtensionAPI() { |
| 191 } | 184 } |
| 192 | 185 |
| 193 bool ExtensionAPI::IsPrivileged(const std::string& full_name) const { | 186 bool ExtensionAPI::IsPrivileged(const std::string& full_name) const { |
| 194 std::string api_name; | 187 std::string api_name; |
| 195 std::string child_name; | 188 std::string child_name; |
| 196 | 189 |
| 197 { | 190 { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 257 | 250 |
| 258 return !unprivileged; | 251 return !unprivileged; |
| 259 } | 252 } |
| 260 | 253 |
| 261 const base::DictionaryValue* ExtensionAPI::GetSchema( | 254 const base::DictionaryValue* ExtensionAPI::GetSchema( |
| 262 const std::string& api_name) const { | 255 const std::string& api_name) const { |
| 263 SchemaMap::const_iterator maybe_schema = schemas_.find(api_name); | 256 SchemaMap::const_iterator maybe_schema = schemas_.find(api_name); |
| 264 return maybe_schema != schemas_.end() ? maybe_schema->second.get() : NULL; | 257 return maybe_schema != schemas_.end() ? maybe_schema->second.get() : NULL; |
| 265 } | 258 } |
| 266 | 259 |
| 267 scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext( | 260 void ExtensionAPI::GetSchemasForExtension(const Extension& extension, |
| 268 Feature::Context context, | 261 GetSchemasFilter filter, |
| 269 const Extension* extension, | 262 SchemaMap* out) const { |
| 270 const GURL& url) const { | 263 // Check both required_permissions and optional_permissions since we need |
| 271 scoped_ptr<std::set<std::string> > result(new std::set<std::string>()); | 264 // to return all schemas that might be needed. |
| 265 GetSchemasForPermissions(*extension.required_permission_set(), filter, out); |
| 266 GetSchemasForPermissions(*extension.optional_permission_set(), filter, out); |
| 272 | 267 |
| 273 switch (context) { | 268 // Note that dependency resolution might introduce APIs outside of the filter |
| 274 case Feature::UNSPECIFIED_CONTEXT: | 269 // (for example, "extensions" has unprivileged componenents but relies on |
| 275 break; | 270 // "tabs" which doesn't). It doesn't matter because schema_generated_bindings |
| 276 | 271 // does individual function/event based checking anyway, but it's a shame. |
| 277 case Feature::PRIVILEGED_CONTEXT: | 272 ResolveDependencies(out); |
| 278 // Availability is determined by the permissions of the extension. | |
| 279 CHECK(extension); | |
| 280 GetAllowedAPIs(extension, result.get()); | |
| 281 ResolveDependencies(result.get()); | |
| 282 break; | |
| 283 | |
| 284 case Feature::UNPRIVILEGED_CONTEXT: | |
| 285 case Feature::CONTENT_SCRIPT_CONTEXT: | |
| 286 // Availability is determined by the permissions of the extension | |
| 287 // (but only those APIs that are unprivileged). | |
| 288 CHECK(extension); | |
| 289 GetAllowedAPIs(extension, result.get()); | |
| 290 // Resolving dependencies before removing unprivileged APIs means that | |
| 291 // some unprivileged APIs may have unrealised dependencies. Too bad! | |
| 292 ResolveDependencies(result.get()); | |
| 293 RemovePrivilegedAPIs(result.get()); | |
| 294 break; | |
| 295 | |
| 296 case Feature::WEB_PAGE_CONTEXT: | |
| 297 // Availablility is determined by the url. | |
| 298 CHECK(url.is_valid()); | |
| 299 GetAPIsMatchingURL(url, result.get()); | |
| 300 break; | |
| 301 } | |
| 302 | |
| 303 return result.Pass(); | |
| 304 } | 273 } |
| 305 | 274 |
| 306 void ExtensionAPI::GetAllowedAPIs( | 275 void ExtensionAPI::ResolveDependencies(SchemaMap* out) const { |
| 307 const Extension* extension, std::set<std::string>* out) const { | 276 std::set<std::string> missing_dependencies; |
| 308 for (SchemaMap::const_iterator i = schemas_.begin(); i != schemas_.end(); | 277 for (SchemaMap::const_iterator i = out->begin(); i != out->end(); ++i) |
| 309 ++i) { | 278 GetMissingDependencies(*i->second, *out, &missing_dependencies); |
| 310 if (extension->required_permission_set()->HasAnyAccessToAPI(i->first) || | 279 |
| 311 extension->optional_permission_set()->HasAnyAccessToAPI(i->first)) { | 280 while (missing_dependencies.size()) { |
| 312 out->insert(i->first); | 281 std::string api_name = *missing_dependencies.begin(); |
| 313 } | 282 missing_dependencies.erase(api_name); |
| 283 linked_ptr<const DictionaryValue> schema = schemas_.find(api_name)->second; |
| 284 (*out)[api_name] = schema; |
| 285 GetMissingDependencies(*schema, *out, &missing_dependencies); |
| 314 } | 286 } |
| 315 } | 287 } |
| 316 | 288 |
| 317 void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) const { | 289 void ExtensionAPI::GetDefaultSchemas(GetSchemasFilter filter, |
| 318 std::set<std::string> missing_dependencies; | 290 SchemaMap* out) const { |
| 319 for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i) | 291 scoped_refptr<ExtensionPermissionSet> default_permissions( |
| 320 GetMissingDependencies(*i, *out, &missing_dependencies); | 292 new ExtensionPermissionSet()); |
| 293 GetSchemasForPermissions(*default_permissions, filter, out); |
| 294 ResolveDependencies(out); |
| 295 } |
| 321 | 296 |
| 322 while (missing_dependencies.size()) { | 297 void ExtensionAPI::GetSchemasForPermissions( |
| 323 std::string next = *missing_dependencies.begin(); | 298 const ExtensionPermissionSet& permissions, |
| 324 missing_dependencies.erase(next); | 299 GetSchemasFilter filter, |
| 325 out->insert(next); | 300 SchemaMap* out) const { |
| 326 GetMissingDependencies(next, *out, &missing_dependencies); | 301 for (SchemaMap::const_iterator it = schemas_.begin(); it != schemas_.end(); |
| 302 ++it) { |
| 303 if (filter == ONLY_UNPRIVILEGED && IsWholeAPIPrivileged(it->first)) |
| 304 continue; |
| 305 if (permissions.HasAnyAccessToAPI(it->first)) |
| 306 (*out)[it->first] = it->second; |
| 327 } | 307 } |
| 328 } | 308 } |
| 329 | 309 |
| 330 void ExtensionAPI::GetMissingDependencies( | 310 bool ExtensionAPI::IsWholeAPIPrivileged(const std::string& api_name) const { |
| 331 const std::string& api_name, | 311 return !completely_unprivileged_apis_.count(api_name) && |
| 332 const std::set<std::string>& excluding, | 312 !partially_unprivileged_apis_.count(api_name); |
| 333 std::set<std::string>* out) const { | |
| 334 const base::DictionaryValue* schema = GetSchema(api_name); | |
| 335 CHECK(schema) << "Schema for " << api_name << " not found"; | |
| 336 | |
| 337 ListValue* dependencies = NULL; | |
| 338 if (!schema->GetList("dependencies", &dependencies)) | |
| 339 return; | |
| 340 | |
| 341 for (size_t i = 0; i < dependencies->GetSize(); ++i) { | |
| 342 std::string api_name; | |
| 343 if (dependencies->GetString(i, &api_name) && !excluding.count(api_name)) | |
| 344 out->insert(api_name); | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) const { | |
| 349 std::set<std::string> privileged_apis; | |
| 350 for (std::set<std::string>::iterator i = apis->begin(); i != apis->end(); | |
| 351 ++i) { | |
| 352 if (!completely_unprivileged_apis_.count(*i) && | |
| 353 !partially_unprivileged_apis_.count(*i)) { | |
| 354 privileged_apis.insert(*i); | |
| 355 } | |
| 356 } | |
| 357 for (std::set<std::string>::iterator i = privileged_apis.begin(); | |
| 358 i != privileged_apis.end(); ++i) { | |
| 359 apis->erase(*i); | |
| 360 } | |
| 361 } | |
| 362 | |
| 363 void ExtensionAPI::GetAPIsMatchingURL(const GURL& url, | |
| 364 std::set<std::string>* out) const { | |
| 365 for (std::map<std::string, URLPatternSet>::const_iterator i = | |
| 366 url_matching_apis_.begin(); i != url_matching_apis_.end(); ++i) { | |
| 367 if (i->second.MatchesURL(url)) | |
| 368 out->insert(i->first); | |
| 369 } | |
| 370 } | 313 } |
| 371 | 314 |
| 372 } // namespace extensions | 315 } // namespace extensions |
| OLD | NEW |