blob: c8e32955043ad29465afde16eac1a87e63bcc39d [file] [log] [blame]
// Copyright (c) 2012 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 "chrome/common/extensions/api/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/string_number_conversions.h"
#include "base/string_split.h"
#include "base/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/features/simple_feature_provider.h"
#include "chrome/common/extensions/permissions/permission_set.h"
#include "googleurl/src/gurl.h"
#include "grit/common_resources.h"
#include "grit/extensions_api_resources.h"
#include "ui/base/layout.h"
#include "ui/base/resource/resource_bundle.h"
using base::DictionaryValue;
using base::ListValue;
using base::Value;
namespace extensions {
using api::GeneratedSchemas;
namespace {
const char* kChildKinds[] = {
"functions",
"events"
};
// Returns true if |dict| has an unprivileged "true" property.
bool IsUnprivileged(const DictionaryValue* dict) {
bool unprivileged = false;
return dict->GetBoolean("unprivileged", &unprivileged) && unprivileged;
}
// Returns whether the list at |name_space_node|.|child_kind| contains any
// children with an { "unprivileged": true } property.
bool HasUnprivilegedChild(const DictionaryValue* name_space_node,
const std::string& child_kind) {
const ListValue* child_list = NULL;
const DictionaryValue* child_dict = NULL;
if (name_space_node->GetList(child_kind, &child_list)) {
for (size_t i = 0; i < child_list->GetSize(); ++i) {
const DictionaryValue* item = NULL;
CHECK(child_list->GetDictionary(i, &item));
if (IsUnprivileged(item))
return true;
}
} else if (name_space_node->GetDictionary(child_kind, &child_dict)) {
for (DictionaryValue::Iterator it(*child_dict); it.HasNext();
it.Advance()) {
const DictionaryValue* item = NULL;
CHECK(it.value().GetAsDictionary(&item));
if (IsUnprivileged(item))
return true;
}
}
return false;
}
base::StringPiece ReadFromResource(int resource_id) {
return ResourceBundle::GetSharedInstance().GetRawDataResource(
resource_id, ui::SCALE_FACTOR_NONE);
}
scoped_ptr<ListValue> LoadSchemaList(const std::string& name,
const base::StringPiece& schema) {
std::string error_message;
scoped_ptr<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(Value::TYPE_LIST)) << " for schema " << schema;
return scoped_ptr<ListValue>(static_cast<ListValue*>(result.release()));
}
const DictionaryValue* FindListItem(const ListValue* list,
const std::string& property_name,
const std::string& property_value) {
for (size_t i = 0; i < list->GetSize(); ++i) {
const 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 DictionaryValue* GetSchemaChild(const DictionaryValue* schema_node,
const std::string& child_name) {
const DictionaryValue* child_node = NULL;
for (size_t i = 0; i < arraysize(kChildKinds); ++i) {
const 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,
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,
Value* value) {
if (value->IsType(Value::TYPE_LIST)) {
ListValue* list;
CHECK(value->GetAsList(&list));
for (ListValue::iterator i = list->begin(); i != list->end(); ++i) {
PrefixRefsWithNamespace(schema_namespace, *i);
}
} else if (value->IsType(Value::TYPE_DICTIONARY)) {
DictionaryValue* dict;
CHECK(value->GetAsDictionary(&dict));
MaybePrefixFieldWithNamespace(schema_namespace, dict, "$ref");
for (DictionaryValue::key_iterator i = dict->begin_keys();
i != dict->end_keys(); ++i) {
Value* next_value;
CHECK(dict->GetWithoutPathExpansion(*i, &next_value));
PrefixRefsWithNamespace(schema_namespace, next_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,
DictionaryValue* schema) {
if (!schema->HasKey("types"))
return;
// Add the namespace to all of the types defined in this schema
ListValue *types;
CHECK(schema->GetList("types", &types));
for (size_t i = 0; i < types->GetSize(); ++i) {
DictionaryValue *type;
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,
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<ListValue> schema_list(LoadSchemaList(name, schema));
std::string schema_namespace;
while (!schema_list->empty()) {
DictionaryValue* schema = NULL;
{
Value* value = NULL;
schema_list->Remove(schema_list->GetSize() - 1, &value);
CHECK(value->IsType(Value::TYPE_DICTIONARY));
schema = static_cast<DictionaryValue*>(value);
}
CHECK(schema->GetString("namespace", &schema_namespace));
PrefixWithNamespace(schema_namespace, schema);
schemas_[schema_namespace] = make_linked_ptr(schema);
CHECK_EQ(1u, unloaded_schemas_.erase(schema_namespace));
// Populate |{completely,partially}_unprivileged_apis_|.
//
// For "partially", only need to look at functions/events; even though
// there are unprivileged properties (e.g. in extensions), access to those
// never reaches C++ land.
bool unprivileged = false;
if (schema->GetBoolean("unprivileged", &unprivileged) && unprivileged) {
completely_unprivileged_apis_.insert(schema_namespace);
} else if (HasUnprivilegedChild(schema, "functions") ||
HasUnprivilegedChild(schema, "events") ||
HasUnprivilegedChild(schema, "properties")) {
partially_unprivileged_apis_.insert(schema_namespace);
}
// Populate |url_matching_apis_|.
ListValue* matches = NULL;
if (schema->GetList("matches", &matches)) {
URLPatternSet pattern_set;
for (size_t i = 0; i < matches->GetSize(); ++i) {
std::string pattern;
CHECK(matches->GetString(i, &pattern));
pattern_set.AddPattern(
URLPattern(UserScript::kValidUserScriptSchemes, pattern));
}
url_matching_apis_[schema_namespace] = pattern_set;
}
// Populate feature maps.
// TODO(aa): Consider not storing features that can never run on the current
// machine (e.g., because of platform restrictions).
bool uses_feature_system = false;
schema->GetBoolean("uses_feature_system", &uses_feature_system);
if (!uses_feature_system)
continue;
Feature* feature = new Feature();
feature->set_name(schema_namespace);
feature->Parse(schema);
FeatureMap* schema_features = new FeatureMap();
CHECK(features_.insert(
std::make_pair(schema_namespace,
make_linked_ptr(schema_features))).second);
CHECK(schema_features->insert(
std::make_pair("", make_linked_ptr(feature))).second);
for (size_t i = 0; i < arraysize(kChildKinds); ++i) {
ListValue* child_list = NULL;
schema->GetList(kChildKinds[i], &child_list);
if (!child_list)
continue;
for (size_t j = 0; j < child_list->GetSize(); ++j) {
DictionaryValue* child = NULL;
CHECK(child_list->GetDictionary(j, &child));
scoped_ptr<Feature> child_feature(new Feature(*feature));
child_feature->Parse(child);
if (child_feature->Equals(*feature))
continue; // no need to store no-op features
std::string child_name;
CHECK(child->GetString("name", &child_name));
child_feature->set_name(schema_namespace + "." + child_name);
CHECK(schema_features->insert(
std::make_pair(child_name,
make_linked_ptr(child_feature.release()))).second);
}
}
}
}
ExtensionAPI::ExtensionAPI() {
RegisterDependencyProvider("api", this);
// TODO(aa): Can remove this when all JSON files are converted.
RegisterDependencyProvider("", this);
}
ExtensionAPI::~ExtensionAPI() {
}
void ExtensionAPI::InitDefaultConfiguration() {
RegisterDependencyProvider(
"manifest", SimpleFeatureProvider::GetManifestFeatures());
RegisterDependencyProvider(
"permission", SimpleFeatureProvider::GetPermissionFeatures());
// Schemas to be loaded from resources.
CHECK(unloaded_schemas_.empty());
RegisterSchema("app", ReadFromResource(
IDR_EXTENSION_API_JSON_APP));
RegisterSchema("bookmarks", ReadFromResource(
IDR_EXTENSION_API_JSON_BOOKMARKS));
RegisterSchema("browserAction", ReadFromResource(
IDR_EXTENSION_API_JSON_BROWSERACTION));
RegisterSchema("browsingData", ReadFromResource(
IDR_EXTENSION_API_JSON_BROWSINGDATA));
RegisterSchema("chromeosInfoPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE));
RegisterSchema("cloudPrintPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_CLOUDPRINTPRIVATE));
RegisterSchema("contentSettings", ReadFromResource(
IDR_EXTENSION_API_JSON_CONTENTSETTINGS));
RegisterSchema("contextMenus", ReadFromResource(
IDR_EXTENSION_API_JSON_CONTEXTMENUS));
RegisterSchema("cookies", ReadFromResource(
IDR_EXTENSION_API_JSON_COOKIES));
RegisterSchema("debugger", ReadFromResource(
IDR_EXTENSION_API_JSON_DEBUGGER));
RegisterSchema("declarativeWebRequest", ReadFromResource(
IDR_EXTENSION_API_JSON_DECLARATIVE_WEBREQUEST));
RegisterSchema("devtools", ReadFromResource(
IDR_EXTENSION_API_JSON_DEVTOOLS));
RegisterSchema("events", ReadFromResource(
IDR_EXTENSION_API_JSON_EVENTS));
RegisterSchema("experimental.accessibility", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_ACCESSIBILITY));
RegisterSchema("experimental.app", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_APP));
RegisterSchema("experimental.bookmarkManager", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_BOOKMARKMANAGER));
RegisterSchema("experimental.commands", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_COMMANDS));
RegisterSchema("experimental.infobars", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_INFOBARS));
RegisterSchema("experimental.input.virtualKeyboard", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_VIRTUALKEYBOARD));
RegisterSchema("experimental.offscreenTabs", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_OFFSCREENTABS));
RegisterSchema("experimental.processes", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_PROCESSES));
RegisterSchema("experimental.record", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_RECORD));
RegisterSchema("experimental.rlz", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_RLZ));
RegisterSchema("runtime", ReadFromResource(
IDR_EXTENSION_API_JSON_RUNTIME));
RegisterSchema("experimental.speechInput", ReadFromResource(
IDR_EXTENSION_API_JSON_EXPERIMENTAL_SPEECHINPUT));
RegisterSchema("extension", ReadFromResource(
IDR_EXTENSION_API_JSON_EXTENSION));
RegisterSchema("fileBrowserHandler", ReadFromResource(
IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER));
RegisterSchema("fileBrowserHandlerInternal", ReadFromResource(
IDR_EXTENSION_API_JSON_FILEBROWSERHANDLERINTERNAL));
RegisterSchema("fileBrowserPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_FILEBROWSERPRIVATE));
RegisterSchema("fontSettings", ReadFromResource(
IDR_EXTENSION_API_JSON_FONTSSETTINGS));
RegisterSchema("history", ReadFromResource(
IDR_EXTENSION_API_JSON_HISTORY));
RegisterSchema("i18n", ReadFromResource(
IDR_EXTENSION_API_JSON_I18N));
RegisterSchema("idle", ReadFromResource(
IDR_EXTENSION_API_JSON_IDLE));
RegisterSchema("input.ime", ReadFromResource(
IDR_EXTENSION_API_JSON_INPUT_IME));
RegisterSchema("inputMethodPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE));
RegisterSchema("managedModePrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_MANAGEDMODEPRIVATE));
RegisterSchema("management", ReadFromResource(
IDR_EXTENSION_API_JSON_MANAGEMENT));
RegisterSchema("mediaPlayerPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_MEDIAPLAYERPRIVATE));
RegisterSchema("metricsPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_METRICSPRIVATE));
RegisterSchema("echoPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_ECHOPRIVATE));
RegisterSchema("omnibox", ReadFromResource(
IDR_EXTENSION_API_JSON_OMNIBOX));
RegisterSchema("pageAction", ReadFromResource(
IDR_EXTENSION_API_JSON_PAGEACTION));
RegisterSchema("pageActions", ReadFromResource(
IDR_EXTENSION_API_JSON_PAGEACTIONS));
RegisterSchema("pageCapture", ReadFromResource(
IDR_EXTENSION_API_JSON_PAGECAPTURE));
RegisterSchema("permissions", ReadFromResource(
IDR_EXTENSION_API_JSON_PERMISSIONS));
RegisterSchema("privacy", ReadFromResource(
IDR_EXTENSION_API_JSON_PRIVACY));
RegisterSchema("proxy", ReadFromResource(
IDR_EXTENSION_API_JSON_PROXY));
RegisterSchema("scriptBadge", ReadFromResource(
IDR_EXTENSION_API_JSON_SCRIPTBADGE));
RegisterSchema("storage", ReadFromResource(
IDR_EXTENSION_API_JSON_STORAGE));
RegisterSchema("systemPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_SYSTEMPRIVATE));
RegisterSchema("tabs", ReadFromResource(
IDR_EXTENSION_API_JSON_TABS));
RegisterSchema("terminalPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_TERMINALPRIVATE));
RegisterSchema("test", ReadFromResource(
IDR_EXTENSION_API_JSON_TEST));
RegisterSchema("topSites", ReadFromResource(
IDR_EXTENSION_API_JSON_TOPSITES));
RegisterSchema("ttsEngine", ReadFromResource(
IDR_EXTENSION_API_JSON_TTSENGINE));
RegisterSchema("tts", ReadFromResource(
IDR_EXTENSION_API_JSON_TTS));
RegisterSchema("types", ReadFromResource(
IDR_EXTENSION_API_JSON_TYPES));
RegisterSchema("wallpaperPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_WALLPAPERPRIVATE));
RegisterSchema("webNavigation", ReadFromResource(
IDR_EXTENSION_API_JSON_WEBNAVIGATION));
RegisterSchema("webRequest", ReadFromResource(
IDR_EXTENSION_API_JSON_WEBREQUEST));
RegisterSchema("webRequestInternal", ReadFromResource(
IDR_EXTENSION_API_JSON_WEBREQUESTINTERNAL));
RegisterSchema("webSocketProxyPrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_WEBSOCKETPROXYPRIVATE));
RegisterSchema("webstore", ReadFromResource(
IDR_EXTENSION_API_JSON_WEBSTORE));
RegisterSchema("webstorePrivate", ReadFromResource(
IDR_EXTENSION_API_JSON_WEBSTOREPRIVATE));
RegisterSchema("windows", ReadFromResource(
IDR_EXTENSION_API_JSON_WINDOWS));
// Schemas to be loaded via JSON generated from IDL files.
GeneratedSchemas::Get(&unloaded_schemas_);
}
void ExtensionAPI::RegisterSchema(const std::string& name,
const base::StringPiece& source) {
unloaded_schemas_[name] = source;
}
void ExtensionAPI::RegisterDependencyProvider(const std::string& name,
FeatureProvider* provider) {
dependency_providers_[name] = provider;
}
bool ExtensionAPI::IsAvailable(const std::string& full_name,
const Extension* extension,
Feature::Context context) {
std::set<std::string> dependency_names;
dependency_names.insert(full_name);
ResolveDependencies(&dependency_names);
for (std::set<std::string>::iterator iter = dependency_names.begin();
iter != dependency_names.end(); ++iter) {
Feature* feature = GetFeatureDependency(full_name);
CHECK(feature) << *iter;
Feature::Availability availability =
feature->IsAvailableToContext(extension, context);
if (availability != Feature::IS_AVAILABLE)
return false;
}
return true;
}
bool ExtensionAPI::IsPrivileged(const std::string& full_name) {
std::string child_name;
std::string api_name = GetAPINameFromFullName(full_name, &child_name);
// First try to use the feature system.
Feature* feature(GetFeature(full_name));
if (feature) {
// An API is 'privileged' if it or any of its dependencies can only be run
// in a blessed context.
std::set<std::string> resolved_dependencies;
resolved_dependencies.insert(full_name);
ResolveDependencies(&resolved_dependencies);
for (std::set<std::string>::iterator iter = resolved_dependencies.begin();
iter != resolved_dependencies.end(); ++iter) {
Feature* dependency = GetFeatureDependency(*iter);
for (std::set<Feature::Context>::iterator context =
dependency->contexts()->begin();
context != dependency->contexts()->end(); ++context) {
if (*context != Feature::BLESSED_EXTENSION_CONTEXT)
return false;
}
}
return true;
}
// If this API hasn't been converted yet, fall back to the old system.
if (completely_unprivileged_apis_.count(api_name))
return false;
const DictionaryValue* schema = GetSchema(api_name);
if (partially_unprivileged_apis_.count(api_name))
return IsChildNamePrivileged(schema, child_name);
return true;
}
bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node,
const std::string& child_name) {
bool unprivileged = false;
const DictionaryValue* child = GetSchemaChild(name_space_node, child_name);
if (!child || !child->GetBoolean("unprivileged", &unprivileged))
return true;
return !unprivileged;
}
const DictionaryValue* ExtensionAPI::GetSchema(const std::string& full_name) {
std::string child_name;
std::string api_name = GetAPINameFromFullName(full_name, &child_name);
const 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.
std::map<std::string, base::StringPiece>::iterator maybe_schema_resource =
unloaded_schemas_.find(api_name);
if (maybe_schema_resource == unloaded_schemas_.end())
return NULL;
LoadSchema(maybe_schema_resource->first, maybe_schema_resource->second);
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;
}
namespace {
bool IsFeatureAllowedForExtension(const std::string& feature,
const extensions::Extension& extension) {
if (extension.is_platform_app() &&
(feature == "app" || feature == "extension"))
return false;
return true;
}
// Removes APIs from |apis| that should not be allowed for |extension|.
// TODO(kalman/asargent) - Make it possible to specify these rules
// declaratively.
void RemoveDisallowedAPIs(const Extension& extension,
std::set<std::string>* apis) {
CHECK(apis);
std::set<std::string>::iterator i = apis->begin();
while (i != apis->end()) {
if (!IsFeatureAllowedForExtension(*i, extension)) {
apis->erase(i++);
} else {
++i;
}
}
}
} // namespace
scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext(
Feature::Context context, const Extension* extension, const GURL& url) {
// We're forced to load all schemas now because we need to know the metadata
// about every API -- and the metadata is stored in the schemas themselves.
// This is a shame.
// TODO(aa/kalman): store metadata in a separate file and don't load all
// schemas.
LoadAllSchemas();
std::set<std::string> temp_result;
// First handle all the APIs that have been converted to the feature system.
if (extension) {
for (APIFeatureMap::iterator iter = features_.begin();
iter != features_.end(); ++iter) {
if (IsAvailable(iter->first, extension, context))
temp_result.insert(iter->first);
}
}
// Second, fall back to the old way.
// TODO(aa): Remove this when all APIs have been converted.
switch (context) {
case Feature::UNSPECIFIED_CONTEXT:
break;
case Feature::BLESSED_EXTENSION_CONTEXT:
if (extension) {
// Availability is determined by the permissions of the extension.
GetAllowedAPIs(extension, &temp_result);
ResolveDependencies(&temp_result);
RemoveDisallowedAPIs(*extension, &temp_result);
}
break;
case Feature::UNBLESSED_EXTENSION_CONTEXT:
case Feature::CONTENT_SCRIPT_CONTEXT:
if (extension) {
// Same as BLESSED_EXTENSION_CONTEXT, but only those APIs that are
// unprivileged.
GetAllowedAPIs(extension, &temp_result);
// Resolving dependencies before removing unprivileged APIs means that
// some unprivileged APIs may have unrealised dependencies. Too bad!
ResolveDependencies(&temp_result);
RemovePrivilegedAPIs(&temp_result);
}
break;
case Feature::WEB_PAGE_CONTEXT:
if (url.is_valid()) {
// Availablility is determined by the url.
GetAPIsMatchingURL(url, &temp_result);
}
break;
}
// Filter out all non-API features and remove the feature type part of the
// name.
scoped_ptr<std::set<std::string> > result(new std::set<std::string>());
for (std::set<std::string>::iterator iter = temp_result.begin();
iter != temp_result.end(); ++iter) {
std::string feature_type;
std::string feature_name;
SplitDependencyName(*iter, &feature_type, &feature_name);
if (feature_type == "api")
result->insert(feature_name);
}
return result.Pass();
}
Feature* ExtensionAPI::GetFeature(const std::string& full_name) {
// Ensure it's loaded.
GetSchema(full_name);
std::string child_name;
std::string api_namespace = GetAPINameFromFullName(full_name, &child_name);
APIFeatureMap::iterator feature_map = features_.find(api_namespace);
if (feature_map == features_.end())
return NULL;
Feature* result = NULL;
FeatureMap::iterator child_feature = feature_map->second->find(child_name);
if (child_feature != feature_map->second->end()) {
result = child_feature->second.get();
} else {
FeatureMap::iterator parent_feature = feature_map->second->find("");
CHECK(parent_feature != feature_map->second->end());
result = parent_feature->second.get();
}
if (result->contexts()->empty()) {
LOG(ERROR) << "API feature '" << full_name
<< "' must specify at least one context.";
return NULL;
}
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);
CHECK(provider != dependency_providers_.end()) << full_name;
Feature* feature = provider->second->GetFeature(feature_name);
CHECK(feature) << full_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 (features_.find(api_name_candidate) != features_.end() ||
schemas_.find(api_name_candidate) != schemas_.end() ||
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 "";
}
void ExtensionAPI::GetAllowedAPIs(
const Extension* extension, std::set<std::string>* out) {
for (SchemaMap::const_iterator i = schemas_.begin(); i != schemas_.end();
++i) {
if (features_.find(i->first) != features_.end()) {
// This API is controlled by the feature system. Nothing to do here.
continue;
}
if (extension->required_permission_set()->HasAnyAccessToAPI(i->first) ||
extension->optional_permission_set()->HasAnyAccessToAPI(i->first)) {
out->insert(i->first);
}
}
}
void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) {
std::set<std::string> missing_dependencies;
for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i)
GetMissingDependencies(*i, *out, &missing_dependencies);
while (missing_dependencies.size()) {
std::string next = *missing_dependencies.begin();
missing_dependencies.erase(next);
out->insert(next);
GetMissingDependencies(next, *out, &missing_dependencies);
}
}
void ExtensionAPI::GetMissingDependencies(
const std::string& api_name,
const std::set<std::string>& excluding,
std::set<std::string>* out) {
std::string feature_type;
std::string feature_name;
SplitDependencyName(api_name, &feature_type, &feature_name);
// Only API features can have dependencies for now.
if (feature_type != "api")
return;
const DictionaryValue* schema = GetSchema(feature_name);
CHECK(schema) << "Schema for " << feature_name << " not found";
const ListValue* dependencies = NULL;
if (!schema->GetList("dependencies", &dependencies))
return;
for (size_t i = 0; i < dependencies->GetSize(); ++i) {
std::string dependency_name;
if (dependencies->GetString(i, &dependency_name) &&
!excluding.count(dependency_name)) {
out->insert(dependency_name);
}
}
}
void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) {
std::set<std::string> privileged_apis;
for (std::set<std::string>::iterator i = apis->begin(); i != apis->end();
++i) {
if (!completely_unprivileged_apis_.count(*i) &&
!partially_unprivileged_apis_.count(*i)) {
privileged_apis.insert(*i);
}
}
for (std::set<std::string>::iterator i = privileged_apis.begin();
i != privileged_apis.end(); ++i) {
apis->erase(*i);
}
}
void ExtensionAPI::GetAPIsMatchingURL(const GURL& url,
std::set<std::string>* out) {
for (std::map<std::string, URLPatternSet>::const_iterator i =
url_matching_apis_.begin(); i != url_matching_apis_.end(); ++i) {
if (features_.find(i->first) != features_.end()) {
// This API is controlled by the feature system. Nothing to do.
continue;
}
if (i->second.MatchesURL(url))
out->insert(i->first);
}
}
void ExtensionAPI::LoadAllSchemas() {
while (unloaded_schemas_.size()) {
std::map<std::string, base::StringPiece>::iterator it =
unloaded_schemas_.begin();
LoadSchema(it->first, it->second);
}
}
} // namespace extensions