| Index: chrome/common/extensions/api/extension_api.cc
|
| diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc
|
| index 3178975fc93f178669e33f6cd40577b1fa872fb2..e8e816b68cd329506a17135e0fbcd1d0b4d288ec 100644
|
| --- a/chrome/common/extensions/api/extension_api.cc
|
| +++ b/chrome/common/extensions/api/extension_api.cc
|
| @@ -9,13 +9,17 @@
|
| #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/extension_permission_set.h"
|
| +#include "chrome/common/extensions/simple_feature_provider.h"
|
| #include "googleurl/src/gurl.h"
|
| #include "grit/common_resources.h"
|
| #include "ui/base/resource/resource_bundle.h"
|
| @@ -30,6 +34,11 @@ using api::GeneratedSchemas;
|
|
|
| namespace {
|
|
|
| +const char* kChildKinds[] = {
|
| + "functions",
|
| + "events"
|
| +};
|
| +
|
| // 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,
|
| @@ -67,11 +76,73 @@ scoped_ptr<ListValue> LoadSchemaList(const base::StringPiece& schema) {
|
| return scoped_ptr<ListValue>(static_cast<ListValue*>(result.release()));
|
| }
|
|
|
| +DictionaryValue* FindListItem(const ListValue* list,
|
| + const std::string& property_name,
|
| + const std::string& property_value) {
|
| + for (size_t i = 0; i < list->GetSize(); ++i) {
|
| + 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) {
|
| + DictionaryValue* child_node = NULL;
|
| + for (size_t i = 0; i < arraysize(kChildKinds); ++i) {
|
| + 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.InitDefaultConfiguration();
|
| + }
|
| + ExtensionAPI api;
|
| +};
|
| +
|
| +base::LazyInstance<Static> g_lazy_instance = LAZY_INSTANCE_INITIALIZER;
|
| +
|
| } // namespace
|
|
|
| // static
|
| -ExtensionAPI* ExtensionAPI::GetInstance() {
|
| - return Singleton<ExtensionAPI>::get();
|
| +ExtensionAPI* ExtensionAPI::GetSharedInstance() {
|
| + return &g_lazy_instance.Get().api;
|
| +}
|
| +
|
| +// 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 base::StringPiece& schema) {
|
| @@ -115,251 +186,332 @@ void ExtensionAPI::LoadSchema(const base::StringPiece& schema) {
|
| }
|
| 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.
|
| - unloaded_schemas_["app"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_APP);
|
| - unloaded_schemas_["bookmarks"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_BOOKMARKS);
|
| - unloaded_schemas_["browserAction"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_BROWSERACTION);
|
| - unloaded_schemas_["browsingData"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_BROWSINGDATA);
|
| - unloaded_schemas_["chromeAuthPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_CHROMEAUTHPRIVATE);
|
| - unloaded_schemas_["chromeosInfoPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE);
|
| - unloaded_schemas_["contentSettings"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_CONTENTSETTINGS);
|
| - unloaded_schemas_["contextMenus"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_CONTEXTMENUS);
|
| - unloaded_schemas_["cookies"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_COOKIES);
|
| - unloaded_schemas_["debugger"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_DEBUGGER);
|
| - unloaded_schemas_["devtools"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_DEVTOOLS);
|
| - unloaded_schemas_["experimental.accessibility"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_ACCESSIBILITY);
|
| - unloaded_schemas_["experimental.alarms"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_ALARMS);
|
| - unloaded_schemas_["experimental.app"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_APP);
|
| - unloaded_schemas_["experimental.bookmarkManager"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_BOOKMARKMANAGER);
|
| - unloaded_schemas_["experimental.declarative"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_DECLARATIVE);
|
| - unloaded_schemas_["experimental.downloads"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_DOWNLOADS);
|
| - unloaded_schemas_["experimental.extension"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_EXTENSION);
|
| - unloaded_schemas_["experimental.fontSettings"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_FONTSSETTINGS);
|
| - unloaded_schemas_["experimental.identity"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_IDENTITY);
|
| - unloaded_schemas_["experimental.infobars"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_INFOBARS);
|
| - unloaded_schemas_["experimental.input.ui"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_UI);
|
| - unloaded_schemas_["experimental.input.virtualKeyboard"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_VIRTUALKEYBOARD);
|
| - unloaded_schemas_["experimental.keybinding"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_KEYBINDING);
|
| - unloaded_schemas_["experimental.managedMode"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_MANAGEDMODE);
|
| - unloaded_schemas_["experimental.offscreenTabs"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_OFFSCREENTABS);
|
| - unloaded_schemas_["experimental.processes"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_PROCESSES);
|
| - unloaded_schemas_["experimental.rlz"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_RLZ);
|
| - unloaded_schemas_["experimental.serial"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_SERIAL);
|
| - unloaded_schemas_["experimental.socket"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_SOCKET);
|
| - unloaded_schemas_["experimental.speechInput"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_SPEECHINPUT);
|
| - unloaded_schemas_["experimental.webRequest"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXPERIMENTAL_WEBREQUEST);
|
| - unloaded_schemas_["extension"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_EXTENSION);
|
| - unloaded_schemas_["fileBrowserHandler"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER);
|
| - unloaded_schemas_["fileBrowserPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_FILEBROWSERPRIVATE);
|
| - unloaded_schemas_["history"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_HISTORY);
|
| - unloaded_schemas_["i18n"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_I18N);
|
| - unloaded_schemas_["idle"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_IDLE);
|
| - unloaded_schemas_["input.ime"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_INPUT_IME);
|
| - unloaded_schemas_["inputMethodPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE);
|
| - unloaded_schemas_["management"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_MANAGEMENT);
|
| - unloaded_schemas_["mediaPlayerPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_MEDIAPLAYERPRIVATE);
|
| - unloaded_schemas_["metricsPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_METRICSPRIVATE);
|
| - unloaded_schemas_["offersPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_OFFERSPRIVATE);
|
| - unloaded_schemas_["omnibox"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_OMNIBOX);
|
| - unloaded_schemas_["pageAction"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_PAGEACTION);
|
| - unloaded_schemas_["pageActions"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_PAGEACTIONS);
|
| - unloaded_schemas_["pageCapture"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_PAGECAPTURE);
|
| - unloaded_schemas_["permissions"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_PERMISSIONS);
|
| - unloaded_schemas_["privacy"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_PRIVACY);
|
| - unloaded_schemas_["proxy"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_PROXY);
|
| - unloaded_schemas_["storage"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_STORAGE);
|
| - unloaded_schemas_["systemPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_SYSTEMPRIVATE);
|
| - unloaded_schemas_["tabs"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_TABS);
|
| - unloaded_schemas_["terminalPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_TERMINALPRIVATE);
|
| - unloaded_schemas_["test"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_TEST);
|
| - unloaded_schemas_["topSites"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_TOPSITES);
|
| - unloaded_schemas_["ttsEngine"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_TTSENGINE);
|
| - unloaded_schemas_["tts"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_TTS);
|
| - unloaded_schemas_["types"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_TYPES);
|
| - unloaded_schemas_["webNavigation"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_WEBNAVIGATION);
|
| - unloaded_schemas_["webRequest"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_WEBREQUEST);
|
| - unloaded_schemas_["webSocketProxyPrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_WEBSOCKETPROXYPRIVATE);
|
| - unloaded_schemas_["webstore"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_WEBSTORE);
|
| - unloaded_schemas_["webstorePrivate"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_WEBSTOREPRIVATE);
|
| - unloaded_schemas_["windows"] = ReadFromResource(
|
| - IDR_EXTENSION_API_JSON_WINDOWS);
|
| + 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("chromeAuthPrivate", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_CHROMEAUTHPRIVATE));
|
| + RegisterSchema("chromeosInfoPrivate", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE));
|
| + 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("devtools", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_DEVTOOLS));
|
| + RegisterSchema("experimental.accessibility", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_ACCESSIBILITY));
|
| + RegisterSchema("experimental.alarms", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_ALARMS));
|
| + RegisterSchema("experimental.app", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_APP));
|
| + RegisterSchema("experimental.bookmarkManager", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_BOOKMARKMANAGER));
|
| + RegisterSchema("experimental.declarative", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_DECLARATIVE));
|
| + RegisterSchema("experimental.downloads", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_DOWNLOADS));
|
| + RegisterSchema("experimental.extension", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_EXTENSION));
|
| + RegisterSchema("experimental.fontSettings", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_FONTSSETTINGS));
|
| + RegisterSchema("experimental.identity", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_IDENTITY));
|
| + RegisterSchema("experimental.infobars", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_INFOBARS));
|
| + RegisterSchema("experimental.input.ui", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_UI));
|
| + RegisterSchema("experimental.input.virtualKeyboard", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_VIRTUALKEYBOARD));
|
| + RegisterSchema("experimental.keybinding", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_KEYBINDING));
|
| + RegisterSchema("experimental.managedMode", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_MANAGEDMODE));
|
| + RegisterSchema("experimental.offscreenTabs", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_OFFSCREENTABS));
|
| + RegisterSchema("experimental.processes", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_PROCESSES));
|
| + RegisterSchema("experimental.rlz", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_RLZ));
|
| + RegisterSchema("experimental.serial", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_SERIAL));
|
| + RegisterSchema("experimental.socket", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_SOCKET));
|
| + RegisterSchema("experimental.speechInput", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_SPEECHINPUT));
|
| + RegisterSchema("experimental.webRequest", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXPERIMENTAL_WEBREQUEST));
|
| + RegisterSchema("extension", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_EXTENSION));
|
| + RegisterSchema("fileBrowserHandler", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER));
|
| + RegisterSchema("fileBrowserPrivate", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_FILEBROWSERPRIVATE));
|
| + 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("management", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_MANAGEMENT));
|
| + RegisterSchema("mediaPlayerPrivate", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_MEDIAPLAYERPRIVATE));
|
| + RegisterSchema("metricsPrivate", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_METRICSPRIVATE));
|
| + RegisterSchema("offersPrivate", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_OFFERSPRIVATE));
|
| + 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("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("webNavigation", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_WEBNAVIGATION));
|
| + RegisterSchema("webRequest", ReadFromResource(
|
| + IDR_EXTENSION_API_JSON_WEBREQUEST));
|
| + 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_);
|
| -}
|
|
|
| -ExtensionAPI::~ExtensionAPI() {
|
| + LoadAllSchemas();
|
| }
|
|
|
| -bool ExtensionAPI::IsPrivileged(const std::string& full_name) {
|
| - std::string api_name;
|
| - std::string child_name;
|
| +void ExtensionAPI::RegisterSchema(const std::string& name,
|
| + const base::StringPiece& source) {
|
| + unloaded_schemas_[name] = source;
|
| +}
|
|
|
| - {
|
| - std::vector<std::string> split;
|
| - base::SplitString(full_name, '.', &split);
|
| - std::reverse(split.begin(), split.end());
|
| - CHECK(!split.empty()); // |full_name| was empty or only whitespace.
|
| -
|
| - api_name = split.back();
|
| - split.pop_back();
|
| - if (api_name == "experimental") {
|
| - CHECK(!split.empty()); // |full_name| was "experimental" alone.
|
| - api_name += "." + split.back();
|
| - split.pop_back();
|
| - }
|
| +void ExtensionAPI::RegisterDependencyProvider(const std::string& name,
|
| + FeatureProvider* provider) {
|
| + dependency_providers_[name] = provider;
|
| +}
|
|
|
| - // This only really works properly if split.size() == 1, however:
|
| - // - if it's empty, it's ok to leave child_name empty; presumably there's
|
| - // no functions or events with empty names.
|
| - // - if it's > 1, we can just do our best.
|
| - if (split.size() > 0)
|
| - child_name = split[0];
|
| +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) {
|
| + scoped_ptr<Feature> feature(GetFeatureDependency(full_name));
|
| + CHECK(feature.get()) << *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);
|
| +
|
| // GetSchema to ensure that it gets loaded before any checks.
|
| const DictionaryValue* schema = GetSchema(api_name);
|
| + if (api_name.empty() || !schema)
|
| + return true; // default to privileged for paranoia's sake.
|
| +
|
| + // First try to use the feature system.
|
| + scoped_ptr<Feature> feature(GetFeature(full_name));
|
| + if (feature.get()) {
|
| + // 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) {
|
| + scoped_ptr<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;
|
|
|
| - if (partially_unprivileged_apis_.count(api_name)) {
|
| - return IsChildNamePrivileged(schema, "functions", child_name) &&
|
| - IsChildNamePrivileged(schema, "events", child_name);
|
| - }
|
| + if (partially_unprivileged_apis_.count(api_name))
|
| + return IsChildNamePrivileged(schema, child_name);
|
|
|
| return true;
|
| }
|
|
|
| -DictionaryValue* ExtensionAPI::FindListItem(
|
| - const ListValue* list,
|
| - const std::string& property_name,
|
| - const std::string& property_value) {
|
| - for (size_t i = 0; i < list->GetSize(); ++i) {
|
| - 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;
|
| -}
|
| -
|
| bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node,
|
| - const std::string& child_kind,
|
| const std::string& child_name) {
|
| - ListValue* child_list = NULL;
|
| - name_space_node->GetList(child_kind, &child_list);
|
| - if (!child_list)
|
| - return true;
|
| -
|
| bool unprivileged = false;
|
| - DictionaryValue* child = FindListItem(child_list, "name", child_name);
|
| + 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& api_name) {
|
| - SchemaMap::const_iterator maybe_schema = schemas_.find(api_name);
|
| - if (maybe_schema != schemas_.end())
|
| - return maybe_schema->second.get();
|
| -
|
| - // 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->second);
|
| - maybe_schema = schemas_.find(api_name);
|
| - CHECK(schemas_.end() != maybe_schema);
|
| - return maybe_schema->second.get();
|
| +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->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;
|
| }
|
|
|
| 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();
|
| -
|
| - scoped_ptr<std::set<std::string> > result(new std::set<std::string>());
|
| + 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;
|
| @@ -367,8 +519,8 @@ scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext(
|
| case Feature::BLESSED_EXTENSION_CONTEXT:
|
| // Availability is determined by the permissions of the extension.
|
| CHECK(extension);
|
| - GetAllowedAPIs(extension, result.get());
|
| - ResolveDependencies(result.get());
|
| + GetAllowedAPIs(extension, &temp_result);
|
| + ResolveDependencies(&temp_result);
|
| break;
|
|
|
| case Feature::UNBLESSED_EXTENSION_CONTEXT:
|
| @@ -376,27 +528,119 @@ scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext(
|
| // Same as BLESSED_EXTENSION_CONTEXT, but only those APIs that are
|
| // unprivileged.
|
| CHECK(extension);
|
| - GetAllowedAPIs(extension, result.get());
|
| + GetAllowedAPIs(extension, &temp_result);
|
| // Resolving dependencies before removing unprivileged APIs means that
|
| // some unprivileged APIs may have unrealised dependencies. Too bad!
|
| - ResolveDependencies(result.get());
|
| - RemovePrivilegedAPIs(result.get());
|
| + ResolveDependencies(&temp_result);
|
| + RemovePrivilegedAPIs(&temp_result);
|
| break;
|
|
|
| case Feature::WEB_PAGE_CONTEXT:
|
| // Availablility is determined by the url.
|
| CHECK(url.is_valid());
|
| - GetAPIsMatchingURL(url, result.get());
|
| + 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();
|
| +}
|
| +
|
| +scoped_ptr<Feature> ExtensionAPI::GetFeature(const std::string& full_name) {
|
| + std::string child_name;
|
| + std::string api_namespace = GetAPINameFromFullName(full_name, &child_name);
|
| +
|
| + APIFeatureMap::iterator api_features = features_.find(api_namespace);
|
| + if (api_features == features_.end())
|
| + return scoped_ptr<Feature>(NULL);
|
| +
|
| + scoped_ptr<Feature> result;
|
| + FeatureMap::iterator child_feature = api_features->second->find(child_name);
|
| + if (child_feature != api_features->second->end()) {
|
| + // TODO(aa): Argh, having FeatureProvider return a scoped pointer was a
|
| + // mistake. See: crbug.com/120068.
|
| + result.reset(new Feature(*child_feature->second));
|
| + } else {
|
| + FeatureMap::iterator parent_feature = api_features->second->find("");
|
| + CHECK(parent_feature != api_features->second->end());
|
| + result.reset(new Feature(*parent_feature->second));
|
| + }
|
| +
|
| + if (result->contexts()->empty()) {
|
| + result.reset();
|
| + LOG(ERROR) << "API feature '" << full_name
|
| + << "' must specify at least one context.";
|
| + }
|
| +
|
| return result.Pass();
|
| }
|
|
|
| +scoped_ptr<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;
|
| +
|
| + scoped_ptr<Feature> feature(provider->second->GetFeature(feature_name));
|
| + CHECK(feature.get()) << full_name;
|
| +
|
| + return feature.Pass();
|
| +}
|
| +
|
| +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);
|
| @@ -421,17 +665,27 @@ void ExtensionAPI::GetMissingDependencies(
|
| const std::string& api_name,
|
| const std::set<std::string>& excluding,
|
| std::set<std::string>* out) {
|
| - const DictionaryValue* schema = GetSchema(api_name);
|
| - CHECK(schema) << "Schema for " << api_name << " not found";
|
| + 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";
|
|
|
| ListValue* dependencies = NULL;
|
| if (!schema->GetList("dependencies", &dependencies))
|
| return;
|
|
|
| for (size_t i = 0; i < dependencies->GetSize(); ++i) {
|
| - std::string api_name;
|
| - if (dependencies->GetString(i, &api_name) && !excluding.count(api_name))
|
| - out->insert(api_name);
|
| + std::string dependency_name;
|
| + if (dependencies->GetString(i, &dependency_name) &&
|
| + !excluding.count(dependency_name)) {
|
| + out->insert(dependency_name);
|
| + }
|
| }
|
| }
|
|
|
| @@ -454,6 +708,11 @@ 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);
|
| }
|
|
|