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.
5 #include "extensions/common/extension_api.h"
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/permissions/permission_set.h"
24 #include "extensions/common/permissions/permissions_data.h"
25 #include "grit/common_resources.h"
26 #include "grit/extensions_api_resources.h"
27 #include "ui/base/resource/resource_bundle.h"
30 namespace extensions {
34 const char* kChildKinds[] = {
39 base::StringPiece ReadFromResource(int resource_id) {
40 return ResourceBundle::GetSharedInstance().GetRawDataResource(
44 scoped_ptr<base::ListValue> LoadSchemaList(const std::string& name,
45 const base::StringPiece& schema) {
46 std::string error_message;
47 scoped_ptr<base::Value> result(
48 base::JSONReader::ReadAndReturnError(
50 base::JSON_PARSE_RFC | base::JSON_DETACHABLE_CHILDREN, // options
54 // Tracking down http://crbug.com/121424
56 base::snprintf(buf, arraysize(buf), "%s: (%d) '%s'",
58 result.get() ? result->GetType() : -1,
59 error_message.c_str());
61 CHECK(result.get()) << error_message << " for schema " << schema;
62 CHECK(result->IsType(base::Value::TYPE_LIST)) << " for schema " << schema;
63 return scoped_ptr<base::ListValue>(static_cast<base::ListValue*>(
67 const base::DictionaryValue* FindListItem(const base::ListValue* list,
68 const std::string& property_name,
69 const std::string& property_value) {
70 for (size_t i = 0; i < list->GetSize(); ++i) {
71 const base::DictionaryValue* item = NULL;
72 CHECK(list->GetDictionary(i, &item))
73 << property_value << "/" << property_name;
75 if (item->GetString(property_name, &value) && value == property_value)
82 const base::DictionaryValue* GetSchemaChild(
83 const base::DictionaryValue* schema_node,
84 const std::string& child_name) {
85 const base::DictionaryValue* child_node = NULL;
86 for (size_t i = 0; i < arraysize(kChildKinds); ++i) {
87 const base::ListValue* list_node = NULL;
88 if (!schema_node->GetList(kChildKinds[i], &list_node))
90 child_node = FindListItem(list_node, "name", child_name);
100 : api(ExtensionAPI::CreateWithDefaultConfiguration()) {
102 scoped_ptr<ExtensionAPI> api;
105 base::LazyInstance<Static> g_lazy_instance = LAZY_INSTANCE_INITIALIZER;
107 // If it exists and does not already specify a namespace, then the value stored
108 // with key |key| in |schema| will be updated to |schema_namespace| + "." +
110 void MaybePrefixFieldWithNamespace(const std::string& schema_namespace,
111 base::DictionaryValue* schema,
112 const std::string& key) {
113 if (!schema->HasKey(key))
117 CHECK(schema->GetString(key, &old_id));
118 if (old_id.find(".") == std::string::npos)
119 schema->SetString(key, schema_namespace + "." + old_id);
122 // Modify all "$ref" keys anywhere in |schema| to be prefxied by
123 // |schema_namespace| if they do not already specify a namespace.
124 void PrefixRefsWithNamespace(const std::string& schema_namespace,
125 base::Value* value) {
126 base::ListValue* list = NULL;
127 base::DictionaryValue* dict = NULL;
128 if (value->GetAsList(&list)) {
129 for (base::ListValue::iterator i = list->begin(); i != list->end(); ++i) {
130 PrefixRefsWithNamespace(schema_namespace, *i);
132 } else if (value->GetAsDictionary(&dict)) {
133 MaybePrefixFieldWithNamespace(schema_namespace, dict, "$ref");
134 for (base::DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) {
135 base::Value* value = NULL;
136 CHECK(dict->GetWithoutPathExpansion(i.key(), &value));
137 PrefixRefsWithNamespace(schema_namespace, value);
142 // Modify all objects in the "types" section of the schema to be prefixed by
143 // |schema_namespace| if they do not already specify a namespace.
144 void PrefixTypesWithNamespace(const std::string& schema_namespace,
145 base::DictionaryValue* schema) {
146 if (!schema->HasKey("types"))
149 // Add the namespace to all of the types defined in this schema
150 base::ListValue *types = NULL;
151 CHECK(schema->GetList("types", &types));
152 for (size_t i = 0; i < types->GetSize(); ++i) {
153 base::DictionaryValue *type = NULL;
154 CHECK(types->GetDictionary(i, &type));
155 MaybePrefixFieldWithNamespace(schema_namespace, type, "id");
156 MaybePrefixFieldWithNamespace(schema_namespace, type, "customBindings");
160 // Modify the schema so that all types are fully qualified.
161 void PrefixWithNamespace(const std::string& schema_namespace,
162 base::DictionaryValue* schema) {
163 PrefixTypesWithNamespace(schema_namespace, schema);
164 PrefixRefsWithNamespace(schema_namespace, schema);
170 ExtensionAPI* ExtensionAPI::GetSharedInstance() {
171 return g_lazy_instance.Get().api.get();
175 ExtensionAPI* ExtensionAPI::CreateWithDefaultConfiguration() {
176 ExtensionAPI* api = new ExtensionAPI();
177 api->InitDefaultConfiguration();
182 void ExtensionAPI::SplitDependencyName(const std::string& full_name,
183 std::string* feature_type,
184 std::string* feature_name) {
185 size_t colon_index = full_name.find(':');
186 if (colon_index == std::string::npos) {
187 // TODO(aa): Remove this code when all API descriptions have been updated.
188 *feature_type = "api";
189 *feature_name = full_name;
193 *feature_type = full_name.substr(0, colon_index);
194 *feature_name = full_name.substr(colon_index + 1);
197 void ExtensionAPI::LoadSchema(const std::string& name,
198 const base::StringPiece& schema) {
199 scoped_ptr<base::ListValue> schema_list(LoadSchemaList(name, schema));
200 std::string schema_namespace;
201 extensions::ExtensionsClient* extensions_client =
202 extensions::ExtensionsClient::Get();
203 DCHECK(extensions_client);
204 while (!schema_list->empty()) {
205 base::DictionaryValue* schema = NULL;
207 scoped_ptr<base::Value> value;
208 schema_list->Remove(schema_list->GetSize() - 1, &value);
209 CHECK(value.release()->GetAsDictionary(&schema));
212 CHECK(schema->GetString("namespace", &schema_namespace));
213 PrefixWithNamespace(schema_namespace, schema);
214 schemas_[schema_namespace] = make_linked_ptr(schema);
215 if (!extensions_client->IsAPISchemaGenerated(schema_namespace))
216 CHECK_EQ(1u, unloaded_schemas_.erase(schema_namespace));
220 ExtensionAPI::ExtensionAPI() : default_configuration_initialized_(false) {
223 ExtensionAPI::~ExtensionAPI() {
226 void ExtensionAPI::InitDefaultConfiguration() {
227 const char* names[] = {"api", "manifest", "permission"};
228 for (size_t i = 0; i < arraysize(names); ++i)
229 RegisterDependencyProvider(names[i], FeatureProvider::GetByName(names[i]));
231 // Schemas to be loaded from resources.
232 CHECK(unloaded_schemas_.empty());
233 RegisterSchemaResource("app", IDR_EXTENSION_API_JSON_APP);
234 RegisterSchemaResource("browserAction", IDR_EXTENSION_API_JSON_BROWSERACTION);
235 RegisterSchemaResource("commands", IDR_EXTENSION_API_JSON_COMMANDS);
236 RegisterSchemaResource("declarativeContent",
237 IDR_EXTENSION_API_JSON_DECLARATIVE_CONTENT);
238 RegisterSchemaResource("declarativeWebRequest",
239 IDR_EXTENSION_API_JSON_DECLARATIVE_WEBREQUEST);
240 RegisterSchemaResource("fileBrowserHandler",
241 IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER);
242 RegisterSchemaResource("inputMethodPrivate",
243 IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE);
244 RegisterSchemaResource("pageAction", IDR_EXTENSION_API_JSON_PAGEACTION);
245 RegisterSchemaResource("pageActions", IDR_EXTENSION_API_JSON_PAGEACTIONS);
246 RegisterSchemaResource("privacy", IDR_EXTENSION_API_JSON_PRIVACY);
247 RegisterSchemaResource("processes", IDR_EXTENSION_API_JSON_PROCESSES);
248 RegisterSchemaResource("proxy", IDR_EXTENSION_API_JSON_PROXY);
249 RegisterSchemaResource("scriptBadge", IDR_EXTENSION_API_JSON_SCRIPTBADGE);
250 RegisterSchemaResource("ttsEngine", IDR_EXTENSION_API_JSON_TTSENGINE);
251 RegisterSchemaResource("tts", IDR_EXTENSION_API_JSON_TTS);
252 RegisterSchemaResource("types", IDR_EXTENSION_API_JSON_TYPES);
253 RegisterSchemaResource("types.private", IDR_EXTENSION_API_JSON_TYPES_PRIVATE);
254 RegisterSchemaResource("webRequestInternal",
255 IDR_EXTENSION_API_JSON_WEBREQUESTINTERNAL);
256 RegisterSchemaResource("webstore", IDR_EXTENSION_API_JSON_WEBSTORE);
257 RegisterSchemaResource("webViewRequest",
258 IDR_EXTENSION_API_JSON_WEBVIEW_REQUEST);
260 default_configuration_initialized_ = true;
263 void ExtensionAPI::RegisterSchemaResource(const std::string& name,
265 unloaded_schemas_[name] = resource_id;
268 void ExtensionAPI::RegisterDependencyProvider(const std::string& name,
269 FeatureProvider* provider) {
270 dependency_providers_[name] = provider;
273 bool ExtensionAPI::IsAnyFeatureAvailableToContext(const Feature& api,
274 const Extension* extension,
275 Feature::Context context,
277 FeatureProviderMap::iterator provider = dependency_providers_.find("api");
278 CHECK(provider != dependency_providers_.end());
279 if (IsAvailable(api, extension, context, url).is_available())
282 // Check to see if there are any parts of this API that are allowed in this
284 const std::vector<Feature*> features = provider->second->GetChildren(api);
285 for (std::vector<Feature*>::const_iterator feature = features.begin();
286 feature != features.end();
288 if (IsAvailable(**feature, extension, context, url).is_available())
294 Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name,
295 const Extension* extension,
296 Feature::Context context,
298 Feature* feature = GetFeatureDependency(full_name);
299 CHECK(feature) << full_name;
300 return IsAvailable(*feature, extension, context, url);
303 Feature::Availability ExtensionAPI::IsAvailable(const Feature& feature,
304 const Extension* extension,
305 Feature::Context context,
307 Feature::Availability availability =
308 feature.IsAvailableToContext(extension, context, url);
309 if (!availability.is_available())
312 for (std::set<std::string>::iterator iter = feature.dependencies().begin();
313 iter != feature.dependencies().end(); ++iter) {
314 Feature::Availability dependency_availability =
315 IsAvailable(*iter, extension, context, url);
316 if (!dependency_availability.is_available())
317 return dependency_availability;
320 return Feature::CreateAvailability(Feature::IS_AVAILABLE, std::string());
323 bool ExtensionAPI::IsPrivileged(const std::string& full_name) {
324 Feature* feature = GetFeatureDependency(full_name);
326 DCHECK(!feature->GetContexts()->empty());
327 // An API is 'privileged' if it can only be run in a blessed context.
328 return feature->GetContexts()->size() ==
329 feature->GetContexts()->count(Feature::BLESSED_EXTENSION_CONTEXT);
332 const base::DictionaryValue* ExtensionAPI::GetSchema(
333 const std::string& full_name) {
334 std::string child_name;
335 std::string api_name = GetAPINameFromFullName(full_name, &child_name);
337 const base::DictionaryValue* result = NULL;
338 SchemaMap::iterator maybe_schema = schemas_.find(api_name);
339 if (maybe_schema != schemas_.end()) {
340 result = maybe_schema->second.get();
342 // Might not have loaded yet; or might just not exist.
343 UnloadedSchemaMap::iterator maybe_schema_resource =
344 unloaded_schemas_.find(api_name);
345 extensions::ExtensionsClient* extensions_client =
346 extensions::ExtensionsClient::Get();
347 DCHECK(extensions_client);
348 if (maybe_schema_resource != unloaded_schemas_.end()) {
349 LoadSchema(maybe_schema_resource->first,
350 ReadFromResource(maybe_schema_resource->second));
351 } else if (default_configuration_initialized_ &&
352 extensions_client->IsAPISchemaGenerated(api_name)) {
353 LoadSchema(api_name, extensions_client->GetAPISchema(api_name));
358 maybe_schema = schemas_.find(api_name);
359 CHECK(schemas_.end() != maybe_schema);
360 result = maybe_schema->second.get();
363 if (!child_name.empty())
364 result = GetSchemaChild(result, child_name);
369 Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) {
370 std::string feature_type;
371 std::string feature_name;
372 SplitDependencyName(full_name, &feature_type, &feature_name);
374 FeatureProviderMap::iterator provider =
375 dependency_providers_.find(feature_type);
376 if (provider == dependency_providers_.end())
379 Feature* feature = provider->second->GetFeature(feature_name);
380 // Try getting the feature for the parent API, if this was a child.
382 std::string child_name;
383 feature = provider->second->GetFeature(
384 GetAPINameFromFullName(feature_name, &child_name));
389 std::string ExtensionAPI::GetAPINameFromFullName(const std::string& full_name,
390 std::string* child_name) {
391 std::string api_name_candidate = full_name;
392 extensions::ExtensionsClient* extensions_client =
393 extensions::ExtensionsClient::Get();
394 DCHECK(extensions_client);
396 if (schemas_.find(api_name_candidate) != schemas_.end() ||
397 extensions_client->IsAPISchemaGenerated(api_name_candidate) ||
398 unloaded_schemas_.find(api_name_candidate) != unloaded_schemas_.end()) {
399 std::string result = api_name_candidate;
402 if (result.length() < full_name.length())
403 *child_name = full_name.substr(result.length() + 1);
411 size_t last_dot_index = api_name_candidate.rfind('.');
412 if (last_dot_index == std::string::npos)
415 api_name_candidate = api_name_candidate.substr(0, last_dot_index);
419 return std::string();
422 } // namespace extensions