Chromium Code Reviews| 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..029e58c66bb29be889eb4185700ca06941d5ac25 100644 |
| --- a/chrome/common/extensions/api/extension_api.cc |
| +++ b/chrome/common/extensions/api/extension_api.cc |
| @@ -9,13 +9,18 @@ |
| #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 "content/public/browser/browser_thread.h" |
| #include "googleurl/src/gurl.h" |
| #include "grit/common_resources.h" |
| #include "ui/base/resource/resource_bundle.h" |
| @@ -23,6 +28,7 @@ |
| using base::DictionaryValue; |
| using base::ListValue; |
| using base::Value; |
| +using content::BrowserThread; |
| namespace extensions { |
| @@ -30,6 +36,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 +78,74 @@ 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(ExtensionAPI::CreateWithDefaultConfiguration()) { |
| + } |
| + scoped_ptr<ExtensionAPI> api; |
| +}; |
| + |
| +base::LazyInstance<Static> g_lazy_instance = LAZY_INSTANCE_INITIALIZER; |
| + |
| } // namespace |
| // static |
| -ExtensionAPI* ExtensionAPI::GetInstance() { |
| - return Singleton<ExtensionAPI>::get(); |
| +ExtensionAPI* ExtensionAPI::GetSharedInstance() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + 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 base::StringPiece& schema) { |
| @@ -115,238 +189,309 @@ 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() { |
| - // 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); |
| + RegisterDependencyProvider("api", this); |
| - // Schemas to be loaded via JSON generated from IDL files. |
| - GeneratedSchemas::Get(&unloaded_schemas_); |
| + // TODO(aa): Can remove this when all JSON files are converted. |
| + RegisterDependencyProvider("", this); |
| } |
| ExtensionAPI::~ExtensionAPI() { |
| } |
| -bool ExtensionAPI::IsPrivileged(const std::string& full_name) { |
| - std::string api_name; |
| - std::string child_name; |
| +void ExtensionAPI::InitDefaultConfiguration() { |
|
Matt Perry
2012/04/04 18:38:47
This method is called when you create a new Extens
Aaron Boodman
2012/04/04 18:41:27
It doesn't actually do any reading. ReadFromResour
|
| + RegisterDependencyProvider( |
| + "manifest", SimpleFeatureProvider::GetManifestFeatures()); |
| + RegisterDependencyProvider( |
| + "permission", SimpleFeatureProvider::GetPermissionFeatures()); |
| - { |
| - 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(); |
| - } |
| + // 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("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)); |
| - // 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]; |
| - } |
| + // Schemas to be loaded via JSON generated from IDL files. |
| + GeneratedSchemas::Get(&unloaded_schemas_); |
| +} |
| - // GetSchema to ensure that it gets loaded before any checks. |
| - const DictionaryValue* schema = GetSchema(api_name); |
| +void ExtensionAPI::RegisterSchema(const std::string& name, |
| + const base::StringPiece& source) { |
| + unloaded_schemas_[name] = source; |
| +} |
| - if (completely_unprivileged_apis_.count(api_name)) |
| - return false; |
| +void ExtensionAPI::RegisterDependencyProvider(const std::string& name, |
| + FeatureProvider* provider) { |
| + dependency_providers_[name] = provider; |
| +} |
| - if (partially_unprivileged_apis_.count(api_name)) { |
| - return IsChildNamePrivileged(schema, "functions", child_name) && |
| - IsChildNamePrivileged(schema, "events", child_name); |
| +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; |
| } |
| -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; |
| +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. |
| + 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; |
| } |
| - return NULL; |
| + // 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_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( |
| @@ -358,8 +503,19 @@ scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext( |
| // 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 +523,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 +532,122 @@ 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) { |
| + // Ensure it's loaded. |
| + GetSchema(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 +672,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 +715,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); |
| } |