diff options
author | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-01 13:07:38 +0000 |
---|---|---|
committer | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-01 13:07:38 +0000 |
commit | d6ec84afaa637fb057ad1cbf5a7c03b02b6a456c (patch) | |
tree | 8b0e7c2d2a8ace6fa88a2eca6901564156cea539 /extensions/common/extension_api.cc | |
parent | 62f9063b7cb9bc8cc3bc1aa5ebe19a5d216a54cf (diff) | |
download | chromium_src-d6ec84afaa637fb057ad1cbf5a7c03b02b6a456c.zip chromium_src-d6ec84afaa637fb057ad1cbf5a7c03b02b6a456c.tar.gz chromium_src-d6ec84afaa637fb057ad1cbf5a7c03b02b6a456c.tar.bz2 |
Moved extension_api to src/extensions/common
This is part of the move of extensions code into the extensions
component. This change does not move the generated API data and API
resources.
TBR=darin@chromium.org
BUG=162530
Review URL: https://codereview.chromium.org/50743005
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@232345 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/common/extension_api.cc')
-rw-r--r-- | extensions/common/extension_api.cc | 416 |
1 files changed, 416 insertions, 0 deletions
diff --git a/extensions/common/extension_api.cc b/extensions/common/extension_api.cc new file mode 100644 index 0000000..eda15ac --- /dev/null +++ b/extensions/common/extension_api.cc @@ -0,0 +1,416 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/common/extension_api.h" + +#include <algorithm> +#include <string> +#include <vector> + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "base/values.h" +#include "chrome/common/extensions/api/generated_schemas.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/permissions/permissions_data.h" +#include "extensions/common/features/feature.h" +#include "extensions/common/features/feature_provider.h" +#include "extensions/common/permissions/permission_set.h" +#include "grit/common_resources.h" +#include "grit/extensions_api_resources.h" +#include "ui/base/resource/resource_bundle.h" +#include "url/gurl.h" + +namespace extensions { + +using api::GeneratedSchemas; + +namespace { + +const char* kChildKinds[] = { + "functions", + "events" +}; + +base::StringPiece ReadFromResource(int resource_id) { + return ResourceBundle::GetSharedInstance().GetRawDataResource( + resource_id); +} + +scoped_ptr<base::ListValue> LoadSchemaList(const std::string& name, + const base::StringPiece& schema) { + std::string error_message; + scoped_ptr<base::Value> result( + base::JSONReader::ReadAndReturnError( + schema, + base::JSON_PARSE_RFC | base::JSON_DETACHABLE_CHILDREN, // options + NULL, // error code + &error_message)); + + // Tracking down http://crbug.com/121424 + char buf[128]; + base::snprintf(buf, arraysize(buf), "%s: (%d) '%s'", + name.c_str(), + result.get() ? result->GetType() : -1, + error_message.c_str()); + + CHECK(result.get()) << error_message << " for schema " << schema; + CHECK(result->IsType(base::Value::TYPE_LIST)) << " for schema " << schema; + return scoped_ptr<base::ListValue>(static_cast<base::ListValue*>( + result.release())); +} + +const base::DictionaryValue* FindListItem(const base::ListValue* list, + const std::string& property_name, + const std::string& property_value) { + for (size_t i = 0; i < list->GetSize(); ++i) { + const base::DictionaryValue* item = NULL; + CHECK(list->GetDictionary(i, &item)) + << property_value << "/" << property_name; + std::string value; + if (item->GetString(property_name, &value) && value == property_value) + return item; + } + + return NULL; +} + +const base::DictionaryValue* GetSchemaChild( + const base::DictionaryValue* schema_node, + const std::string& child_name) { + const base::DictionaryValue* child_node = NULL; + for (size_t i = 0; i < arraysize(kChildKinds); ++i) { + const base::ListValue* list_node = NULL; + if (!schema_node->GetList(kChildKinds[i], &list_node)) + continue; + child_node = FindListItem(list_node, "name", child_name); + if (child_node) + return child_node; + } + + return NULL; +} + +struct Static { + Static() + : api(ExtensionAPI::CreateWithDefaultConfiguration()) { + } + scoped_ptr<ExtensionAPI> api; +}; + +base::LazyInstance<Static> g_lazy_instance = LAZY_INSTANCE_INITIALIZER; + +// If it exists and does not already specify a namespace, then the value stored +// with key |key| in |schema| will be updated to |schema_namespace| + "." + +// |schema[key]|. +void MaybePrefixFieldWithNamespace(const std::string& schema_namespace, + base::DictionaryValue* schema, + const std::string& key) { + if (!schema->HasKey(key)) + return; + + std::string old_id; + CHECK(schema->GetString(key, &old_id)); + if (old_id.find(".") == std::string::npos) + schema->SetString(key, schema_namespace + "." + old_id); +} + +// Modify all "$ref" keys anywhere in |schema| to be prefxied by +// |schema_namespace| if they do not already specify a namespace. +void PrefixRefsWithNamespace(const std::string& schema_namespace, + base::Value* value) { + base::ListValue* list = NULL; + base::DictionaryValue* dict = NULL; + if (value->GetAsList(&list)) { + for (base::ListValue::iterator i = list->begin(); i != list->end(); ++i) { + PrefixRefsWithNamespace(schema_namespace, *i); + } + } else if (value->GetAsDictionary(&dict)) { + MaybePrefixFieldWithNamespace(schema_namespace, dict, "$ref"); + for (base::DictionaryValue::Iterator i(*dict); !i.IsAtEnd(); i.Advance()) { + base::Value* value = NULL; + CHECK(dict->GetWithoutPathExpansion(i.key(), &value)); + PrefixRefsWithNamespace(schema_namespace, value); + } + } +} + +// Modify all objects in the "types" section of the schema to be prefixed by +// |schema_namespace| if they do not already specify a namespace. +void PrefixTypesWithNamespace(const std::string& schema_namespace, + base::DictionaryValue* schema) { + if (!schema->HasKey("types")) + return; + + // Add the namespace to all of the types defined in this schema + base::ListValue *types = NULL; + CHECK(schema->GetList("types", &types)); + for (size_t i = 0; i < types->GetSize(); ++i) { + base::DictionaryValue *type = NULL; + CHECK(types->GetDictionary(i, &type)); + MaybePrefixFieldWithNamespace(schema_namespace, type, "id"); + MaybePrefixFieldWithNamespace(schema_namespace, type, "customBindings"); + } +} + +// Modify the schema so that all types are fully qualified. +void PrefixWithNamespace(const std::string& schema_namespace, + base::DictionaryValue* schema) { + PrefixTypesWithNamespace(schema_namespace, schema); + PrefixRefsWithNamespace(schema_namespace, schema); +} + +} // namespace + +// static +ExtensionAPI* ExtensionAPI::GetSharedInstance() { + return g_lazy_instance.Get().api.get(); +} + +// static +ExtensionAPI* ExtensionAPI::CreateWithDefaultConfiguration() { + ExtensionAPI* api = new ExtensionAPI(); + api->InitDefaultConfiguration(); + return api; +} + +// static +void ExtensionAPI::SplitDependencyName(const std::string& full_name, + std::string* feature_type, + std::string* feature_name) { + size_t colon_index = full_name.find(':'); + if (colon_index == std::string::npos) { + // TODO(aa): Remove this code when all API descriptions have been updated. + *feature_type = "api"; + *feature_name = full_name; + return; + } + + *feature_type = full_name.substr(0, colon_index); + *feature_name = full_name.substr(colon_index + 1); +} + +void ExtensionAPI::LoadSchema(const std::string& name, + const base::StringPiece& schema) { + scoped_ptr<base::ListValue> schema_list(LoadSchemaList(name, schema)); + std::string schema_namespace; + + while (!schema_list->empty()) { + base::DictionaryValue* schema = NULL; + { + scoped_ptr<base::Value> value; + schema_list->Remove(schema_list->GetSize() - 1, &value); + CHECK(value.release()->GetAsDictionary(&schema)); + } + + CHECK(schema->GetString("namespace", &schema_namespace)); + PrefixWithNamespace(schema_namespace, schema); + schemas_[schema_namespace] = make_linked_ptr(schema); + if (!GeneratedSchemas::IsGenerated(schema_namespace)) + CHECK_EQ(1u, unloaded_schemas_.erase(schema_namespace)); + } +} + +ExtensionAPI::ExtensionAPI() : default_configuration_initialized_(false) { +} + +ExtensionAPI::~ExtensionAPI() { +} + +void ExtensionAPI::InitDefaultConfiguration() { + const char* names[] = {"api", "manifest", "permission"}; + for (size_t i = 0; i < arraysize(names); ++i) + RegisterDependencyProvider(names[i], FeatureProvider::GetByName(names[i])); + + // Schemas to be loaded from resources. + CHECK(unloaded_schemas_.empty()); + RegisterSchemaResource("app", IDR_EXTENSION_API_JSON_APP); + RegisterSchemaResource("browserAction", IDR_EXTENSION_API_JSON_BROWSERACTION); + RegisterSchemaResource("browsingData", IDR_EXTENSION_API_JSON_BROWSINGDATA); + RegisterSchemaResource("commands", IDR_EXTENSION_API_JSON_COMMANDS); + RegisterSchemaResource("declarativeContent", + IDR_EXTENSION_API_JSON_DECLARATIVE_CONTENT); + RegisterSchemaResource("declarativeWebRequest", + IDR_EXTENSION_API_JSON_DECLARATIVE_WEBREQUEST); + RegisterSchemaResource("runtime", IDR_EXTENSION_API_JSON_RUNTIME); + RegisterSchemaResource("fileBrowserHandler", + IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER); + RegisterSchemaResource("fileBrowserPrivate", + IDR_EXTENSION_API_JSON_FILEBROWSERPRIVATE); + RegisterSchemaResource("inputMethodPrivate", + IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE); + RegisterSchemaResource("pageAction", IDR_EXTENSION_API_JSON_PAGEACTION); + RegisterSchemaResource("pageActions", IDR_EXTENSION_API_JSON_PAGEACTIONS); + RegisterSchemaResource("privacy", IDR_EXTENSION_API_JSON_PRIVACY); + RegisterSchemaResource("processes", IDR_EXTENSION_API_JSON_PROCESSES); + RegisterSchemaResource("proxy", IDR_EXTENSION_API_JSON_PROXY); + RegisterSchemaResource("scriptBadge", IDR_EXTENSION_API_JSON_SCRIPTBADGE); + RegisterSchemaResource("streamsPrivate", + IDR_EXTENSION_API_JSON_STREAMSPRIVATE); + RegisterSchemaResource("ttsEngine", IDR_EXTENSION_API_JSON_TTSENGINE); + RegisterSchemaResource("tts", IDR_EXTENSION_API_JSON_TTS); + RegisterSchemaResource("types", IDR_EXTENSION_API_JSON_TYPES); + RegisterSchemaResource("types.private", IDR_EXTENSION_API_JSON_TYPES_PRIVATE); + RegisterSchemaResource("webRequestInternal", + IDR_EXTENSION_API_JSON_WEBREQUESTINTERNAL); + RegisterSchemaResource("webstore", IDR_EXTENSION_API_JSON_WEBSTORE); + RegisterSchemaResource("webstorePrivate", + IDR_EXTENSION_API_JSON_WEBSTOREPRIVATE); + default_configuration_initialized_ = true; +} + +void ExtensionAPI::RegisterSchemaResource(const std::string& name, + int resource_id) { + unloaded_schemas_[name] = resource_id; +} + +void ExtensionAPI::RegisterDependencyProvider(const std::string& name, + FeatureProvider* provider) { + dependency_providers_[name] = provider; +} + +bool ExtensionAPI::IsAnyFeatureAvailableToContext(const std::string& api_name, + const Extension* extension, + Feature::Context context, + const GURL& url) { + FeatureProviderMap::iterator provider = dependency_providers_.find("api"); + CHECK(provider != dependency_providers_.end()); + const std::vector<std::string>& features = + provider->second->GetAllFeatureNames(); + + // Check to see if there are any parts of this API that are allowed in this + // context. + for (std::vector<std::string>::const_iterator i = features.begin(); + i != features.end(); ++i) { + const std::string& feature_name = *i; + if (feature_name != api_name && feature_name.find(api_name + ".") == 0) { + if (IsAvailable(feature_name, extension, context, url).is_available()) + return true; + } + } + return IsAvailable(api_name, extension, context, url).is_available(); +} + +Feature::Availability ExtensionAPI::IsAvailable(const std::string& full_name, + const Extension* extension, + Feature::Context context, + const GURL& url) { + Feature* feature = GetFeatureDependency(full_name); + CHECK(feature) << full_name; + + Feature::Availability availability = + feature->IsAvailableToContext(extension, context, url); + if (!availability.is_available()) + return availability; + + for (std::set<std::string>::iterator iter = feature->dependencies().begin(); + iter != feature->dependencies().end(); ++iter) { + Feature::Availability dependency_availability = + IsAvailable(*iter, extension, context, url); + if (!dependency_availability.is_available()) + return dependency_availability; + } + + return Feature::CreateAvailability(Feature::IS_AVAILABLE, std::string()); +} + +bool ExtensionAPI::IsPrivileged(const std::string& full_name) { + Feature* feature = GetFeatureDependency(full_name); + CHECK(feature); + DCHECK(!feature->GetContexts()->empty()); + // An API is 'privileged' if it can only be run in a blessed context. + return feature->GetContexts()->size() == + feature->GetContexts()->count(Feature::BLESSED_EXTENSION_CONTEXT); +} + +const base::DictionaryValue* ExtensionAPI::GetSchema( + const std::string& full_name) { + std::string child_name; + std::string api_name = GetAPINameFromFullName(full_name, &child_name); + + const base::DictionaryValue* result = NULL; + SchemaMap::iterator maybe_schema = schemas_.find(api_name); + if (maybe_schema != schemas_.end()) { + result = maybe_schema->second.get(); + } else { + // Might not have loaded yet; or might just not exist. + UnloadedSchemaMap::iterator maybe_schema_resource = + unloaded_schemas_.find(api_name); + if (maybe_schema_resource != unloaded_schemas_.end()) { + LoadSchema(maybe_schema_resource->first, + ReadFromResource(maybe_schema_resource->second)); + } else if (default_configuration_initialized_ && + GeneratedSchemas::IsGenerated(api_name)) { + LoadSchema(api_name, GeneratedSchemas::Get(api_name)); + } else { + return NULL; + } + + maybe_schema = schemas_.find(api_name); + CHECK(schemas_.end() != maybe_schema); + result = maybe_schema->second.get(); + } + + if (!child_name.empty()) + result = GetSchemaChild(result, child_name); + + return result; +} + +Feature* ExtensionAPI::GetFeatureDependency(const std::string& full_name) { + std::string feature_type; + std::string feature_name; + SplitDependencyName(full_name, &feature_type, &feature_name); + + FeatureProviderMap::iterator provider = + dependency_providers_.find(feature_type); + if (provider == dependency_providers_.end()) + return NULL; + + Feature* feature = provider->second->GetFeature(feature_name); + // Try getting the feature for the parent API, if this was a child. + if (!feature) { + std::string child_name; + feature = provider->second->GetFeature( + GetAPINameFromFullName(feature_name, &child_name)); + } + return feature; +} + +std::string ExtensionAPI::GetAPINameFromFullName(const std::string& full_name, + std::string* child_name) { + std::string api_name_candidate = full_name; + while (true) { + if (schemas_.find(api_name_candidate) != schemas_.end() || + GeneratedSchemas::IsGenerated(api_name_candidate) || + unloaded_schemas_.find(api_name_candidate) != unloaded_schemas_.end()) { + std::string result = api_name_candidate; + + if (child_name) { + if (result.length() < full_name.length()) + *child_name = full_name.substr(result.length() + 1); + else + *child_name = ""; + } + + return result; + } + + size_t last_dot_index = api_name_candidate.rfind('.'); + if (last_dot_index == std::string::npos) + break; + + api_name_candidate = api_name_candidate.substr(0, last_dot_index); + } + + *child_name = ""; + return std::string(); +} + +} // namespace extensions |