diff options
Diffstat (limited to 'chrome/common/extensions/api/extension_api.cc')
-rw-r--r-- | chrome/common/extensions/api/extension_api.cc | 692 |
1 files changed, 479 insertions, 213 deletions
diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc index 3178975..029e58c6 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() { + 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); } |