summaryrefslogtreecommitdiffstats
path: root/chrome/common/extensions
diff options
context:
space:
mode:
authorkalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-23 12:57:12 +0000
committerkalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-23 12:57:12 +0000
commit4dffaf29261a76ac6a560facc33d0bbe951629d9 (patch)
tree098cd9c4f1f0c43b9e46799748c52adb0210458d /chrome/common/extensions
parent9ec39638198bf85a5f19e74c81a0feb4326d5e73 (diff)
downloadchromium_src-4dffaf29261a76ac6a560facc33d0bbe951629d9.zip
chromium_src-4dffaf29261a76ac6a560facc33d0bbe951629d9.tar.gz
chromium_src-4dffaf29261a76ac6a560facc33d0bbe951629d9.tar.bz2
Make ExtensionAPI load schemas lazily where possible.
BUG=118144 TEST=unit_tests --gtest_filter=ExtensionAPI.* Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=128434 Review URL: http://codereview.chromium.org/9677069 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@128466 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/extensions')
-rw-r--r--chrome/common/extensions/api/extension_api.cc382
-rw-r--r--chrome/common/extensions/api/extension_api.h46
-rw-r--r--chrome/common/extensions/api/extension_api_unittest.cc104
3 files changed, 321 insertions, 211 deletions
diff --git a/chrome/common/extensions/api/extension_api.cc b/chrome/common/extensions/api/extension_api.cc
index 91b785b..8571417 100644
--- a/chrome/common/extensions/api/extension_api.cc
+++ b/chrome/common/extensions/api/extension_api.cc
@@ -20,8 +20,14 @@
#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
@@ -44,163 +50,215 @@ bool HasUnprivilegedChild(const DictionaryValue* name_space_node,
return false;
}
-} // namespace
-
-// static
-ExtensionAPI* ExtensionAPI::GetInstance() {
- return Singleton<ExtensionAPI>::get();
+base::StringPiece ReadFromResource(int resource_id) {
+ return ResourceBundle::GetSharedInstance().GetRawDataResource(resource_id);
}
-static base::ListValue* LoadSchemaList(int resource_id) {
- const bool kAllowTrailingCommas = false;
+scoped_ptr<ListValue> LoadSchemaList(const base::StringPiece& schema) {
std::string error_message;
- Value* result =
+ scoped_ptr<Value> result(
base::JSONReader::ReadAndReturnError(
- ResourceBundle::GetSharedInstance().GetRawDataResource(
- resource_id).as_string(),
- kAllowTrailingCommas,
+ schema.as_string(),
+ false, // allow trailing commas
NULL, // error code
- &error_message);
- CHECK(result) << error_message;
- CHECK(result->IsType(base::Value::TYPE_LIST));
- return static_cast<base::ListValue*>(result);
+ &error_message));
+ CHECK(result.get()) << error_message;
+ CHECK(result->IsType(Value::TYPE_LIST));
+ return scoped_ptr<ListValue>(static_cast<ListValue*>(result.release()));
}
-void ExtensionAPI::LoadSchemaFromResource(int resource_id) {
- RegisterSchema(LoadSchemaList(resource_id));
-}
+} // namespace
-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);
- }
+// static
+ExtensionAPI* ExtensionAPI::GetInstance() {
+ return Singleton<ExtensionAPI>::get();
}
-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());
+void ExtensionAPI::LoadSchema(const base::StringPiece& schema) {
+ scoped_ptr<ListValue> schema_list(LoadSchemaList(schema));
+ std::string schema_namespace;
- // 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;
+ 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);
}
- // 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);
+ 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);
}
- }
- // Populate |url_matching_apis_|.
- for (SchemaMap::const_iterator it = schemas_.begin();
- it != schemas_.end(); ++it) {
+ // Populate |url_matching_apis_|.
ListValue* matches = NULL;
- {
- 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));
+ 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;
}
- 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_["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);
+
+ // Schemas to be loaded via JSON generated from IDL files.
+ GeneratedSchemas::Get(&unloaded_schemas_);
+}
+
ExtensionAPI::~ExtensionAPI() {
}
-bool ExtensionAPI::IsPrivileged(const std::string& full_name) const {
+bool ExtensionAPI::IsPrivileged(const std::string& full_name) {
std::string api_name;
std::string child_name;
@@ -224,11 +282,13 @@ bool ExtensionAPI::IsPrivileged(const std::string& full_name) const {
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);
}
@@ -237,9 +297,9 @@ bool ExtensionAPI::IsPrivileged(const std::string& full_name) const {
}
DictionaryValue* ExtensionAPI::FindListItem(
- const base::ListValue* list,
+ const ListValue* list,
const std::string& property_name,
- const std::string& property_value) const {
+ const std::string& property_value) {
for (size_t i = 0; i < list->GetSize(); ++i) {
DictionaryValue* item = NULL;
CHECK(list->GetDictionary(i, &item))
@@ -254,7 +314,7 @@ DictionaryValue* ExtensionAPI::FindListItem(
bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node,
const std::string& child_kind,
- const std::string& child_name) const {
+ const std::string& child_name) {
ListValue* child_list = NULL;
name_space_node->GetList(child_kind, &child_list);
if (!child_list)
@@ -268,16 +328,32 @@ bool ExtensionAPI::IsChildNamePrivileged(const DictionaryValue* name_space_node,
return !unprivileged;
}
-const base::DictionaryValue* ExtensionAPI::GetSchema(
- const std::string& api_name) const {
+const DictionaryValue* ExtensionAPI::GetSchema(const std::string& api_name) {
SchemaMap::const_iterator maybe_schema = schemas_.find(api_name);
- return maybe_schema != schemas_.end() ? maybe_schema->second.get() : NULL;
+ 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();
}
scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext(
- Feature::Context context,
- const Extension* extension,
- const GURL& url) const {
+ Feature::Context context, const Extension* extension, const GURL& url) {
+ // We're forced to load all schemas now because we need to know the metadata
+ // about every API -- and the metadata is stored in the schemas themselves.
+ // This is a shame.
+ // TODO(aa/kalman): store metadata in a separate file and don't load all
+ // schemas.
+ LoadAllSchemas();
+
scoped_ptr<std::set<std::string> > result(new std::set<std::string>());
switch (context) {
@@ -314,7 +390,7 @@ scoped_ptr<std::set<std::string> > ExtensionAPI::GetAPIsForContext(
}
void ExtensionAPI::GetAllowedAPIs(
- const Extension* extension, std::set<std::string>* out) const {
+ const Extension* extension, std::set<std::string>* out) {
for (SchemaMap::const_iterator i = schemas_.begin(); i != schemas_.end();
++i) {
if (extension->required_permission_set()->HasAnyAccessToAPI(i->first) ||
@@ -324,7 +400,7 @@ void ExtensionAPI::GetAllowedAPIs(
}
}
-void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) const {
+void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) {
std::set<std::string> missing_dependencies;
for (std::set<std::string>::iterator i = out->begin(); i != out->end(); ++i)
GetMissingDependencies(*i, *out, &missing_dependencies);
@@ -340,8 +416,8 @@ void ExtensionAPI::ResolveDependencies(std::set<std::string>* out) const {
void ExtensionAPI::GetMissingDependencies(
const std::string& api_name,
const std::set<std::string>& excluding,
- std::set<std::string>* out) const {
- const base::DictionaryValue* schema = GetSchema(api_name);
+ std::set<std::string>* out) {
+ const DictionaryValue* schema = GetSchema(api_name);
CHECK(schema) << "Schema for " << api_name << " not found";
ListValue* dependencies = NULL;
@@ -355,7 +431,7 @@ void ExtensionAPI::GetMissingDependencies(
}
}
-void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) const {
+void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) {
std::set<std::string> privileged_apis;
for (std::set<std::string>::iterator i = apis->begin(); i != apis->end();
++i) {
@@ -371,7 +447,7 @@ void ExtensionAPI::RemovePrivilegedAPIs(std::set<std::string>* apis) const {
}
void ExtensionAPI::GetAPIsMatchingURL(const GURL& url,
- std::set<std::string>* out) const {
+ 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 (i->second.MatchesURL(url))
@@ -379,4 +455,10 @@ 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 4907152..1ae7824 100644
--- a/chrome/common/extensions/api/extension_api.h
+++ b/chrome/common/extensions/api/extension_api.h
@@ -14,6 +14,7 @@
#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"
@@ -36,76 +37,77 @@ 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) const;
+ bool IsPrivileged(const std::string& name);
// 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;
+ const base::DictionaryValue* GetSchema(const std::string& api_name);
// 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) const;
+ Feature::Context context, const Extension* extension, const GURL& url);
private:
friend struct DefaultSingletonTraits<ExtensionAPI>;
- 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);
+ // Loads a schema.
+ void LoadSchema(const base::StringPiece& 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;
+ const std::string& property_value);
// 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;
+ const std::string& child_name);
// 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) const;
+ void GetAllowedAPIs(const Extension* extension, std::set<std::string>* out);
// Adds dependent schemas to |out| as determined by the "dependencies"
// property.
- void ResolveDependencies(std::set<std::string>* out) const;
+ void ResolveDependencies(std::set<std::string>* out);
// 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) const;
+ std::set<std::string>* out);
// 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) const;
+ void RemovePrivilegedAPIs(std::set<std::string>* apis);
// Adds an APIs that match |url| to |out|.
- void GetAPIsMatchingURL(const GURL& url, std::set<std::string>* out) const;
+ void GetAPIsMatchingURL(const GURL& url, std::set<std::string>* out);
+
+ // Loads all remaining resources from |unloaded_schemas_|.
+ void LoadAllSchemas();
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 1bcf33b..d6e324f 100644
--- a/chrome/common/extensions/api/extension_api_unittest.cc
+++ b/chrome/common/extensions/api/extension_api_unittest.cc
@@ -13,36 +13,57 @@
#include "chrome/common/extensions/extension.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace extensions {
namespace {
-using extensions::ExtensionAPI;
-using extensions::Feature;
-
TEST(ExtensionAPI, IsPrivileged) {
- ExtensionAPI* extension_api = ExtensionAPI::GetInstance();
- EXPECT_FALSE(extension_api->IsPrivileged("extension.connect"));
- EXPECT_FALSE(extension_api->IsPrivileged("extension.onConnect"));
+ ExtensionAPI extension_api;
+
+ 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"));
+ 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"));
}
scoped_refptr<Extension> CreateExtensionWithPermissions(
@@ -84,16 +105,18 @@ TEST(ExtensionAPI, ExtensionWithUnprivilegedAPIs) {
extension = CreateExtensionWithPermissions(permissions);
}
+ ExtensionAPI extension_api;
+
scoped_ptr<std::set<std::string> > privileged_apis =
- ExtensionAPI::GetInstance()->GetAPIsForContext(
+ extension_api.GetAPIsForContext(
Feature::BLESSED_EXTENSION_CONTEXT, extension.get(), GURL());
scoped_ptr<std::set<std::string> > unprivileged_apis =
- ExtensionAPI::GetInstance()->GetAPIsForContext(
+ extension_api.GetAPIsForContext(
Feature::UNBLESSED_EXTENSION_CONTEXT, extension.get(), GURL());
scoped_ptr<std::set<std::string> > content_script_apis =
- ExtensionAPI::GetInstance()->GetAPIsForContext(
+ extension_api.GetAPIsForContext(
Feature::CONTENT_SCRIPT_CONTEXT, extension.get(), GURL());
// "storage" is completely unprivileged.
@@ -119,7 +142,7 @@ TEST(ExtensionAPI, ExtensionWithDependencies) {
scoped_refptr<Extension> extension =
CreateExtensionWithPermission("ttsEngine");
scoped_ptr<std::set<std::string> > apis =
- ExtensionAPI::GetInstance()->GetAPIsForContext(
+ ExtensionAPI().GetAPIsForContext(
Feature::BLESSED_EXTENSION_CONTEXT, extension.get(), GURL());
EXPECT_EQ(1u, apis->count("ttsEngine"));
EXPECT_EQ(1u, apis->count("tts"));
@@ -131,39 +154,42 @@ TEST(ExtensionAPI, ExtensionWithDependencies) {
scoped_refptr<Extension> extension =
CreateExtensionWithPermission("tts");
scoped_ptr<std::set<std::string> > apis =
- ExtensionAPI::GetInstance()->GetAPIsForContext(
+ ExtensionAPI().GetAPIsForContext(
Feature::BLESSED_EXTENSION_CONTEXT, extension.get(), GURL());
EXPECT_EQ(0u, apis->count("ttsEngine"));
EXPECT_EQ(1u, apis->count("tts"));
}
}
-bool MatchesURL(const std::string& api_name, const std::string& url) {
+bool MatchesURL(
+ ExtensionAPI* api, const std::string& api_name, const std::string& url) {
scoped_ptr<std::set<std::string> > apis =
- ExtensionAPI::GetInstance()->GetAPIsForContext(
- Feature::WEB_PAGE_CONTEXT, NULL, GURL(url));
+ api->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("app", "http://example.com/example.html"));
- EXPECT_TRUE(MatchesURL("app", "https://blah.net"));
- EXPECT_TRUE(MatchesURL("app", "file://somefile.html"));
+ 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"));
// But not internal URLs (for chrome-extension:// the app API is injected by
// GetSchemasForExtension).
- EXPECT_FALSE(MatchesURL("app", "about:flags"));
- EXPECT_FALSE(MatchesURL("app", "chrome://flags"));
- EXPECT_FALSE(MatchesURL("app", "chrome-extension://fakeextension"));
+ EXPECT_FALSE(MatchesURL(&api, "app", "about:flags"));
+ EXPECT_FALSE(MatchesURL(&api, "app", "chrome://flags"));
+ EXPECT_FALSE(MatchesURL(&api, "app", "chrome-extension://fakeextension"));
// "storage" API (for example) isn't available to any URLs.
- 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"));
+ 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"));
}
} // namespace
+} // namespace extensions