1// Copyright 2013 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 "extensions/common/extension_api.h" 6 7#include <algorithm> 8#include <string> 9#include <vector> 10 11#include "base/json/json_reader.h" 12#include "base/json/json_writer.h" 13#include "base/lazy_instance.h" 14#include "base/logging.h" 15#include "base/strings/string_number_conversions.h" 16#include "base/strings/string_split.h" 17#include "base/strings/string_util.h" 18#include "base/values.h" 19#include "extensions/common/extension.h" 20#include "extensions/common/extensions_client.h" 21#include "extensions/common/features/feature.h" 22#include "extensions/common/features/feature_provider.h" 23#include "extensions/common/features/simple_feature.h" 24#include "extensions/common/permissions/permission_set.h" 25#include "extensions/common/permissions/permissions_data.h" 26#include "ui/base/resource/resource_bundle.h" 27#include "url/gurl.h" 28 29namespace extensions { 30 31namespace { 32 33const char* kChildKinds[] = { 34 "functions", 35 "events" 36}; 37 38base::StringPiece ReadFromResource(int resource_id) { 39 return ResourceBundle::GetSharedInstance().GetRawDataResource( 40 resource_id); 41} 42 43scoped_ptr<base::ListValue> LoadSchemaList(const std::string& name, 44 const base::StringPiece& schema) { 45 std::string error_message; 46 scoped_ptr<base::Value> result( 47 base::JSONReader::ReadAndReturnError( 48 schema, 49 base::JSON_PARSE_RFC | base::JSON_DETACHABLE_CHILDREN, // options 50 NULL, // error code 51 &error_message)); 52 53 // Tracking down http://crbug.com/121424 54 char buf[128]; 55 base::snprintf(buf, arraysize(buf), "%s: (%d) '%s'", 56 name.c_str(), 57 result.get() ? result->GetType() : -1, 58 error_message.c_str()); 59 60 CHECK(result.get()) << error_message << " for schema " << schema; 61 CHECK(result->IsType(base::Value::TYPE_LIST)) << " for schema " << schema; 62 return scoped_ptr<base::ListValue>(static_cast<base::ListValue*>( 63 result.release())); 64} 65 66const base::DictionaryValue* FindListItem(const base::ListValue* list, 67 const std::string& property_name, 68 const std::string& property_value) { 69 for (size_t i = 0; i < list->GetSize(); ++i) { 70 const base::DictionaryValue* item = NULL; 71 CHECK(list->GetDictionary(i, &item)) 72 << property_value << "/" << property_name; 73 std::string value; 74 if (item->GetString(property_name, &value) && value == property_value) 75 return item; 76 } 77 78 return NULL; 79} 80 81const base::DictionaryValue* GetSchemaChild( 82 const base::DictionaryValue* schema_node, 83 const std::string& child_name) { 84 const base::DictionaryValue* child_node = NULL; 85 for (size_t i = 0; i < arraysize(kChildKinds); ++i) { 86 const base::ListValue* list_node = NULL; 87 if (!schema_node->GetList(kChildKinds[i], &list_node)) 88 continue; 89 child_node = FindListItem(list_node, "name", child_name); 90 if (child_node) 91 return child_node; 92 } 93 94 return NULL; 95} 96 97struct Static { 98 Static() 99 : api(ExtensionAPI::CreateWithDefaultConfiguration()) { 100 } 101 scoped_ptr<ExtensionAPI> api; 102}; 103 104base::LazyInstance<Static> g_lazy_instance = LAZY_INSTANCE_INITIALIZER; 105 106// May override |g_lazy_instance| for a test. 107ExtensionAPI* g_shared_instance_for_test = NULL; 108 109// If it exists and does not already specify a namespace, then the value stored 110// with key |key| in |schema| will be updated to |schema_namespace| + "." + 111// |schema[key]|. 112void MaybePrefixFieldWithNamespace(const std::string& schema_namespace, 113 base::DictionaryValue* schema, 114 const std::string& key) { 115 if (!schema->HasKey(key)) 116 return; 117 118 std::string old_id; 119 CHECK(schema->GetString(key, &old_id)); 120 if (old_id.find(".") == std::string::npos) 121 schema->SetString(key, schema_namespace + "." + old_id); 122} 123 124// Modify all "$ref" keys anywhere in |schema| to be prefxied by 125// |schema_namespace| if they do not already specify a namespace. 126void PrefixRefsWithNamespace(const std::string& schema_namespace, 127 base::Value* value) { 128 base::ListValue* list = NULL; 129 base::DictionaryValue* dict = NULL; 130 if (value->GetAsList(&list)) { 131 for (base::ListValue::iterator i = list->begin(); i != list->end(); ++i) { 132 PrefixRefsWithNamespace(schema_namespace, *i); 133 } 134 } else if (value->GetAsDictionary(&dict)) { 135 MaybePrefixFieldWithNamespace(schema_namespace, dict, "$ref"); 136 for (base::DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) { 137 base::Value* value = NULL; 138 CHECK(dict->GetWithoutPathExpansion(i.key(), &value)); 139 PrefixRefsWithNamespace(schema_namespace, value); 140 } 141 } 142} 143 144// Modify all objects in the "types" section of the schema to be prefixed by 145// |schema_namespace| if they do not already specify a namespace. 146void PrefixTypesWithNamespace(const std::string& schema_namespace, 147 base::DictionaryValue* schema) { 148 if (!schema->HasKey("types")) 149 return; 150 151 // Add the namespace to all of the types defined in this schema 152 base::ListValue *types = NULL; 153 CHECK(schema->GetList("types", &types)); 154 for (size_t i = 0; i < types->GetSize(); ++i) { 155 base::DictionaryValue *type = NULL; 156 CHECK(types->GetDictionary(i, &type)); 157 MaybePrefixFieldWithNamespace(schema_namespace, type, "id"); 158 MaybePrefixFieldWithNamespace(schema_namespace, type, "customBindings"); 159 } 160} 161 162// Modify the schema so that all types are fully qualified. 163void PrefixWithNamespace(const std::string& schema_namespace, 164 base::DictionaryValue* schema) { 165 PrefixTypesWithNamespace(schema_namespace, schema); 166 PrefixRefsWithNamespace(schema_namespace, schema); 167} 168 169} // namespace 170 171// static 172ExtensionAPI* ExtensionAPI::GetSharedInstance() { 173 return g_shared_instance_for_test ? g_shared_instance_for_test 174 : g_lazy_instance.Get().api.get(); 175} 176 177// static 178ExtensionAPI* ExtensionAPI::CreateWithDefaultConfiguration() { 179 ExtensionAPI* api = new ExtensionAPI(); 180 api->InitDefaultConfiguration(); 181 return api; 182} 183 184// static 185void ExtensionAPI::SplitDependencyName(const std::string& full_name, 186 std::string* feature_type, 187 std::string* feature_name) { 188 size_t colon_index = full_name.find(':'); 189 if (colon_index == std::string::npos) { 190 // TODO(aa): Remove this code when all API descriptions have been updated. 191 *feature_type = "api"; 192 *feature_name = full_name; 193 return; 194 } 195 196 *feature_type = full_name.substr(0, colon_index); 197 *feature_name = full_name.substr(colon_index + 1); 198} 199 200ExtensionAPI::OverrideSharedInstanceForTest::OverrideSharedInstanceForTest( 201 ExtensionAPI* testing_api) 202 : original_api_(g_shared_instance_for_test) { 203 g_shared_instance_for_test = testing_api; 204} 205 206ExtensionAPI::OverrideSharedInstanceForTest::~OverrideSharedInstanceForTest() { 207 g_shared_instance_for_test = original_api_; 208} 209 210void ExtensionAPI::LoadSchema(const std::string& name, 211 const base::StringPiece& schema) { 212 scoped_ptr<base::ListValue> schema_list(LoadSchemaList(name, schema)); 213 std::string schema_namespace; 214 extensions::ExtensionsClient* extensions_client = 215 extensions::ExtensionsClient::Get(); 216 DCHECK(extensions_client); 217 while (!schema_list->empty()) { 218 base::DictionaryValue* schema = NULL; 219 { 220 scoped_ptr<base::Value> value; 221 schema_list->Remove(schema_list->GetSize() - 1, &value); 222 CHECK(value.release()->GetAsDictionary(&schema)); 223 } 224 225 CHECK(schema->GetString("namespace", &schema_namespace)); 226 PrefixWithNamespace(schema_namespace, schema); 227 schemas_[schema_namespace] = make_linked_ptr(schema); 228 if (!extensions_client->IsAPISchemaGenerated(schema_namespace)) 229 CHECK_EQ(1u, unloaded_schemas_.erase(schema_namespace)); 230 } 231} 232 233ExtensionAPI::ExtensionAPI() : default_configuration_initialized_(false) { 234} 235 236ExtensionAPI::~ExtensionAPI() { 237} 238 239void ExtensionAPI::InitDefaultConfiguration() { 240 const char* names[] = {"api", "manifest", "permission"}; 241 for (size_t i = 0; i < arraysize(names); ++i) 242 RegisterDependencyProvider(names[i], FeatureProvider::GetByName(names[i])); 243 244 ExtensionsClient::Get()->RegisterAPISchemaResources(this); 245 246 default_configuration_initialized_ = true; 247} 248 249void ExtensionAPI::RegisterSchemaResource(const std::string& name, 250 int resource_id) { 251 unloaded_schemas_[name] = resource_id; 252} 253 254void ExtensionAPI::RegisterDependencyProvider(const std::string& name, 255 const FeatureProvider* provider) { 256 dependency_providers_[name] = provider; 257} 258 259bool ExtensionAPI::IsAnyFeatureAvailableToContext(const Feature& api, 260 const Extension* extension, 261 Feature::Context context, 262 const GURL& url) { 263 FeatureProviderMap::iterator provider = dependency_providers_.find("api"); 264 CHECK(provider != dependency_providers_.end()); 265 266 if (api.IsAvailableToContext(extension, context, url).is_available()) 267 return true; 268 269 // Check to see if there are any parts of this API that are allowed in this 270 // context. 271 const std::vector<Feature*> features = provider->second->GetChildren(api); 272 for (std::vector<Feature*>::const_iterator it = features.begin(); 273 it != features.end(); 274 ++it) { 275 if ((*it)->IsAvailableToContext(extension, context, url).is_available()) 276 return true; 277 } 278 return false; 279} 280 281Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name, 282 const Extension* extension, 283 Feature::Context context, 284 const GURL& url) { 285 Feature* feature = GetFeatureDependency(full_name); 286 if (!feature) { 287 return Feature::CreateAvailability(Feature::NOT_PRESENT, 288 std::string("Unknown feature: ") + full_name); 289 } 290 return feature->IsAvailableToContext(extension, context, url); 291} 292 293bool ExtensionAPI::IsAvailableInUntrustedContext(const std::string& name, 294 const Extension* extension) { 295 return IsAvailable(name, extension, Feature::CONTENT_SCRIPT_CONTEXT, GURL()) 296 .is_available() || 297 IsAvailable( 298 name, extension, Feature::UNBLESSED_EXTENSION_CONTEXT, GURL()) 299 .is_available() || 300 IsAvailable(name, extension, Feature::BLESSED_WEB_PAGE_CONTEXT, GURL()) 301 .is_available() || 302 IsAvailable(name, extension, Feature::WEB_PAGE_CONTEXT, GURL()) 303 .is_available(); 304} 305 306bool ExtensionAPI::IsAvailableToWebUI(const std::string& name, 307 const GURL& url) { 308 return IsAvailable(name, NULL, Feature::WEBUI_CONTEXT, url).is_available(); 309} 310 311const base::DictionaryValue* ExtensionAPI::GetSchema( 312 const std::string& full_name) { 313 std::string child_name; 314 std::string api_name = GetAPINameFromFullName(full_name, &child_name); 315 316 const base::DictionaryValue* result = NULL; 317 SchemaMap::iterator maybe_schema = schemas_.find(api_name); 318 if (maybe_schema != schemas_.end()) { 319 result = maybe_schema->second.get(); 320 } else { 321 // Might not have loaded yet; or might just not exist. 322 UnloadedSchemaMap::iterator maybe_schema_resource = 323 unloaded_schemas_.find(api_name); 324 extensions::ExtensionsClient* extensions_client = 325 extensions::ExtensionsClient::Get(); 326 DCHECK(extensions_client); 327 if (maybe_schema_resource != unloaded_schemas_.end()) { 328 LoadSchema(maybe_schema_resource->first, 329 ReadFromResource(maybe_schema_resource->second)); 330 } else if (default_configuration_initialized_ && 331 extensions_client->IsAPISchemaGenerated(api_name)) { 332 LoadSchema(api_name, extensions_client->GetAPISchema(api_name)); 333 } else { 334 return NULL; 335 } 336 337 maybe_schema = schemas_.find(api_name); 338 CHECK(schemas_.end() != maybe_schema); 339 result = maybe_schema->second.get(); 340 } 341 342 if (!child_name.empty()) 343 result = GetSchemaChild(result, child_name); 344 345 return result; 346} 347 348Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) { 349 std::string feature_type; 350 std::string feature_name; 351 SplitDependencyName(full_name, &feature_type, &feature_name); 352 353 FeatureProviderMap::iterator provider = 354 dependency_providers_.find(feature_type); 355 if (provider == dependency_providers_.end()) 356 return NULL; 357 358 Feature* feature = provider->second->GetFeature(feature_name); 359 // Try getting the feature for the parent API, if this was a child. 360 if (!feature) { 361 std::string child_name; 362 feature = provider->second->GetFeature( 363 GetAPINameFromFullName(feature_name, &child_name)); 364 } 365 return feature; 366} 367 368std::string ExtensionAPI::GetAPINameFromFullName(const std::string& full_name, 369 std::string* child_name) { 370 std::string api_name_candidate = full_name; 371 extensions::ExtensionsClient* extensions_client = 372 extensions::ExtensionsClient::Get(); 373 DCHECK(extensions_client); 374 while (true) { 375 if (schemas_.find(api_name_candidate) != schemas_.end() || 376 extensions_client->IsAPISchemaGenerated(api_name_candidate) || 377 unloaded_schemas_.find(api_name_candidate) != unloaded_schemas_.end()) { 378 std::string result = api_name_candidate; 379 380 if (child_name) { 381 if (result.length() < full_name.length()) 382 *child_name = full_name.substr(result.length() + 1); 383 else 384 *child_name = ""; 385 } 386 387 return result; 388 } 389 390 size_t last_dot_index = api_name_candidate.rfind('.'); 391 if (last_dot_index == std::string::npos) 392 break; 393 394 api_name_candidate = api_name_candidate.substr(0, last_dot_index); 395 } 396 397 *child_name = ""; 398 return std::string(); 399} 400 401} // namespace extensions 402