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); |
} |