diff options
author | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-23 10:02:15 +0000 |
---|---|---|
committer | pfeldman@chromium.org <pfeldman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-23 10:02:15 +0000 |
commit | 096bf9b475c2d59341fb427398a8b5ab0cfb0005 (patch) | |
tree | 366ce94d1810783c4990fe826c11a3e527677895 /chrome/common/extensions | |
parent | cc7dd06075650443b0fe42609e6fd6855caf6764 (diff) | |
download | chromium_src-096bf9b475c2d59341fb427398a8b5ab0cfb0005.zip chromium_src-096bf9b475c2d59341fb427398a8b5ab0cfb0005.tar.gz chromium_src-096bf9b475c2d59341fb427398a8b5ab0cfb0005.tar.bz2 |
Revert r128434 "Make ExtensionAPI load schemas lazily where possible." for breaking install tests.
TBR=kalman
Review URL: https://chromiumcodereview.appspot.com/9845007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@128448 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/extensions')
-rw-r--r-- | chrome/common/extensions/api/extension_api.cc | 380 | ||||
-rw-r--r-- | chrome/common/extensions/api/extension_api.h | 46 | ||||
-rw-r--r-- | chrome/common/extensions/api/extension_api_unittest.cc | 104 |
3 files changed, 211 insertions, 319 deletions
diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc index d6612d9..91b785b 100644 --- a/chrome/common/extensions/api/extension_api.cc +++ b/chrome/common/extensions/api/extension_api.cc @@ -20,14 +20,8 @@ #include "grit/common_resources.h" #include "ui/base/resource/resource_bundle.h" -using base::DictionaryValue; -using base::ListValue; -using base::Value; - namespace extensions { -using api::GeneratedSchemas; - namespace { // Returns whether the list at |name_space_node|.|child_kind| contains any @@ -50,213 +44,163 @@ bool HasUnprivilegedChild(const DictionaryValue* name_space_node, return false; } -base::StringPiece ReadFromResource(int resource_id) { - return ResourceBundle::GetSharedInstance().GetRawDataResource(resource_id); +} // namespace + +// static +ExtensionAPI* ExtensionAPI::GetInstance() { + return Singleton<ExtensionAPI>::get(); } -scoped_ptr<ListValue> LoadSchemaList(const base::StringPiece& schema) { +static base::ListValue* LoadSchemaList(int resource_id) { + const bool kAllowTrailingCommas = false; std::string error_message; - scoped_ptr<Value> result( + Value* result = base::JSONReader::ReadAndReturnError( - schema.as_string(), - false, // allow trailing commas + ResourceBundle::GetSharedInstance().GetRawDataResource( + resource_id).as_string(), + kAllowTrailingCommas, NULL, // error code - &error_message)); - CHECK(result.get()) << error_message; - CHECK(result->IsType(Value::TYPE_LIST)); - return scoped_ptr<ListValue>(static_cast<ListValue*>(result.release())); + &error_message); + CHECK(result) << error_message; + CHECK(result->IsType(base::Value::TYPE_LIST)); + return static_cast<base::ListValue*>(result); } -} // namespace - -// static -ExtensionAPI* ExtensionAPI::GetInstance() { - return Singleton<ExtensionAPI>::get(); +void ExtensionAPI::LoadSchemaFromResource(int resource_id) { + RegisterSchema(LoadSchemaList(resource_id)); } -void ExtensionAPI::LoadSchema(const base::StringPiece& schema) { - scoped_ptr<ListValue> schema_list(LoadSchemaList(schema)); +void ExtensionAPI::RegisterSchema(base::ListValue* loaded_schema) { + // We take ownership of loaded_schema, so we need to delete it. + scoped_ptr<base::ListValue> scoped_loaded_schema(loaded_schema); + Value* value = NULL; std::string schema_namespace; + while (!loaded_schema->empty()) { + loaded_schema->Remove(loaded_schema->GetSize() - 1, &value); + CHECK(value->IsType(Value::TYPE_DICTIONARY)); + const DictionaryValue* schema = static_cast<const DictionaryValue*>(value); + CHECK(schema->GetString("namespace", &schema_namespace)); + schemas_[schema_namespace] = linked_ptr<const DictionaryValue>(schema); + } +} - while (!schema_list->empty()) { - const DictionaryValue* schema = NULL; - { - Value* value = NULL; - schema_list->Remove(schema_list->GetSize() - 1, &value); - CHECK(value->IsType(Value::TYPE_DICTIONARY)); - schema = static_cast<const DictionaryValue*>(value); +ExtensionAPI::ExtensionAPI() { + static int kJsonApiResourceIds[] = { + IDR_EXTENSION_API_JSON_APP, + IDR_EXTENSION_API_JSON_BOOKMARKS, + IDR_EXTENSION_API_JSON_BROWSERACTION, + IDR_EXTENSION_API_JSON_BROWSING_DATA, + IDR_EXTENSION_API_JSON_CHROMEAUTHPRIVATE, + IDR_EXTENSION_API_JSON_CHROMEOSINFOPRIVATE, + IDR_EXTENSION_API_JSON_CHROMEPRIVATE, + IDR_EXTENSION_API_JSON_CONTENTSETTINGS, + IDR_EXTENSION_API_JSON_CONTEXTMENUS, + IDR_EXTENSION_API_JSON_COOKIES, + IDR_EXTENSION_API_JSON_DEBUGGER, + IDR_EXTENSION_API_JSON_DEVTOOLS, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_ACCESSIBILITY, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_APP, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_BOOKMARKMANAGER, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_DECLARATIVE, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_DOWNLOADS, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_EXTENSIONS, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_FONTS, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_INFOBARS, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_UI, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_INPUT_VIRTUALKEYBOARD, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_KEYBINDING, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_MANAGED_MODE, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_OFFSCREENTABS, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_PROCESSES, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_RLZ, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_SERIAL, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_SOCKET, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_SPEECHINPUT, + IDR_EXTENSION_API_JSON_EXPERIMENTAL_WEBREQUEST, + IDR_EXTENSION_API_JSON_EXTENSION, + IDR_EXTENSION_API_JSON_FILEBROWSERHANDLER, + IDR_EXTENSION_API_JSON_FILEBROWSERPRIVATE, + IDR_EXTENSION_API_JSON_HISTORY, + IDR_EXTENSION_API_JSON_I18N, + IDR_EXTENSION_API_JSON_IDLE, + IDR_EXTENSION_API_JSON_INPUT_IME, + IDR_EXTENSION_API_JSON_INPUTMETHODPRIVATE, + IDR_EXTENSION_API_JSON_MANAGEMENT, + IDR_EXTENSION_API_JSON_MEDIAPLAYERPRIVATE, + IDR_EXTENSION_API_JSON_METRICSPRIVATE, + IDR_EXTENSION_API_JSON_OFFERSPRIVATE, + IDR_EXTENSION_API_JSON_OMNIBOX, + IDR_EXTENSION_API_JSON_PAGEACTION, + IDR_EXTENSION_API_JSON_PAGEACTIONS, + IDR_EXTENSION_API_JSON_PAGECAPTURE, + IDR_EXTENSION_API_JSON_PERMISSIONS, + IDR_EXTENSION_API_JSON_PRIVACY, + IDR_EXTENSION_API_JSON_PROXY, + IDR_EXTENSION_API_JSON_STORAGE, + IDR_EXTENSION_API_JSON_SYSTEMPRIVATE, + IDR_EXTENSION_API_JSON_TABS, + IDR_EXTENSION_API_JSON_TERMINALPRIVATE, + IDR_EXTENSION_API_JSON_TEST, + IDR_EXTENSION_API_JSON_TOPSITES, + IDR_EXTENSION_API_JSON_TTS, + IDR_EXTENSION_API_JSON_TTSENGINE, + IDR_EXTENSION_API_JSON_TYPES, + IDR_EXTENSION_API_JSON_WEBNAVIGATION, + IDR_EXTENSION_API_JSON_WEBREQUEST, + IDR_EXTENSION_API_JSON_WEBSOCKETPROXYPRIVATE, + IDR_EXTENSION_API_JSON_WEBSTORE, + IDR_EXTENSION_API_JSON_WEBSTOREPRIVATE, + IDR_EXTENSION_API_JSON_WINDOWS, + }; + + for (size_t i = 0; i < arraysize(kJsonApiResourceIds); i++) { + LoadSchemaFromResource(kJsonApiResourceIds[i]); + } + RegisterSchema(api::GeneratedSchemas::Get()); + + // Populate {completely,partially}_unprivileged_apis_. + for (SchemaMap::iterator it = schemas_.begin(); it != schemas_.end(); ++it) { + bool unprivileged = false; + it->second->GetBoolean("unprivileged", &unprivileged); + if (unprivileged) { + completely_unprivileged_apis_.insert(it->first); + continue; } - CHECK(schema->GetString("namespace", &schema_namespace)); - schemas_[schema_namespace] = make_linked_ptr(schema); - unloaded_schemas_.erase(schema_namespace); - - // Populate |{completely,partially}_unprivileged_apis_|. - // - // For "partially", only need to look at functions/events; even though - // there are unprivileged properties (e.g. in extensions), access to those - // never reaches C++ land. - if (schema->HasKey("unprivileged")) { - completely_unprivileged_apis_.insert(schema_namespace); - } else if (HasUnprivilegedChild(schema, "functions") || - HasUnprivilegedChild(schema, "events")) { - partially_unprivileged_apis_.insert(schema_namespace); + // Only need to look at functions/events; even though there are unprivileged + // properties (e.g. in extensions), access to those never reaches C++ land. + if (HasUnprivilegedChild(it->second.get(), "functions") || + HasUnprivilegedChild(it->second.get(), "events")) { + partially_unprivileged_apis_.insert(it->first); } + } - // Populate |url_matching_apis_|. + // Populate |url_matching_apis_|. + for (SchemaMap::const_iterator it = schemas_.begin(); + it != schemas_.end(); ++it) { ListValue* matches = NULL; - if (schema->GetList("matches", &matches)) { - URLPatternSet pattern_set; - for (size_t i = 0; i < matches->GetSize(); ++i) { - std::string pattern; - CHECK(matches->GetString(i, &pattern)); - pattern_set.AddPattern( - URLPattern(UserScript::kValidUserScriptSchemes, pattern)); - } - url_matching_apis_[schema_namespace] = pattern_set; + { + Value* matches_value = NULL; + if (!it->second->Get("matches", &matches_value)) + continue; + CHECK_EQ(Value::TYPE_LIST, matches_value->GetType()); + matches = static_cast<ListValue*>(matches_value); + } + URLPatternSet pattern_set; + for (size_t i = 0; i < matches->GetSize(); ++i) { + std::string pattern; + CHECK(matches->GetString(i, &pattern)); + pattern_set.AddPattern( + URLPattern(UserScript::kValidUserScriptSchemes, pattern)); } + url_matching_apis_[it->first] = pattern_set; } } -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_["chromePrivate"] = ReadFromResource( - IDR_EXTENSION_API_JSON_CHROMEPRIVATE); - 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.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.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_["webstorePrivate"] = ReadFromResource( - IDR_EXTENSION_API_JSON_WEBSTOREPRIVATE); - unloaded_schemas_["windows"] = ReadFromResource( - IDR_EXTENSION_API_JSON_WINDOWS); - - // Schemas to be loaded via JSON generated from IDL files. - GeneratedSchemas::Get(&unloaded_schemas_); -} - ExtensionAPI::~ExtensionAPI() { } -bool ExtensionAPI::IsPrivileged(const std::string& full_name) { +bool ExtensionAPI::IsPrivileged(const std::string& full_name) const { std::string api_name; std::string child_name; @@ -280,13 +224,11 @@ bool ExtensionAPI::IsPrivileged(const std::string& full_name) { child_name = split[0]; } - // GetSchema to ensure that it gets loaded before any checks. - const DictionaryValue* schema = GetSchema(api_name); - if (completely_unprivileged_apis_.count(api_name)) return false; if (partially_unprivileged_apis_.count(api_name)) { + const DictionaryValue* schema = GetSchema(api_name); return IsChildNamePrivileged(schema, "functions", child_name) && IsChildNamePrivileged(schema, "events", child_name); } @@ -295,9 +237,9 @@ bool ExtensionAPI::IsPrivileged(const std::string& full_name) { } DictionaryValue* ExtensionAPI::FindListItem( - const ListValue* list, + const base::ListValue* list, const std::string& property_name, - const std::string& property_value) { + const std::string& property_value) const { for (size_t i = 0; i < list->GetSize(); ++i) { DictionaryValue* item = NULL; CHECK(list->GetDictionary(i, &item)) @@ -312,7 +254,7 @@ DictionaryValue* ExtensionAPI::FindListItem( bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node, const std::string& child_kind, - const std::string& child_name) { + const std::string& child_name) const { ListValue* child_list = NULL; name_space_node->GetList(child_kind, &child_list); if (!child_list) @@ -326,32 +268,16 @@ bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node, return !unprivileged; } -const DictionaryValue* ExtensionAPI::GetSchema(const std::string& api_name) { +const base::DictionaryValue* ExtensionAPI::GetSchema( + const std::string& api_name) const { 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(); + return maybe_schema != schemas_.end() ? maybe_schema->second.get() : NULL; } 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(); - + Feature::Context context, + const Extension* extension, + const GURL& url) const { scoped_ptr<std::set<std::string> > result(new std::set<std::string>()); switch (context) { @@ -388,7 +314,7 @@ scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext( } void ExtensionAPI::GetAllowedAPIs( - const Extension* extension, std::set<std::string>* out) { + const Extension* extension, std::set<std::string>* out) const { for (SchemaMap::const_iterator i = schemas_.begin(); i != schemas_.end(); ++i) { if (extension->required_permission_set()->HasAnyAccessToAPI(i->first) || @@ -398,7 +324,7 @@ void ExtensionAPI::GetAllowedAPIs( } } -void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) { +void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) const { std::set<std::string> missing_dependencies; for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i) GetMissingDependencies(*i, *out, &missing_dependencies); @@ -414,8 +340,8 @@ void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) { 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); + std::set<std::string>* out) const { + const base::DictionaryValue* schema = GetSchema(api_name); CHECK(schema) << "Schema for " << api_name << " not found"; ListValue* dependencies = NULL; @@ -429,7 +355,7 @@ void ExtensionAPI::GetMissingDependencies( } } -void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) { +void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) const { std::set<std::string> privileged_apis; for (std::set<std::string>::iterator i = apis->begin(); i != apis->end(); ++i) { @@ -445,7 +371,7 @@ void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) { } void ExtensionAPI::GetAPIsMatchingURL(const GURL& url, - std::set<std::string>* out) { + std::set<std::string>* out) const { for (std::map<std::string, URLPatternSet>::const_iterator i = url_matching_apis_.begin(); i != url_matching_apis_.end(); ++i) { if (i->second.MatchesURL(url)) @@ -453,10 +379,4 @@ void ExtensionAPI::GetAPIsMatchingURL(const GURL& url, } } -void ExtensionAPI::LoadAllSchemas() { - while (unloaded_schemas_.size()) { - LoadSchema(unloaded_schemas_.begin()->second); - } -} - } // namespace extensions diff --git a/chrome/common/extensions/api/extension_api.h b/chrome/common/extensions/api/extension_api.h index 1ae7824..4907152 100644 --- a/chrome/common/extensions/api/extension_api.h +++ b/chrome/common/extensions/api/extension_api.h @@ -14,7 +14,6 @@ #include "base/memory/linked_ptr.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" -#include "base/string_piece.h" #include "base/values.h" #include "chrome/common/extensions/feature.h" #include "chrome/common/extensions/url_pattern_set.h" @@ -37,77 +36,76 @@ class ExtensionAPI { // Returns the single instance of this class. static ExtensionAPI* GetInstance(); - // Public for construction from unit tests. Use GetInstance() normally. - ExtensionAPI(); - ~ExtensionAPI(); - // Returns true if |name| is a privileged API path. Privileged paths can only // be called from extension code which is running in its own designated // extension process. They cannot be called from extension code running in // content scripts, or other low-privileged contexts. - bool IsPrivileged(const std::string& name); + bool IsPrivileged(const std::string& name) const; // Gets the schema for the extension API with namespace |api_name|. // Ownership remains with this object. - const base::DictionaryValue* GetSchema(const std::string& api_name); + const base::DictionaryValue* GetSchema(const std::string& api_name) const; // Gets the APIs available to |context| given an |extension| and |url|. The // extension or URL may not be relevant to all contexts, and may be left // NULL/empty. scoped_ptr<std::set<std::string> > GetAPIsForContext( - Feature::Context context, const Extension* extension, const GURL& url); + Feature::Context context, + const Extension* extension, + const GURL& url) const; private: friend struct DefaultSingletonTraits<ExtensionAPI>; - // Loads a schema. - void LoadSchema(const base::StringPiece& schema); + ExtensionAPI(); + ~ExtensionAPI(); + + // Loads a schema from a resource. + void LoadSchemaFromResource(int resource_id); + + // Given a schema in ListValue form, registers it in a map. Takes ownership + // of |loaded_schema|. + void RegisterSchema(base::ListValue* loaded_schema); // Find an item in |list| with the specified property name and value, or NULL // if no such item exists. base::DictionaryValue* FindListItem(const base::ListValue* list, const std::string& property_name, - const std::string& property_value); + const std::string& property_value) const; // Returns true if the function or event under |namespace_node| with // the specified |child_name| is privileged, or false otherwise. If the name // is not found, defaults to privileged. bool IsChildNamePrivileged(const base::DictionaryValue* namespace_node, const std::string& child_kind, - const std::string& child_name); + const std::string& child_name) const; // Adds all APIs to |out| that |extension| has any permission (required or // optional) to use. - void GetAllowedAPIs(const Extension* extension, std::set<std::string>* out); + void GetAllowedAPIs( + const Extension* extension, std::set<std::string>* out) const; // Adds dependent schemas to |out| as determined by the "dependencies" // property. - void ResolveDependencies(std::set<std::string>* out); + void ResolveDependencies(std::set<std::string>* out) const; // Adds any APIs listed in "dependencies" found in the schema for |api_name| // but not in |excluding| to |out|. void GetMissingDependencies( const std::string& api_name, const std::set<std::string>& excluding, - std::set<std::string>* out); + std::set<std::string>* out) const; // Removes all APIs from |apis| which are *entirely* privileged. This won't // include APIs such as "storage" which is entirely unprivileged, nor // "extension" which has unprivileged components. - void RemovePrivilegedAPIs(std::set<std::string>* apis); + void RemovePrivilegedAPIs(std::set<std::string>* apis) const; // Adds an APIs that match |url| to |out|. - void GetAPIsMatchingURL(const GURL& url, std::set<std::string>* out); - - // Loads all remaining resources from |unloaded_schemas_|. - void LoadAllSchemas(); + void GetAPIsMatchingURL(const GURL& url, std::set<std::string>* out) const; static ExtensionAPI* instance_; - // Map from each API that hasn't been loaded yet to the schema which defines - // it. Note that there may be multiple APIs per schema. - std::map<std::string, base::StringPiece> unloaded_schemas_; - // Schemas for each namespace. typedef std::map<std::string, linked_ptr<const DictionaryValue> > SchemaMap; SchemaMap schemas_; diff --git a/chrome/common/extensions/api/extension_api_unittest.cc b/chrome/common/extensions/api/extension_api_unittest.cc index d6e324f..1bcf33b 100644 --- a/chrome/common/extensions/api/extension_api_unittest.cc +++ b/chrome/common/extensions/api/extension_api_unittest.cc @@ -13,57 +13,36 @@ #include "chrome/common/extensions/extension.h" #include "testing/gtest/include/gtest/gtest.h" -namespace extensions { namespace { -TEST(ExtensionAPI, IsPrivileged) { - ExtensionAPI extension_api; +using extensions::ExtensionAPI; +using extensions::Feature; - EXPECT_FALSE(extension_api.IsPrivileged("extension.connect")); - EXPECT_FALSE(extension_api.IsPrivileged("extension.onConnect")); +TEST(ExtensionAPI, IsPrivileged) { + ExtensionAPI* extension_api = ExtensionAPI::GetInstance(); + EXPECT_FALSE(extension_api->IsPrivileged("extension.connect")); + EXPECT_FALSE(extension_api->IsPrivileged("extension.onConnect")); // Properties are not supported yet. - EXPECT_TRUE(extension_api.IsPrivileged("extension.lastError")); + EXPECT_TRUE(extension_api->IsPrivileged("extension.lastError")); // Default unknown names to privileged for paranoia's sake. - EXPECT_TRUE(extension_api.IsPrivileged("")); - EXPECT_TRUE(extension_api.IsPrivileged("<unknown-namespace>")); - EXPECT_TRUE(extension_api.IsPrivileged("extension.<unknown-member>")); + EXPECT_TRUE(extension_api->IsPrivileged("")); + EXPECT_TRUE(extension_api->IsPrivileged("<unknown-namespace>")); + EXPECT_TRUE(extension_api->IsPrivileged("extension.<unknown-member>")); // Exists, but privileged. - EXPECT_TRUE(extension_api.IsPrivileged("extension.getViews")); - EXPECT_TRUE(extension_api.IsPrivileged("history.search")); + EXPECT_TRUE(extension_api->IsPrivileged("extension.getViews")); + EXPECT_TRUE(extension_api->IsPrivileged("history.search")); // Whole APIs that are unprivileged. - EXPECT_FALSE(extension_api.IsPrivileged("app.getDetails")); - EXPECT_FALSE(extension_api.IsPrivileged("app.isInstalled")); - EXPECT_FALSE(extension_api.IsPrivileged("storage.local")); - EXPECT_FALSE(extension_api.IsPrivileged("storage.local.onChanged")); - EXPECT_FALSE(extension_api.IsPrivileged("storage.local.set")); - EXPECT_FALSE(extension_api.IsPrivileged("storage.local.MAX_ITEMS")); - EXPECT_FALSE(extension_api.IsPrivileged("storage.set")); -} - -TEST(ExtensionAPI, LazyGetSchema) { - ExtensionAPI apis; - - EXPECT_EQ(NULL, apis.GetSchema("")); - EXPECT_EQ(NULL, apis.GetSchema("")); - EXPECT_EQ(NULL, apis.GetSchema("experimental")); - EXPECT_EQ(NULL, apis.GetSchema("experimental")); - EXPECT_EQ(NULL, apis.GetSchema("foo")); - EXPECT_EQ(NULL, apis.GetSchema("foo")); - - EXPECT_TRUE(apis.GetSchema("experimental.dns")); - EXPECT_TRUE(apis.GetSchema("experimental.dns")); - EXPECT_TRUE(apis.GetSchema("experimental.infobars")); - EXPECT_TRUE(apis.GetSchema("experimental.infobars")); - EXPECT_TRUE(apis.GetSchema("extension")); - EXPECT_TRUE(apis.GetSchema("extension")); - EXPECT_TRUE(apis.GetSchema("omnibox")); - EXPECT_TRUE(apis.GetSchema("omnibox")); - EXPECT_TRUE(apis.GetSchema("storage")); - EXPECT_TRUE(apis.GetSchema("storage")); + EXPECT_FALSE(extension_api->IsPrivileged("app.getDetails")); + EXPECT_FALSE(extension_api->IsPrivileged("app.isInstalled")); + EXPECT_FALSE(extension_api->IsPrivileged("storage.local")); + EXPECT_FALSE(extension_api->IsPrivileged("storage.local.onChanged")); + EXPECT_FALSE(extension_api->IsPrivileged("storage.local.set")); + EXPECT_FALSE(extension_api->IsPrivileged("storage.local.MAX_ITEMS")); + EXPECT_FALSE(extension_api->IsPrivileged("storage.set")); } scoped_refptr<Extension> CreateExtensionWithPermissions( @@ -105,18 +84,16 @@ TEST(ExtensionAPI, ExtensionWithUnprivilegedAPIs) { extension = CreateExtensionWithPermissions(permissions); } - ExtensionAPI extension_api; - scoped_ptr<std::set<std::string> > privileged_apis = - extension_api.GetAPIsForContext( + ExtensionAPI::GetInstance()->GetAPIsForContext( Feature::BLESSED_EXTENSION_CONTEXT, extension.get(), GURL()); scoped_ptr<std::set<std::string> > unprivileged_apis = - extension_api.GetAPIsForContext( + ExtensionAPI::GetInstance()->GetAPIsForContext( Feature::UNBLESSED_EXTENSION_CONTEXT, extension.get(), GURL()); scoped_ptr<std::set<std::string> > content_script_apis = - extension_api.GetAPIsForContext( + ExtensionAPI::GetInstance()->GetAPIsForContext( Feature::CONTENT_SCRIPT_CONTEXT, extension.get(), GURL()); // "storage" is completely unprivileged. @@ -142,7 +119,7 @@ TEST(ExtensionAPI, ExtensionWithDependencies) { scoped_refptr<Extension> extension = CreateExtensionWithPermission("ttsEngine"); scoped_ptr<std::set<std::string> > apis = - ExtensionAPI().GetAPIsForContext( + ExtensionAPI::GetInstance()->GetAPIsForContext( Feature::BLESSED_EXTENSION_CONTEXT, extension.get(), GURL()); EXPECT_EQ(1u, apis->count("ttsEngine")); EXPECT_EQ(1u, apis->count("tts")); @@ -154,42 +131,39 @@ TEST(ExtensionAPI, ExtensionWithDependencies) { scoped_refptr<Extension> extension = CreateExtensionWithPermission("tts"); scoped_ptr<std::set<std::string> > apis = - ExtensionAPI().GetAPIsForContext( + ExtensionAPI::GetInstance()->GetAPIsForContext( Feature::BLESSED_EXTENSION_CONTEXT, extension.get(), GURL()); EXPECT_EQ(0u, apis->count("ttsEngine")); EXPECT_EQ(1u, apis->count("tts")); } } -bool MatchesURL( - ExtensionAPI* api, const std::string& api_name, const std::string& url) { +bool MatchesURL(const std::string& api_name, const std::string& url) { scoped_ptr<std::set<std::string> > apis = - api->GetAPIsForContext(Feature::WEB_PAGE_CONTEXT, NULL, GURL(url)); + ExtensionAPI::GetInstance()->GetAPIsForContext( + Feature::WEB_PAGE_CONTEXT, NULL, GURL(url)); return apis->count(api_name); } TEST(ExtensionAPI, URLMatching) { - ExtensionAPI api; - // "app" API is available to all URLs that content scripts can be injected. - EXPECT_TRUE(MatchesURL(&api, "app", "http://example.com/example.html")); - EXPECT_TRUE(MatchesURL(&api, "app", "https://blah.net")); - EXPECT_TRUE(MatchesURL(&api, "app", "file://somefile.html")); + EXPECT_TRUE(MatchesURL("app", "http://example.com/example.html")); + EXPECT_TRUE(MatchesURL("app", "https://blah.net")); + EXPECT_TRUE(MatchesURL("app", "file://somefile.html")); // But not internal URLs (for chrome-extension:// the app API is injected by // GetSchemasForExtension). - EXPECT_FALSE(MatchesURL(&api, "app", "about:flags")); - EXPECT_FALSE(MatchesURL(&api, "app", "chrome://flags")); - EXPECT_FALSE(MatchesURL(&api, "app", "chrome-extension://fakeextension")); + EXPECT_FALSE(MatchesURL("app", "about:flags")); + EXPECT_FALSE(MatchesURL("app", "chrome://flags")); + EXPECT_FALSE(MatchesURL("app", "chrome-extension://fakeextension")); // "storage" API (for example) isn't available to any URLs. - EXPECT_FALSE(MatchesURL(&api, "storage", "http://example.com/example.html")); - EXPECT_FALSE(MatchesURL(&api, "storage", "https://blah.net")); - EXPECT_FALSE(MatchesURL(&api, "storage", "file://somefile.html")); - EXPECT_FALSE(MatchesURL(&api, "storage", "about:flags")); - EXPECT_FALSE(MatchesURL(&api, "storage", "chrome://flags")); - EXPECT_FALSE(MatchesURL(&api, "storage", "chrome-extension://fakeextension")); + EXPECT_FALSE(MatchesURL("storage", "http://example.com/example.html")); + EXPECT_FALSE(MatchesURL("storage", "https://blah.net")); + EXPECT_FALSE(MatchesURL("storage", "file://somefile.html")); + EXPECT_FALSE(MatchesURL("storage", "about:flags")); + EXPECT_FALSE(MatchesURL("storage", "chrome://flags")); + EXPECT_FALSE(MatchesURL("storage", "chrome-extension://fakeextension")); } } // namespace -} // namespace extensions |