summaryrefslogtreecommitdiffstats
path: root/chrome/common
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/common')
-rw-r--r--chrome/common/extensions/extension.cc598
-rw-r--r--chrome/common/extensions/extension.h210
-rw-r--r--chrome/common/extensions/extension_manifests_unittest.cc14
-rw-r--r--chrome/common/extensions/extension_permission_set.cc778
-rw-r--r--chrome/common/extensions/extension_permission_set.h389
-rw-r--r--chrome/common/extensions/extension_permission_set_unittest.cc941
-rw-r--r--chrome/common/extensions/extension_unittest.cc407
-rw-r--r--chrome/common/extensions/url_pattern_set.cc16
-rw-r--r--chrome/common/extensions/url_pattern_set.h6
-rw-r--r--chrome/common/extensions/url_pattern_set_unittest.cc53
10 files changed, 2264 insertions, 1148 deletions
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index 3a47efe..e98c5f0 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -98,50 +98,6 @@ bool IsBaseCrxKey(const std::string& key) {
return false;
}
-// Constant used to represent an undefined l10n message id.
-const int kUndefinedMessageId = -1;
-
-// Names of API modules that do not require a permission.
-const char kBrowserActionModuleName[] = "browserAction";
-const char kBrowserActionsModuleName[] = "browserActions";
-const char kDevToolsModuleName[] = "devtools";
-const char kExtensionModuleName[] = "extension";
-const char kI18NModuleName[] = "i18n";
-const char kOmniboxModuleName[] = "omnibox";
-const char kPageActionModuleName[] = "pageAction";
-const char kPageActionsModuleName[] = "pageActions";
-const char kTestModuleName[] = "test";
-const char kTypesModuleName[] = "types";
-
-// Names of modules that can be used without listing it in the permissions
-// section of the manifest.
-const char* kNonPermissionModuleNames[] = {
- kBrowserActionModuleName,
- kBrowserActionsModuleName,
- kDevToolsModuleName,
- kExtensionModuleName,
- kI18NModuleName,
- kOmniboxModuleName,
- kPageActionModuleName,
- kPageActionsModuleName,
- kTestModuleName,
- kTypesModuleName
-};
-const size_t kNumNonPermissionModuleNames =
- arraysize(kNonPermissionModuleNames);
-
-// Names of functions (within modules requiring permissions) that can be used
-// without asking for the module permission. In other words, functions you can
-// use with no permissions specified.
-const char* kNonPermissionFunctionNames[] = {
- "tabs.create",
- "tabs.onRemoved",
- "tabs.remove",
- "tabs.update",
-};
-const size_t kNumNonPermissionFunctionNames =
- arraysize(kNonPermissionFunctionNames);
-
// A singleton object containing global data needed by the extension objects.
class ExtensionConfig {
public:
@@ -149,25 +105,14 @@ class ExtensionConfig {
return Singleton<ExtensionConfig>::get();
}
- Extension::PermissionMessage::MessageId GetPermissionMessageId(
- const std::string& permission) {
- return Extension::kPermissions[permission_map_[permission]].message_id;
- }
-
Extension::ScriptingWhitelist* whitelist() { return &scripting_whitelist_; }
private:
friend struct DefaultSingletonTraits<ExtensionConfig>;
- ExtensionConfig() {
- for (size_t i = 0; i < Extension::kNumPermissions; ++i)
- permission_map_[Extension::kPermissions[i].name] = i;
- };
-
+ ExtensionConfig() { }
~ExtensionConfig() { }
- std::map<const std::string, size_t> permission_map_;
-
// A whitelist of extensions that can script anywhere. Do not add to this
// list (except in tests) without consulting the Extensions team first.
// Note: Component extensions have this right implicitly and do not need to be
@@ -175,10 +120,6 @@ class ExtensionConfig {
Extension::ScriptingWhitelist scripting_whitelist_;
};
-// Aliased to kTabPermission for purposes of API checks, but not allowed
-// in the permissions field of the manifest.
-static const char kWindowPermission[] = "windows";
-
// Rank extension locations in a way that allows
// Extension::GetHigherPriorityLocation() to compare locations.
// An extension installed from two locations will have the location
@@ -264,90 +205,6 @@ const int Extension::kPageActionIconMaxSize = 19;
const int Extension::kBrowserActionIconMaxSize = 19;
const int Extension::kSidebarIconMaxSize = 16;
-// Explicit permissions -- permission declaration required.
-const char Extension::kBackgroundPermission[] = "background";
-const char Extension::kBookmarkPermission[] = "bookmarks";
-const char Extension::kClipboardReadPermission[] = "clipboardRead";
-const char Extension::kClipboardWritePermission[] = "clipboardWrite";
-const char Extension::kContextMenusPermission[] = "contextMenus";
-const char Extension::kContentSettingsPermission[] = "contentSettings";
-const char Extension::kCookiePermission[] = "cookies";
-const char Extension::kChromePrivatePermission[] = "chromePrivate";
-const char Extension::kChromeosInfoPrivatePermission[] = "chromeosInfoPrivate";
-const char Extension::kDebuggerPermission[] = "debugger";
-const char Extension::kExperimentalPermission[] = "experimental";
-const char Extension::kFileBrowserHandlerPermission[] = "fileBrowserHandler";
-const char Extension::kFileBrowserPrivatePermission[] = "fileBrowserPrivate";
-const char Extension::kGeolocationPermission[] = "geolocation";
-const char Extension::kHistoryPermission[] = "history";
-const char Extension::kIdlePermission[] = "idle";
-const char Extension::kManagementPermission[] = "management";
-const char Extension::kMediaPlayerPrivatePermission[] = "mediaPlayerPrivate";
-const char Extension::kNotificationPermission[] = "notifications";
-const char Extension::kProxyPermission[] = "proxy";
-const char Extension::kTabPermission[] = "tabs";
-const char Extension::kUnlimitedStoragePermission[] = "unlimitedStorage";
-const char Extension::kWebstorePrivatePermission[] = "webstorePrivate";
-const char Extension::kWebSocketProxyPrivatePermission[] =
- "webSocketProxyPrivate";
-
-// In general, all permissions should have an install message.
-// See ExtensionsTest.PermissionMessages for an explanation of each
-// exception.
-const Extension::Permission Extension::kPermissions[] = {
- { kBackgroundPermission, PermissionMessage::ID_NONE },
- { kBookmarkPermission, PermissionMessage::ID_BOOKMARKS },
- { kChromePrivatePermission, PermissionMessage::ID_NONE },
- { kChromeosInfoPrivatePermission, PermissionMessage::ID_NONE },
- { kClipboardReadPermission, PermissionMessage::ID_CLIPBOARD },
- { kClipboardWritePermission, PermissionMessage::ID_NONE },
- { kContentSettingsPermission, PermissionMessage::ID_NONE },
- { kContextMenusPermission, PermissionMessage::ID_NONE },
- { kCookiePermission, PermissionMessage::ID_NONE },
- { kDebuggerPermission, PermissionMessage::ID_DEBUGGER },
- { kExperimentalPermission, PermissionMessage::ID_NONE },
- { kFileBrowserHandlerPermission, PermissionMessage::ID_NONE },
- { kFileBrowserPrivatePermission, PermissionMessage::ID_NONE },
- { kGeolocationPermission, PermissionMessage::ID_GEOLOCATION },
- { kHistoryPermission, PermissionMessage::ID_BROWSING_HISTORY },
- { kIdlePermission, PermissionMessage::ID_NONE },
- { kManagementPermission, PermissionMessage::ID_MANAGEMENT },
- { kMediaPlayerPrivatePermission, PermissionMessage::ID_NONE },
- { kNotificationPermission, PermissionMessage::ID_NONE },
- { kProxyPermission, PermissionMessage::ID_NONE },
- { kTabPermission, PermissionMessage::ID_TABS },
- { kUnlimitedStoragePermission, PermissionMessage::ID_NONE },
- { kWebSocketProxyPrivatePermission, PermissionMessage::ID_NONE },
- { kWebstorePrivatePermission, PermissionMessage::ID_NONE },
-};
-const size_t Extension::kNumPermissions = arraysize(Extension::kPermissions);
-
-const char* const Extension::kHostedAppPermissionNames[] = {
- Extension::kBackgroundPermission,
- Extension::kChromePrivatePermission,
- Extension::kClipboardReadPermission,
- Extension::kClipboardWritePermission,
- Extension::kExperimentalPermission,
- Extension::kGeolocationPermission,
- Extension::kNotificationPermission,
- Extension::kUnlimitedStoragePermission,
- Extension::kWebstorePrivatePermission,
-};
-const size_t Extension::kNumHostedAppPermissions =
- arraysize(Extension::kHostedAppPermissionNames);
-
-const char* const Extension::kComponentPrivatePermissionNames[] = {
- Extension::kFileBrowserPrivatePermission,
- Extension::kWebstorePrivatePermission,
- Extension::kMediaPlayerPrivatePermission,
- Extension::kChromeosInfoPrivatePermission,
-};
-const size_t Extension::kNumComponentPrivatePermissions =
- arraysize(Extension::kComponentPrivatePermissionNames);
-
-// We purposefully don't put this into kPermissionNames.
-const char Extension::kOldUnlimitedStoragePermission[] = "unlimited_storage";
-
const int Extension::kValidWebExtentSchemes =
URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
@@ -364,84 +221,6 @@ Extension::InputComponentInfo::InputComponentInfo()
Extension::InputComponentInfo::~InputComponentInfo() {}
//
-// PermissionMessage
-//
-
-// static
-Extension::PermissionMessage Extension::PermissionMessage::CreateFromMessageId(
- Extension::PermissionMessage::MessageId message_id) {
- DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN);
- if (message_id <= ID_NONE)
- return PermissionMessage(message_id, string16());
-
- string16 message = l10n_util::GetStringUTF16(kMessageIds[message_id]);
- return PermissionMessage(message_id, message);
-}
-
-// static
-Extension::PermissionMessage Extension::PermissionMessage::CreateFromHostList(
- const std::vector<std::string>& hosts) {
- CHECK(hosts.size() > 0);
-
- MessageId message_id;
- string16 message;
- switch (hosts.size()) {
- case 1:
- message_id = ID_HOSTS_1;
- message = l10n_util::GetStringFUTF16(kMessageIds[message_id],
- UTF8ToUTF16(hosts[0]));
- break;
- case 2:
- message_id = ID_HOSTS_2;
- message = l10n_util::GetStringFUTF16(kMessageIds[message_id],
- UTF8ToUTF16(hosts[0]),
- UTF8ToUTF16(hosts[1]));
- break;
- case 3:
- message_id = ID_HOSTS_3;
- message = l10n_util::GetStringFUTF16(kMessageIds[message_id],
- UTF8ToUTF16(hosts[0]),
- UTF8ToUTF16(hosts[1]),
- UTF8ToUTF16(hosts[2]));
- break;
- default:
- message_id = ID_HOSTS_4_OR_MORE;
- message = l10n_util::GetStringFUTF16(
- kMessageIds[message_id],
- UTF8ToUTF16(hosts[0]),
- UTF8ToUTF16(hosts[1]),
- base::IntToString16(hosts.size() - 2));
- break;
- }
-
- return PermissionMessage(message_id, message);
-}
-
-Extension::PermissionMessage::PermissionMessage(
- Extension::PermissionMessage::MessageId message_id, string16 message)
- : message_id_(message_id),
- message_(message) {
-}
-
-const int Extension::PermissionMessage::kMessageIds[] = {
- kUndefinedMessageId, // "unknown"
- kUndefinedMessageId, // "none"
- IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS,
- IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION,
- IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY,
- IDS_EXTENSION_PROMPT_WARNING_TABS,
- IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT,
- IDS_EXTENSION_PROMPT_WARNING_DEBUGGER,
- IDS_EXTENSION_PROMPT_WARNING_1_HOST,
- IDS_EXTENSION_PROMPT_WARNING_2_HOSTS,
- IDS_EXTENSION_PROMPT_WARNING_3_HOSTS,
- IDS_EXTENSION_PROMPT_WARNING_4_OR_MORE_HOSTS,
- IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS,
- IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS,
- IDS_EXTENSION_PROMPT_WARNING_CLIPBOARD
-};
-
-//
// Extension
//
@@ -491,145 +270,12 @@ Extension::Location Extension::GetHigherPriorityLocation(
return (loc1_rank > loc2_rank ? loc1 : loc2 );
}
-// static
-Extension::PermissionMessage::MessageId Extension::GetPermissionMessageId(
- const std::string& permission) {
- return ExtensionConfig::GetInstance()->GetPermissionMessageId(permission);
-}
-
-Extension::PermissionMessages Extension::GetPermissionMessages() const {
- PermissionMessages messages;
- if (!plugins().empty()) {
- messages.push_back(PermissionMessage::CreateFromMessageId(
- PermissionMessage::ID_FULL_ACCESS));
- return messages;
- }
-
- if (HasEffectiveAccessToAllHosts()) {
- messages.push_back(PermissionMessage::CreateFromMessageId(
- PermissionMessage::ID_HOSTS_ALL));
- } else {
- std::vector<std::string> hosts = GetDistinctHostsForDisplay(
- GetEffectiveHostPermissions().patterns());
- if (!hosts.empty())
- messages.push_back(PermissionMessage::CreateFromHostList(hosts));
- }
-
- std::set<PermissionMessage> simple_msgs = GetSimplePermissionMessages();
- messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end());
-
- return messages;
+ExtensionPermissionMessages Extension::GetPermissionMessages() const {
+ return permission_set_->GetPermissionMessages();
}
std::vector<string16> Extension::GetPermissionMessageStrings() const {
- std::vector<string16> messages;
- PermissionMessages permissions = GetPermissionMessages();
- for (PermissionMessages::const_iterator i = permissions.begin();
- i != permissions.end(); ++i)
- messages.push_back(i->message());
- return messages;
-}
-
-std::set<Extension::PermissionMessage>
- Extension::GetSimplePermissionMessages() const {
- std::set<PermissionMessage> messages;
- std::set<std::string>::const_iterator i;
- for (i = api_permissions().begin(); i != api_permissions().end(); ++i) {
- PermissionMessage::MessageId message_id = GetPermissionMessageId(*i);
- DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN);
- if (message_id > PermissionMessage::ID_NONE)
- messages.insert(PermissionMessage::CreateFromMessageId(message_id));
- }
- return messages;
-}
-
-// static
-std::vector<std::string> Extension::GetDistinctHostsForDisplay(
- const URLPatternList& list) {
- return GetDistinctHosts(list, true);
-}
-
-// static
-bool Extension::IsElevatedHostList(
- const URLPatternList& old_list, const URLPatternList& new_list) {
- // TODO(jstritar): This is overly conservative with respect to subdomains.
- // For example, going from *.google.com to www.google.com will be
- // considered an elevation, even though it is not (http://crbug.com/65337).
-
- std::vector<std::string> new_hosts = GetDistinctHosts(new_list, false);
- std::vector<std::string> old_hosts = GetDistinctHosts(old_list, false);
-
- std::set<std::string> old_hosts_set(old_hosts.begin(), old_hosts.end());
- std::set<std::string> new_hosts_set(new_hosts.begin(), new_hosts.end());
- std::set<std::string> new_hosts_only;
-
- std::set_difference(new_hosts_set.begin(), new_hosts_set.end(),
- old_hosts_set.begin(), old_hosts_set.end(),
- std::inserter(new_hosts_only, new_hosts_only.begin()));
-
- return !new_hosts_only.empty();
-}
-
-// Helper for GetDistinctHosts(): com > net > org > everything else.
-static bool RcdBetterThan(const std::string& a, const std::string& b) {
- if (a == b)
- return false;
- if (a == "com")
- return true;
- if (a == "net")
- return b != "com";
- if (a == "org")
- return b != "com" && b != "net";
- return false;
-}
-
-// static
-std::vector<std::string> Extension::GetDistinctHosts(
- const URLPatternList& host_patterns,
- bool include_rcd) {
- // Use a vector to preserve order (also faster than a map on small sets).
- // Each item is a host split into two parts: host without RCDs and
- // current best RCD.
- typedef std::vector<std::pair<std::string, std::string> > HostVector;
- HostVector hosts_best_rcd;
- for (size_t i = 0; i < host_patterns.size(); ++i) {
- std::string host = host_patterns[i].host();
-
- // Add the subdomain wildcard back to the host, if necessary.
- if (host_patterns[i].match_subdomains())
- host = "*." + host;
-
- // If the host has an RCD, split it off so we can detect duplicates.
- std::string rcd;
- size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength(
- host, false);
- if (reg_len && reg_len != std::string::npos) {
- if (include_rcd) // else leave rcd empty
- rcd = host.substr(host.size() - reg_len);
- host = host.substr(0, host.size() - reg_len);
- }
-
- // Check if we've already seen this host.
- HostVector::iterator it = hosts_best_rcd.begin();
- for (; it != hosts_best_rcd.end(); ++it) {
- if (it->first == host)
- break;
- }
- // If this host was found, replace the RCD if this one is better.
- if (it != hosts_best_rcd.end()) {
- if (include_rcd && RcdBetterThan(rcd, it->second))
- it->second = rcd;
- } else { // Previously unseen host, append it.
- hosts_best_rcd.push_back(std::make_pair(host, rcd));
- }
- }
-
- // Build up the final vector by concatenating hosts and RCDs.
- std::vector<std::string> distinct_hosts;
- for (HostVector::iterator it = hosts_best_rcd.begin();
- it != hosts_best_rcd.end(); ++it)
- distinct_hosts.push_back(it->first + it->second);
- return distinct_hosts;
+ return permission_set_->GetWarningMessages();
}
FilePath Extension::MaybeNormalizePath(const FilePath& path) {
@@ -648,16 +294,6 @@ FilePath Extension::MaybeNormalizePath(const FilePath& path) {
#endif
}
-// static
-bool Extension::IsHostedAppPermission(const std::string& str) {
- for (size_t i = 0; i < Extension::kNumHostedAppPermissions; ++i) {
- if (str == Extension::kHostedAppPermissionNames[i]) {
- return true;
- }
- }
- return false;
-}
-
const std::string Extension::VersionString() const {
return version()->GetString();
}
@@ -1624,55 +1260,6 @@ bool Extension::FormatPEMForFileOutput(const std::string& input,
}
// static
-bool Extension::IsPrivilegeIncrease(const bool granted_full_access,
- const std::set<std::string>& granted_apis,
- const URLPatternSet& granted_extent,
- const Extension* new_extension) {
- // If the extension had native code access, we don't need to go any further.
- // Things can't get any worse.
- if (granted_full_access)
- return false;
-
- // Otherwise, if the new extension has a plugin, it's a privilege increase.
- if (new_extension->HasFullPermissions())
- return true;
-
- // If the extension hadn't been granted access to all hosts in the past, then
- // see if the extension requires more host permissions.
- if (!HasEffectiveAccessToAllHosts(granted_extent, granted_apis)) {
- if (new_extension->HasEffectiveAccessToAllHosts())
- return true;
-
- const URLPatternSet new_extent =
- new_extension->GetEffectiveHostPermissions();
-
- if (IsElevatedHostList(granted_extent.patterns(), new_extent.patterns()))
- return true;
- }
-
- std::set<std::string> new_apis = new_extension->api_permissions();
- std::set<std::string> new_apis_only;
- std::set_difference(new_apis.begin(), new_apis.end(),
- granted_apis.begin(), granted_apis.end(),
- std::inserter(new_apis_only, new_apis_only.begin()));
-
- // Ignore API permissions that don't require user approval when deciding if
- // an extension has increased its privileges.
- size_t new_api_count = 0;
- for (std::set<std::string>::iterator i = new_apis_only.begin();
- i != new_apis_only.end(); ++i) {
- DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN);
- if (GetPermissionMessageId(*i) > PermissionMessage::ID_NONE)
- new_api_count++;
- }
-
- if (new_api_count)
- return true;
-
- return false;
-}
-
-// static
void Extension::DecodeIcon(const Extension* extension,
Icons icon_size,
scoped_ptr<SkBitmap>* result) {
@@ -1739,6 +1326,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
(flags & STRICT_ERROR_CHECKS ? URLPattern::PARSE_STRICT
: URLPattern::PARSE_LENIENT);
+ // Initialize permissions with an empty, default permission set.
+ permission_set_.reset(new ExtensionPermissionSet());
+
if (source.HasKey(keys::kPublicKey)) {
std::string public_key_bytes;
if (!source.GetString(keys::kPublicKey,
@@ -2257,6 +1847,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
}
+ ExtensionAPIPermissionSet api_permissions;
+ URLPatternSet host_permissions;
+
// Initialize the permissions (optional).
if (source.HasKey(keys::kPermissions)) {
ListValue* permissions = NULL;
@@ -2274,10 +1867,13 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
return false;
}
+ ExtensionAPIPermission* permission =
+ ExtensionPermissionsInfo::GetInstance()->GetByName(permission_str);
+
// Only COMPONENT extensions can use private APIs.
// TODO(asargent) - We want a more general purpose mechanism for this,
// and better error messages. (http://crbug.com/54013)
- if (!IsComponentOnlyPermission(permission_str)
+ if (!IsComponentOnlyPermission(permission)
#ifndef NDEBUG
&& !CommandLine::ForCurrentProcess()->HasSwitch(
switches::kExposePrivateExtensionApi)
@@ -2286,31 +1882,27 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
continue;
}
- // Remap the old unlimited storage permission name.
- if (permission_str == kOldUnlimitedStoragePermission)
- permission_str = kUnlimitedStoragePermission;
-
if (web_extent().is_empty() || location() == Extension::COMPONENT) {
// Check if it's a module permission. If so, enable that permission.
- if (IsAPIPermission(permission_str)) {
+ if (permission != NULL) {
// Only allow the experimental API permission if the command line
// flag is present, or if the extension is a component of Chrome.
- if (IsDisallowedExperimentalPermission(permission_str) &&
+ if (IsDisallowedExperimentalPermission(permission->id()) &&
location() != Extension::COMPONENT) {
*error = errors::kExperimentalFlagRequired;
return false;
}
- api_permissions_.insert(permission_str);
+ api_permissions.insert(permission->id());
continue;
}
} else {
// Hosted apps only get access to a subset of the valid permissions.
- if (IsHostedAppPermission(permission_str)) {
- if (IsDisallowedExperimentalPermission(permission_str)) {
+ if (permission != NULL && permission->is_hosted_app()) {
+ if (IsDisallowedExperimentalPermission(permission->id())) {
*error = errors::kExperimentalFlagRequired;
return false;
}
- api_permissions_.insert(permission_str);
+ api_permissions.insert(permission->id());
continue;
}
}
@@ -2340,7 +1932,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
}
- host_permissions_.push_back(pattern);
+ host_permissions.AddPattern(pattern);
}
// If it's not a host permission, then it's probably an unknown API
@@ -2365,8 +1957,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
if (is_hosted_app()) {
// Make sure "background" permission is set.
- if (api_permissions_.find(kBackgroundPermission) ==
- api_permissions_.end()) {
+ if (!api_permissions.count(ExtensionAPIPermission::kBackground)) {
*error = errors::kBackgroundPermissionNeeded;
return false;
}
@@ -2605,7 +2196,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
*error = errors::kInvalidDevToolsPage;
return false;
}
- if (!HasApiPermission(Extension::kExperimentalPermission)) {
+ if (!api_permissions.count(ExtensionAPIPermission::kExperimental)) {
*error = errors::kDevToolsExperimental;
return false;
}
@@ -2619,7 +2210,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
*error = errors::kInvalidSidebar;
return false;
}
- if (!HasApiPermission(Extension::kExperimentalPermission)) {
+ if (!api_permissions.count(ExtensionAPIPermission::kExperimental)) {
*error = errors::kSidebarExperimental;
return false;
}
@@ -2705,7 +2296,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
return false;
}
- InitEffectiveHostPermissions();
+ permission_set_.reset(
+ new ExtensionPermissionSet(this, api_permissions, host_permissions));
// Although |source| is passed in as a const, it's still possible to modify
// it. This is dangerous since the utility process re-uses |source| after
@@ -2896,91 +2488,37 @@ bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const {
return true;
}
-// static
-bool Extension::HasApiPermission(
- const std::set<std::string>& api_permissions,
- const std::string& function_name) {
- std::string permission_name = function_name;
-
- for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) {
- if (permission_name == kNonPermissionFunctionNames[i])
- return true;
- }
-
- // See if this is a function or event name first and strip out the package.
- // Functions will be of the form package.function
- // Events will be of the form package/id or package.optional.stuff
- size_t separator = function_name.find_first_of("./");
- if (separator != std::string::npos)
- permission_name = function_name.substr(0, separator);
-
- // windows and tabs are the same permission.
- if (permission_name == kWindowPermission)
- permission_name = Extension::kTabPermission;
-
- if (api_permissions.count(permission_name))
- return true;
-
- for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) {
- if (permission_name == kNonPermissionModuleNames[i]) {
- return true;
- }
- }
-
- return false;
+bool Extension::HasAPIPermission(
+ ExtensionAPIPermission::ID permission) const {
+ return permission_set()->HasAPIPermission(permission);
}
-bool Extension::HasHostPermission(const GURL& url) const {
- for (URLPatternList::const_iterator host = host_permissions().begin();
- host != host_permissions().end(); ++host) {
- // Non-component extensions can only access chrome://favicon and no other
- // chrome:// scheme urls.
- if (url.SchemeIs(chrome::kChromeUIScheme) &&
- url.host() != chrome::kChromeUIFaviconHost &&
- location() != Extension::COMPONENT)
- return false;
-
- if (host->MatchesURL(url))
- return true;
- }
- return false;
+bool Extension::HasAPIPermission(
+ const std::string& function_name) const {
+ return permission_set()->HasAccessToFunction(function_name);
}
-void Extension::InitEffectiveHostPermissions() {
- // Some APIs effectively grant access to every site. New ones should be
- // added here. (I'm looking at you, network API)
- if (HasApiPermission(api_permissions_, kProxyPermission) ||
- !devtools_url_.is_empty()) {
- URLPattern all_urls(URLPattern::SCHEME_ALL);
- all_urls.set_match_all_urls(true);
- effective_host_permissions_.AddPattern(all_urls);
- return;
- }
-
- for (URLPatternList::const_iterator host = host_permissions().begin();
- host != host_permissions().end(); ++host)
- effective_host_permissions_.AddPattern(*host);
+const URLPatternSet& Extension::GetEffectiveHostPermissions() const {
+ return permission_set()->effective_hosts();
+}
- for (UserScriptList::const_iterator content_script =
- content_scripts().begin();
- content_script != content_scripts().end(); ++content_script) {
- URLPatternList::const_iterator pattern =
- content_script->url_patterns().begin();
- for (; pattern != content_script->url_patterns().end(); ++pattern)
- effective_host_permissions_.AddPattern(*pattern);
- }
+bool Extension::HasHostPermission(const GURL& url) const {
+ if (url.SchemeIs(chrome::kChromeUIScheme) &&
+ url.host() != chrome::kChromeUIFaviconHost &&
+ location() != Extension::COMPONENT)
+ return false;
+ return permission_set()->HasExplicitAccessToOrigin(url);
}
-bool Extension::IsComponentOnlyPermission(const std::string& permission) const {
+bool Extension::IsComponentOnlyPermission(
+ const ExtensionAPIPermission* api) const {
if (location() == Extension::COMPONENT)
return true;
- // Non-component extensions are not allowed to access private apis.
- for (size_t i = 0; i < Extension::kNumComponentPrivatePermissions; ++i) {
- if (permission == Extension::kComponentPrivatePermissionNames[i])
- return false;
- }
- return true;
+ if (api == NULL)
+ return true;
+
+ return !api->is_component_only();
}
bool Extension::HasMultipleUISurfaces() const {
@@ -3025,10 +2563,8 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url,
// Otherwise, see if this extension has permission to execute script
// programmatically on pages.
- for (size_t i = 0; i < host_permissions_.size(); ++i) {
- if (host_permissions_[i].MatchesURL(page_url))
- return true;
- }
+ if (permission_set()->HasExplicitAccessToOrigin(page_url))
+ return true;
if (error) {
*error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
@@ -3038,28 +2574,12 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url,
return false;
}
-// static
-bool Extension::HasEffectiveAccessToAllHosts(
- const URLPatternSet& effective_host_permissions,
- const std::set<std::string>& api_permissions) {
- const URLPatternList patterns = effective_host_permissions.patterns();
- for (URLPatternList::const_iterator host = patterns.begin();
- host != patterns.end(); ++host) {
- if (host->match_all_urls() ||
- (host->match_subdomains() && host->host().empty()))
- return true;
- }
-
- return false;
-}
-
bool Extension::HasEffectiveAccessToAllHosts() const {
- return HasEffectiveAccessToAllHosts(GetEffectiveHostPermissions(),
- api_permissions());
+ return permission_set_->HasEffectiveAccessToAllHosts();
}
bool Extension::HasFullPermissions() const {
- return !plugins().empty();
+ return permission_set_->HasEffectiveFullAccess();
}
bool Extension::ShowConfigureContextMenus() const {
@@ -3071,21 +2591,12 @@ bool Extension::ShowConfigureContextMenus() const {
}
bool Extension::IsDisallowedExperimentalPermission(
- const std::string& permission_str) const {
- return permission_str == Extension::kExperimentalPermission &&
+ ExtensionAPIPermission::ID permission) const {
+ return permission == ExtensionAPIPermission::kExperimental &&
!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableExperimentalExtensionApis);
}
-bool Extension::IsAPIPermission(const std::string& str) const {
- for (size_t i = 0; i < Extension::kNumPermissions; ++i) {
- if (str == Extension::kPermissions[i].name) {
- return true;
- }
- }
- return false;
-}
-
bool Extension::CanExecuteScriptEverywhere() const {
if (location() == Extension::COMPONENT
#ifndef NDEBUG
@@ -3161,7 +2672,8 @@ ExtensionInfo::~ExtensionInfo() {}
UninstalledExtensionInfo::UninstalledExtensionInfo(
const Extension& extension)
: extension_id(extension.id()),
- extension_api_permissions(extension.api_permissions()),
+ extension_api_permissions(
+ extension.permission_set()->GetAPIsAsStrings()),
extension_type(extension.GetType()),
update_url(extension.update_url()) {}
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 6624268..7e584d7 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -18,6 +18,7 @@
#include "base/memory/scoped_ptr.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_icon_set.h"
+#include "chrome/common/extensions/extension_permission_set.h"
#include "chrome/common/extensions/user_script.h"
#include "chrome/common/extensions/url_pattern.h"
#include "chrome/common/extensions/url_pattern_set.h"
@@ -142,80 +143,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
std::string gender;
};
- // When prompting the user to install or approve permissions, we display
- // messages describing the effects of the permissions and not the permissions
- // themselves. Each PermissionMessage represents one of the messages that is
- // shown to the user.
- class PermissionMessage {
- public:
- // Do not reorder or add new enumerations in this list. If you need to add a
- // new enum, add it just prior to ID_ENUM_BOUNDARY and enter its l10n
- // message in kMessageIds.
- enum MessageId {
- ID_UNKNOWN,
- ID_NONE,
- ID_BOOKMARKS,
- ID_GEOLOCATION,
- ID_BROWSING_HISTORY,
- ID_TABS,
- ID_MANAGEMENT,
- ID_DEBUGGER,
- ID_HOSTS_1,
- ID_HOSTS_2,
- ID_HOSTS_3,
- ID_HOSTS_4_OR_MORE,
- ID_HOSTS_ALL,
- ID_FULL_ACCESS,
- ID_CLIPBOARD,
- ID_ENUM_BOUNDARY
- };
-
- // Creates a permission message with the given |message_id| and initializes
- // its message to the appropriate value.
- static PermissionMessage CreateFromMessageId(MessageId message_id);
-
- // Creates the corresponding permission message for a list of hosts. This
- // method exists because the hosts are presented as one message that depends
- // on what and how many hosts there are.
- static PermissionMessage CreateFromHostList(
- const std::vector<std::string>& hosts);
-
- // Gets the id of the permission message, which can be used in UMA
- // histograms.
- MessageId message_id() const { return message_id_; }
-
- // Gets a localized message describing this permission. Please note that
- // the message will be empty for message types TYPE_NONE and TYPE_UNKNOWN.
- const string16& message() const { return message_; }
-
- // Comparator to work with std::set.
- bool operator<(const PermissionMessage& that) const {
- return message_id_ < that.message_id_;
- }
-
- private:
- PermissionMessage(MessageId message_id, string16 message_);
-
- // The index of the id in the array is its enum value. The first two values
- // are non-existent message ids to act as placeholders for "unknown" and
- // "none".
- // Note: Do not change the order of the items in this list since they
- // are used in a histogram. The order must match the MessageId order.
- static const int kMessageIds[];
-
- MessageId message_id_;
- string16 message_;
- };
-
- typedef std::vector<PermissionMessage> PermissionMessages;
-
- // A permission is defined by its |name| (what is used in the manifest),
- // and the |message_id| that's used by install/update UI.
- struct Permission {
- const char* const name;
- const PermissionMessage::MessageId message_id;
- };
-
enum InitFromValueFlags {
NO_FLAGS = 0,
@@ -256,38 +183,15 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// its install source should be set to GetHigherPriorityLocation(A, B).
static Location GetHigherPriorityLocation(Location loc1, Location loc2);
- // Get's the install message id for |permission|. Returns
- // MessageId::TYPE_NONE if none exists.
- static PermissionMessage::MessageId GetPermissionMessageId(
- const std::string& permission);
-
// Returns the full list of permission messages that this extension
// should display at install time.
- PermissionMessages GetPermissionMessages() const;
+ ExtensionPermissionMessages GetPermissionMessages() const;
// Returns the full list of permission messages that this extension
// should display at install time. The messages are returned as strings
// for convenience.
std::vector<string16> GetPermissionMessageStrings() const;
- // Returns the distinct hosts that should be displayed in the install UI
- // for the URL patterns |list|. This discards some of the detail that is
- // present in the manifest to make it as easy as possible to process by
- // users. In particular we disregard the scheme and path components of
- // URLPatterns and de-dupe the result, which includes filtering out common
- // hosts with differing RCDs (aka Registry Controlled Domains, most of which
- // are Top Level Domains but also include exceptions like co.uk).
- // NOTE: when de-duping hosts the preferred RCD will be returned, given this
- // order of preference: .com, .net, .org, first in list.
- static std::vector<std::string> GetDistinctHostsForDisplay(
- const URLPatternList& list);
-
- // Compares two URLPatternLists for security equality by returning whether
- // the URL patterns in |new_list| contain additional distinct hosts compared
- // to |old_list|.
- static bool IsElevatedHostList(
- const URLPatternList& old_list, const URLPatternList& new_list);
-
// Icon sizes used by the extension system.
static const int kIconSizes[];
@@ -296,56 +200,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
static const int kBrowserActionIconMaxSize;
static const int kSidebarIconMaxSize;
- // Each permission is a module that the extension is permitted to use.
- //
- // NOTE: To add a new permission, define it here, and add an entry to
- // Extension::kPermissions.
- static const char kBackgroundPermission[];
- static const char kBookmarkPermission[];
- static const char kClipboardReadPermission[];
- static const char kClipboardWritePermission[];
- static const char kContentSettingsPermission[];
- static const char kContextMenusPermission[];
- static const char kCookiePermission[];
- static const char kChromePrivatePermission[];
- static const char kChromeosInfoPrivatePermission[];
- static const char kDebuggerPermission[];
- static const char kExperimentalPermission[];
- static const char kFileBrowserHandlerPermission[];
- static const char kFileBrowserPrivatePermission[];
- static const char kGeolocationPermission[];
- static const char kHistoryPermission[];
- static const char kIdlePermission[];
- static const char kManagementPermission[];
- static const char kMediaPlayerPrivatePermission[];
- static const char kNotificationPermission[];
- static const char kProxyPermission[];
- static const char kTabPermission[];
- static const char kUnlimitedStoragePermission[];
- static const char kWebstorePrivatePermission[];
- static const char kWebSocketProxyPrivatePermission[];
-
- static const Permission kPermissions[];
- static const size_t kNumPermissions;
- static const char* const kHostedAppPermissionNames[];
- static const size_t kNumHostedAppPermissions;
- static const char* const kComponentPrivatePermissionNames[];
- static const size_t kNumComponentPrivatePermissions;
-
- // The old name for the unlimited storage permission, which is deprecated but
- // still accepted as meaning the same thing as kUnlimitedStoragePermission.
- static const char kOldUnlimitedStoragePermission[];
-
// Valid schemes for web extent URLPatterns.
static const int kValidWebExtentSchemes;
// Valid schemes for host permission URLPatterns.
static const int kValidHostPermissionSchemes;
- // Returns true if the string is one of the known hosted app permissions (see
- // kHostedAppPermissionNames).
- static bool IsHostedAppPermission(const std::string& permission);
-
// The name of the manifest inside an extension.
static const FilePath::CharType kManifestFilename[];
@@ -455,14 +315,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
std::string* output,
bool is_public);
- // Determine whether |new_extension| has increased privileges compared to
- // its previously granted permissions, specified by |granted_apis|,
- // |granted_extent| and |granted_full_access|.
- static bool IsPrivilegeIncrease(const bool granted_full_access,
- const std::set<std::string>& granted_apis,
- const URLPatternSet& granted_extent,
- const Extension* new_extension);
-
// Given an extension and icon size, read it if present and decode it into
// result. In the browser process, this will DCHECK if not called on the
// file thread. To easily load extension images on the UI thread, see
@@ -497,24 +349,10 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
static void SetScriptingWhitelist(const ScriptingWhitelist& whitelist);
static const ScriptingWhitelist* GetScriptingWhitelist();
- // Returns true if the extension has the specified API permission.
- static bool HasApiPermission(const std::set<std::string>& api_permissions,
- const std::string& function_name);
-
- // Whether the |effective_host_permissions| and |api_permissions| include
- // effective access to all hosts. See the non-static version of the method
- // for more details.
- static bool HasEffectiveAccessToAllHosts(
- const URLPatternSet& effective_host_permissions,
- const std::set<std::string>& api_permissions);
+ bool HasAPIPermission(ExtensionAPIPermission::ID permission) const;
+ bool HasAPIPermission(const std::string& function_name) const;
- bool HasApiPermission(const std::string& function_name) const {
- return HasApiPermission(this->api_permissions(), function_name);
- }
-
- const URLPatternSet& GetEffectiveHostPermissions() const {
- return effective_host_permissions_;
- }
+ const URLPatternSet& GetEffectiveHostPermissions() const;
// Whether or not the extension is allowed permission for a URL pattern from
// the manifest. http, https, and chrome://favicon/ is allowed for all
@@ -630,10 +468,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
const GURL& options_url() const { return options_url_; }
const GURL& devtools_url() const { return devtools_url_; }
const std::vector<GURL>& toolstrips() const { return toolstrips_; }
- const std::set<std::string>& api_permissions() const {
- return api_permissions_;
+ const ExtensionPermissionSet* permission_set() const {
+ return permission_set_.get();
}
- const URLPatternList& host_permissions() const { return host_permissions_; }
const GURL& update_url() const { return update_url_; }
const ExtensionIconSet& icons() const { return icons_; }
const DictionaryValue* manifest_value() const {
@@ -766,10 +603,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
ExtensionSidebarDefaults* LoadExtensionSidebarDefaults(
const DictionaryValue* sidebar, std::string* error);
- // Calculates the effective host permissions from the permissions and content
- // script petterns.
- void InitEffectiveHostPermissions();
-
// Returns true if the extension has more than one "UI surface". For example,
// an extension that has a browser action and a page action.
bool HasMultipleUISurfaces() const;
@@ -780,21 +613,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// Only allow the experimental API permission if the command line
// flag is present.
- bool IsDisallowedExperimentalPermission(const std::string& permission) const;
-
- // Returns true if the string is one of the known api permissions (see
- // kPermissions).
- bool IsAPIPermission(const std::string& permission) const;
+ bool IsDisallowedExperimentalPermission(
+ ExtensionAPIPermission::ID permission) const;
// Returns true if this is a component, or we are not attempting to access a
// component-private permission.
- bool IsComponentOnlyPermission(const std::string& permission) const;
-
- // The set of unique API install messages that the extension has.
- // NOTE: This only includes messages related to permissions declared in the
- // "permissions" key in the manifest. Permissions implied from other features
- // of the manifest, like plugins and content scripts are not included.
- std::set<PermissionMessage> GetSimplePermissionMessages() const;
+ bool IsComponentOnlyPermission(const ExtensionAPIPermission* api) const;
// Cached images for this extension. This should only be touched on the UI
// thread.
@@ -825,15 +649,8 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// Defines the set of URLs in the extension's web content.
URLPatternSet extent_;
- // The set of host permissions that the extension effectively has access to,
- // which is a merge of host_permissions_ and all of the match patterns in
- // any content scripts the extension has. This is used to determine which
- // URLs have the ability to load an extension's resources via embedded
- // chrome-extension: URLs (see extension_protocols.cc).
- URLPatternSet effective_host_permissions_;
-
- // The set of module-level APIs this extension can use.
- std::set<std::string> api_permissions_;
+ // The set of permissions that the extension effectively has access to.
+ scoped_ptr<ExtensionPermissionSet> permission_set_;
// The icons for the extension.
ExtensionIconSet icons_;
@@ -909,9 +726,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// Whether the extension is a theme.
bool is_theme_;
- // The sites this extension has permission to talk to (using XHR, etc).
- URLPatternList host_permissions_;
-
// The homepage for this extension. Useful if it is not hosted by Google and
// therefore does not have a Gallery URL.
GURL homepage_url_;
diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc
index e2a2d76..7a7122a 100644
--- a/chrome/common/extensions/extension_manifests_unittest.cc
+++ b/chrome/common/extensions/extension_manifests_unittest.cc
@@ -296,8 +296,8 @@ TEST_F(ExtensionManifestTest, UpdateUrls) {
TEST_F(ExtensionManifestTest, OldUnlimitedStoragePermission) {
scoped_refptr<Extension> extension = LoadStrictAndExpectSuccess(
"old_unlimited_storage.json");
- EXPECT_TRUE(extension->HasApiPermission(
- Extension::kUnlimitedStoragePermission));
+ EXPECT_TRUE(extension->HasAPIPermission(
+ ExtensionAPIPermission::kUnlimitedStorage));
}
TEST_F(ExtensionManifestTest, ValidApp) {
@@ -620,14 +620,18 @@ TEST_F(ExtensionManifestTest, AllowUnrecognizedPermissions) {
ListValue *permissions = new ListValue();
manifest->Set(keys::kPermissions, permissions);
- for (size_t i = 0; i < Extension::kNumPermissions; i++) {
- const char* name = Extension::kPermissions[i].name;
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ ExtensionAPIPermissionSet api_perms = info->GetAll();
+ for (ExtensionAPIPermissionSet::iterator i = api_perms.begin();
+ i != api_perms.end(); ++i) {
+ ExtensionAPIPermission* permission = info->GetByID(*i);
+ const char* name = permission->name();
StringValue* p = new StringValue(name);
permissions->Clear();
permissions->Append(p);
std::string message_name = base::StringPrintf("permission-%s", name);
- if (name == Extension::kExperimentalPermission) {
+ if (*i == ExtensionAPIPermission::kExperimental) {
// Experimental permission is allowed, but requires this switch.
CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kEnableExperimentalExtensionApis);
diff --git a/chrome/common/extensions/extension_permission_set.cc b/chrome/common/extensions/extension_permission_set.cc
new file mode 100644
index 0000000..d87e9e3
--- /dev/null
+++ b/chrome/common/extensions/extension_permission_set.cc
@@ -0,0 +1,778 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/extensions/extension_permission_set.h"
+
+#include <algorithm>
+#include <string>
+
+#include "base/memory/singleton.h"
+#include "base/values.h"
+#include "base/string_number_conversions.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/extensions/extension_l10n_util.h"
+#include "chrome/common/extensions/url_pattern.h"
+#include "chrome/common/extensions/url_pattern_set.h"
+#include "grit/generated_resources.h"
+#include "net/base/registry_controlled_domain.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+// Helper for GetDistinctHosts(): com > net > org > everything else.
+bool RcdBetterThan(std::string a, std::string b) {
+ if (a == b)
+ return false;
+ if (a == "com")
+ return true;
+ if (a == "net")
+ return b != "com";
+ if (a == "org")
+ return b != "com" && b != "net";
+ return false;
+}
+
+// Names of API modules that do not require a permission.
+const char kBrowserActionModuleName[] = "browserAction";
+const char kBrowserActionsModuleName[] = "browserActions";
+const char kDevToolsModuleName[] = "devtools";
+const char kExtensionModuleName[] = "extension";
+const char kI18NModuleName[] = "i18n";
+const char kOmniboxModuleName[] = "omnibox";
+const char kPageActionModuleName[] = "pageAction";
+const char kPageActionsModuleName[] = "pageActions";
+const char kTestModuleName[] = "test";
+const char kTypesModuleName[] = "types";
+
+// Names of modules that can be used without listing it in the permissions
+// section of the manifest.
+const char* kNonPermissionModuleNames[] = {
+ kBrowserActionModuleName,
+ kBrowserActionsModuleName,
+ kDevToolsModuleName,
+ kExtensionModuleName,
+ kI18NModuleName,
+ kOmniboxModuleName,
+ kPageActionModuleName,
+ kPageActionsModuleName,
+ kTestModuleName,
+ kTypesModuleName
+};
+const size_t kNumNonPermissionModuleNames =
+ arraysize(kNonPermissionModuleNames);
+
+// Names of functions (within modules requiring permissions) that can be used
+// without asking for the module permission. In other words, functions you can
+// use with no permissions specified.
+const char* kNonPermissionFunctionNames[] = {
+ "tabs.create",
+ "tabs.onRemoved",
+ "tabs.remove",
+ "tabs.update",
+};
+const size_t kNumNonPermissionFunctionNames =
+ arraysize(kNonPermissionFunctionNames);
+
+const char kOldUnlimitedStoragePermission[] = "unlimited_storage";
+const char kWindowsPermission[] = "windows";
+
+void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) {
+ CHECK(out);
+ const URLPatternList& patterns = set.patterns();
+ for (size_t i = 0; i < patterns.size(); ++i) {
+ URLPattern p = patterns.at(i);
+ p.SetPath("/*");
+ out->AddPattern(p);
+ }
+}
+
+} // namespace
+
+//
+// PermissionMessage
+//
+
+// static
+ExtensionPermissionMessage ExtensionPermissionMessage::CreateFromHostList(
+ const std::vector<std::string>& hosts) {
+ CHECK(hosts.size() > 0);
+ ID message_id;
+ string16 message;
+ switch (hosts.size()) {
+ case 1:
+ message_id = kHosts1;
+ message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_1_HOST,
+ UTF8ToUTF16(hosts[0]));
+ break;
+ case 2:
+ message_id = kHosts2;
+ message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_2_HOSTS,
+ UTF8ToUTF16(hosts[0]),
+ UTF8ToUTF16(hosts[1]));
+ break;
+ case 3:
+ message_id = kHosts3;
+ message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_3_HOSTS,
+ UTF8ToUTF16(hosts[0]),
+ UTF8ToUTF16(hosts[1]),
+ UTF8ToUTF16(hosts[2]));
+ break;
+ default:
+ message_id = kHosts4OrMore;
+ message = l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PROMPT_WARNING_4_OR_MORE_HOSTS,
+ UTF8ToUTF16(hosts[0]),
+ UTF8ToUTF16(hosts[1]),
+ base::IntToString16(hosts.size() - 2));
+ break;
+ }
+
+ return ExtensionPermissionMessage(message_id, message);
+}
+
+ExtensionPermissionMessage::ExtensionPermissionMessage(
+ ExtensionPermissionMessage::ID id, const string16& message)
+ : id_(id), message_(message) {
+}
+
+ExtensionPermissionMessage::~ExtensionPermissionMessage() {
+}
+
+//
+// ExtensionPermission
+//
+
+ExtensionPermissionMessage ExtensionAPIPermission::GetMessage() const {
+ return ExtensionPermissionMessage(
+ message_id_, l10n_util::GetStringUTF16(l10n_message_id_));
+}
+
+ExtensionAPIPermission::ExtensionAPIPermission(
+ ID id,
+ const char* name,
+ bool is_hosted_app,
+ bool is_component_only,
+ int l10n_message_id,
+ ExtensionPermissionMessage::ID message_id,
+ bool implies_full_access,
+ bool implies_full_url_access)
+ : id_(id),
+ name_(name),
+ implies_full_access_(implies_full_access),
+ implies_full_url_access_(implies_full_url_access),
+ is_hosted_app_(is_hosted_app),
+ is_component_only_(is_component_only),
+ l10n_message_id_(l10n_message_id),
+ message_id_(message_id) {
+}
+
+ExtensionAPIPermission::~ExtensionAPIPermission() {
+}
+
+//
+// ExtensionPermissionsInfo
+//
+
+// static
+ExtensionPermissionsInfo* ExtensionPermissionsInfo::GetInstance() {
+ return Singleton<ExtensionPermissionsInfo>::get();
+}
+
+ExtensionAPIPermission* ExtensionPermissionsInfo::GetByID(
+ ExtensionAPIPermission::ID id) {
+ IDMap::iterator i = id_map_.find(id);
+ return (i == id_map_.end()) ? NULL : i->second;
+}
+
+ExtensionAPIPermission* ExtensionPermissionsInfo::GetByName(std::string name) {
+ NameMap::iterator i = name_map_.find(name);
+ return (i == name_map_.end()) ? NULL : i->second;
+}
+
+ExtensionAPIPermissionSet ExtensionPermissionsInfo::GetAll() {
+ ExtensionAPIPermissionSet permissions;
+ for (IDMap::const_iterator i = id_map_.begin(); i != id_map_.end(); ++i)
+ permissions.insert(i->second->id());
+ return permissions;
+}
+
+ExtensionAPIPermissionSet ExtensionPermissionsInfo::GetAllByName(
+ const std::set<std::string>& permission_names) {
+ ExtensionAPIPermissionSet permissions;
+ for (std::set<std::string>::const_iterator i = permission_names.begin();
+ i != permission_names.end(); ++i) {
+ ExtensionAPIPermission* permission = GetByName(*i);
+ if (permission)
+ permissions.insert(permission->id());
+ }
+ return permissions;
+}
+
+ExtensionPermissionsInfo::~ExtensionPermissionsInfo() {
+ for (IDMap::iterator i = id_map_.begin(); i != id_map_.end(); ++i)
+ delete i->second;
+}
+
+ExtensionPermissionsInfo::ExtensionPermissionsInfo()
+ : hosted_app_permission_count_(0),
+ permission_count_(0) {
+ // Hosted app permissions
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kBackground, "background", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kClipboardRead, "clipboardRead",
+ IDS_EXTENSION_PROMPT_WARNING_CLIPBOARD,
+ ExtensionPermissionMessage::kClipboard);
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kClipboardWrite, "clipboardWrite", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kChromePrivate, "chromePrivate", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kExperimental, "experimental", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kGeolocation, "geolocation",
+ IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION,
+ ExtensionPermissionMessage::kGeolocation);
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kNotification, "notifications", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterHostedAppPermission(
+ ExtensionAPIPermission::kUnlimitedStorage, "unlimitedStorage", 0,
+ ExtensionPermissionMessage::kNone);
+
+ // Hosted app and private permissions.
+ RegisterPermission(
+ ExtensionAPIPermission::kWebstorePrivate, "webstorePrivate", 0,
+ ExtensionPermissionMessage::kNone,
+ true, true, false, false);
+
+ // Extension permissions.
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kBookmark, "bookmarks",
+ IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS,
+ ExtensionPermissionMessage::kBookmarks);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kContentSettings, "contentSettings", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kContextMenus, "contextMenus", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kCookie, "cookies", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kDebugger, "debugger",
+ IDS_EXTENSION_PROMPT_WARNING_DEBUGGER,
+ ExtensionPermissionMessage::kDebugger);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kFileBrowserHandler, "fileBrowserHandler", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kHistory, "history",
+ IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY,
+ ExtensionPermissionMessage::kBrowsingHistory);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kIdle, "idle", 0,
+ ExtensionPermissionMessage::kNone);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kManagement, "management",
+ IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT,
+ ExtensionPermissionMessage::kManagement);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kTab, "tabs",
+ IDS_EXTENSION_PROMPT_WARNING_TABS,
+ ExtensionPermissionMessage::kTabs);
+ RegisterExtensionPermission(
+ ExtensionAPIPermission::kWebSocketProxyPrivate,
+ "webSocketProxyPrivate", 0,
+ ExtensionPermissionMessage::kNone);
+
+ // Private permissions
+ RegisterPrivatePermission(
+ ExtensionAPIPermission::kChromeosInfoPrivate, "chromeosInfoPrivate");
+ RegisterPrivatePermission(
+ ExtensionAPIPermission::kFileBrowserPrivate, "fileBrowserPrivate");
+ RegisterPrivatePermission(
+ ExtensionAPIPermission::kMediaPlayerPrivate, "mediaPlayerPrivate");
+
+ // Full url access permissions.
+ RegisterPermission(
+ ExtensionAPIPermission::kProxy, "proxy", 0,
+ ExtensionPermissionMessage::kNone, false, false, false, true);
+
+ RegisterPermission(
+ ExtensionAPIPermission::kDevtools, "devtools", 0,
+ ExtensionPermissionMessage::kNone, false, false, false, true);
+
+ RegisterPermission(
+ ExtensionAPIPermission::kPlugin, "plugin",
+ IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS,
+ ExtensionPermissionMessage::kFullAccess, false, false, true, true);
+
+ RegisterPermission(
+ ExtensionAPIPermission::kDefault, "default", 0,
+ ExtensionPermissionMessage::kNone, false, false, false, false);
+
+ // Register Aliases
+ RegisterAlias("unlimitedStorage", kOldUnlimitedStoragePermission);
+ RegisterAlias("tabs", kWindowsPermission);
+}
+
+void ExtensionPermissionsInfo::RegisterAlias(
+ const char* name, const char* alias) {
+ CHECK(name_map_.find(name) != name_map_.end());
+ CHECK(name_map_.find(alias) == name_map_.end());
+ name_map_[alias] = name_map_[name];
+}
+
+void ExtensionPermissionsInfo::RegisterExtensionPermission(
+ ExtensionAPIPermission::ID id,
+ const char* name,
+ int l10n_message_id,
+ ExtensionPermissionMessage::ID message_id) {
+ RegisterPermission(id, name, l10n_message_id, message_id,
+ false, false, false, false);
+}
+
+void ExtensionPermissionsInfo::RegisterHostedAppPermission(
+ ExtensionAPIPermission::ID id,
+ const char* name,
+ int l10n_message_id,
+ ExtensionPermissionMessage::ID message_id) {
+ RegisterPermission(id, name, l10n_message_id, message_id,
+ true, false, false, false);
+}
+
+void ExtensionPermissionsInfo::RegisterPrivatePermission(
+ ExtensionAPIPermission::ID id, const char* name) {
+ RegisterPermission(id, name, 0, ExtensionPermissionMessage::kNone,
+ false, true, false, false);
+}
+
+void ExtensionPermissionsInfo::RegisterPermission(
+ ExtensionAPIPermission::ID id,
+ const char* name,
+ int l10n_message_id,
+ ExtensionPermissionMessage::ID message_id,
+ bool is_hosted_app,
+ bool is_component_only,
+ bool implies_full_access,
+ bool implies_full_url_access) {
+ CHECK(id_map_.find(id) == id_map_.end());
+ CHECK(name_map_.find(name) == name_map_.end());
+
+ ExtensionAPIPermission* permission =
+ new ExtensionAPIPermission(id,
+ name,
+ is_hosted_app,
+ is_component_only,
+ l10n_message_id,
+ message_id,
+ implies_full_access,
+ implies_full_url_access);
+ id_map_[id] = permission;
+ name_map_[name] = permission;
+
+ permission_count_++;
+ if (permission->is_hosted_app())
+ hosted_app_permission_count_++;
+}
+
+//
+// ExtensionPermissionSet
+//
+
+ExtensionPermissionSet::ExtensionPermissionSet() {
+}
+
+ExtensionPermissionSet::ExtensionPermissionSet(
+ const Extension* extension,
+ const ExtensionAPIPermissionSet& apis,
+ const URLPatternSet& explicit_hosts)
+ : apis_(apis) {
+ CHECK(extension);
+ AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_);
+ InitImplicitExtensionPermissions(extension);
+ InitEffectiveHosts();
+}
+
+ExtensionPermissionSet::ExtensionPermissionSet(
+ const ExtensionAPIPermissionSet& apis,
+ const URLPatternSet& explicit_hosts,
+ const URLPatternSet& scriptable_hosts)
+ : apis_(apis),
+ scriptable_hosts_(scriptable_hosts) {
+ AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_);
+ InitEffectiveHosts();
+}
+
+ExtensionPermissionSet::~ExtensionPermissionSet() {
+}
+
+// static
+ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion(
+ const ExtensionPermissionSet* set1,
+ const ExtensionPermissionSet* set2) {
+ ExtensionPermissionSet empty;
+ const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? &empty : set1;
+ const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? &empty : set2;
+
+ ExtensionAPIPermissionSet apis;
+ std::set_union(set1_safe->apis().begin(), set1_safe->apis().end(),
+ set2_safe->apis().begin(), set2_safe->apis().end(),
+ std::insert_iterator<ExtensionAPIPermissionSet>(
+ apis, apis.begin()));
+
+ URLPatternSet explicit_hosts;
+ URLPatternSet scriptable_hosts;
+ URLPatternSet::CreateUnion(set1_safe->explicit_hosts(),
+ set2_safe->explicit_hosts(),
+ &explicit_hosts);
+ URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(),
+ set2_safe->scriptable_hosts(),
+ &scriptable_hosts);
+
+ return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts);
+}
+
+std::set<std::string> ExtensionPermissionSet::GetAPIsAsStrings() const {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ std::set<std::string> apis_str;
+ for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin();
+ i != apis_.end(); ++i) {
+ ExtensionAPIPermission* permission = info->GetByID(*i);
+ if (permission)
+ apis_str.insert(permission->name());
+ }
+ return apis_str;
+}
+
+std::vector<std::string>
+ ExtensionPermissionSet::GetDistinctHostsForDisplay() const {
+ return GetDistinctHosts(effective_hosts_.patterns(), true);
+}
+
+ExtensionPermissionMessages
+ ExtensionPermissionSet::GetPermissionMessages() const {
+ ExtensionPermissionMessages messages;
+
+ if (HasEffectiveFullAccess()) {
+ messages.push_back(ExtensionPermissionMessage(
+ ExtensionPermissionMessage::kFullAccess,
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS)));
+ return messages;
+ }
+
+ if (HasEffectiveAccessToAllHosts()) {
+ messages.push_back(ExtensionPermissionMessage(
+ ExtensionPermissionMessage::kHostsAll,
+ l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS)));
+ } else {
+ std::vector<std::string> hosts = GetDistinctHostsForDisplay();
+ if (!hosts.empty())
+ messages.push_back(ExtensionPermissionMessage::CreateFromHostList(hosts));
+ }
+
+ std::set<ExtensionPermissionMessage> simple_msgs =
+ GetSimplePermissionMessages();
+ messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end());
+
+ return messages;
+}
+
+std::vector<string16> ExtensionPermissionSet::GetWarningMessages() const {
+ std::vector<string16> messages;
+ ExtensionPermissionMessages permissions = GetPermissionMessages();
+ for (ExtensionPermissionMessages::const_iterator i = permissions.begin();
+ i != permissions.end(); ++i)
+ messages.push_back(i->message());
+ return messages;
+}
+
+bool ExtensionPermissionSet::IsEmpty() const {
+ // Not default if any host permissions are present.
+ if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty()))
+ return false;
+
+ // Or if it has no api permissions.
+ return apis().empty();
+}
+
+bool ExtensionPermissionSet::HasAPIPermission(
+ ExtensionAPIPermission::ID permission) const {
+ return apis().find(permission) != apis().end();
+}
+
+bool ExtensionPermissionSet::HasAccessToFunction(
+ const std::string& function_name) const {
+ // TODO(jstritar): Embed this information in each permission and add a method
+ // like GrantsAccess(function_name) to ExtensionAPIPermission. A "default"
+ // permission can then handle the modules and functions that everyone can
+ // access.
+ std::string permission_name = function_name;
+
+ for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) {
+ if (permission_name == kNonPermissionFunctionNames[i])
+ return true;
+ }
+
+ // See if this is a function or event name first and strip out the package.
+ // Functions will be of the form package.function
+ // Events will be of the form package/id or package.optional.stuff
+ size_t separator = function_name.find_first_of("./");
+ if (separator != std::string::npos)
+ permission_name = function_name.substr(0, separator);
+
+ ExtensionAPIPermission* permission =
+ ExtensionPermissionsInfo::GetInstance()->GetByName(permission_name);
+ if (permission && apis_.count(permission->id()))
+ return true;
+
+ for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) {
+ if (permission_name == kNonPermissionModuleNames[i]) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ExtensionPermissionSet::HasExplicitAccessToOrigin(
+ const GURL& origin) const {
+ return explicit_hosts().MatchesURL(origin);
+}
+
+bool ExtensionPermissionSet::HasScriptableAccessToURL(
+ const GURL& origin) const {
+ // We only need to check our host list to verify access. The host list should
+ // already reflect any special rules (such as chrome://favicon, component
+ // all hosts access, etc.).
+ return scriptable_hosts().MatchesURL(origin);
+}
+
+bool ExtensionPermissionSet::HasEffectiveAccessToAllHosts() const {
+ // There are two ways this set can have effective access to all hosts:
+ // 1) it has an <all_urls> URL pattern.
+ // 2) it has a named permission with implied full URL access.
+ const URLPatternList patterns = effective_hosts().patterns();
+ for (URLPatternList::const_iterator host = patterns.begin();
+ host != patterns.end(); ++host) {
+ if (host->match_all_urls() ||
+ (host->match_subdomains() && host->host().empty()))
+ return true;
+ }
+
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ for (ExtensionAPIPermissionSet::const_iterator i = apis().begin();
+ i != apis().end(); ++i) {
+ ExtensionAPIPermission* permission = info->GetByID(*i);
+ if (permission->implies_full_url_access())
+ return true;
+ }
+ return false;
+}
+
+bool ExtensionPermissionSet::HasEffectiveAccessToURL(
+ const GURL& url) const {
+ return effective_hosts().MatchesURL(url);
+}
+
+bool ExtensionPermissionSet::HasEffectiveFullAccess() const {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ for (ExtensionAPIPermissionSet::const_iterator i = apis().begin();
+ i != apis().end(); ++i) {
+ ExtensionAPIPermission* permission = info->GetByID(*i);
+ if (permission->implies_full_access())
+ return true;
+ }
+ return false;
+}
+
+bool ExtensionPermissionSet::HasPrivatePermissions() const {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin();
+ i != apis_.end(); ++i) {
+ ExtensionAPIPermission* permission = info->GetByID(*i);
+ if (permission && permission->is_component_only())
+ return true;
+ }
+ return false;
+}
+
+bool ExtensionPermissionSet::HasLessPrivilegesThan(
+ const ExtensionPermissionSet* permissions) const {
+ // Things can't get worse than native code access.
+ if (HasEffectiveFullAccess())
+ return false;
+
+ // Otherwise, it's a privilege increase if the new one has full access.
+ if (permissions->HasEffectiveFullAccess())
+ return true;
+
+ if (HasLessHostPrivilegesThan(permissions))
+ return true;
+
+ if (HasLessAPIPrivilegesThan(permissions))
+ return true;
+
+ return false;
+}
+
+// static
+std::vector<std::string> ExtensionPermissionSet::GetDistinctHosts(
+ const URLPatternList& host_patterns, bool include_rcd) {
+ // Use a vector to preserve order (also faster than a map on small sets).
+ // Each item is a host split into two parts: host without RCDs and
+ // current best RCD.
+ typedef std::vector<std::pair<std::string, std::string> > HostVector;
+ HostVector hosts_best_rcd;
+ for (size_t i = 0; i < host_patterns.size(); ++i) {
+ std::string host = host_patterns[i].host();
+
+ // Add the subdomain wildcard back to the host, if necessary.
+ if (host_patterns[i].match_subdomains())
+ host = "*." + host;
+
+ // If the host has an RCD, split it off so we can detect duplicates.
+ std::string rcd;
+ size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength(
+ host, false);
+ if (reg_len && reg_len != std::string::npos) {
+ if (include_rcd) // else leave rcd empty
+ rcd = host.substr(host.size() - reg_len);
+ host = host.substr(0, host.size() - reg_len);
+ }
+
+ // Check if we've already seen this host.
+ HostVector::iterator it = hosts_best_rcd.begin();
+ for (; it != hosts_best_rcd.end(); ++it) {
+ if (it->first == host)
+ break;
+ }
+ // If this host was found, replace the RCD if this one is better.
+ if (it != hosts_best_rcd.end()) {
+ if (include_rcd && RcdBetterThan(rcd, it->second))
+ it->second = rcd;
+ } else { // Previously unseen host, append it.
+ hosts_best_rcd.push_back(std::make_pair(host, rcd));
+ }
+ }
+
+ // Build up the final vector by concatenating hosts and RCDs.
+ std::vector<std::string> distinct_hosts;
+ for (HostVector::iterator it = hosts_best_rcd.begin();
+ it != hosts_best_rcd.end(); ++it)
+ distinct_hosts.push_back(it->first + it->second);
+ return distinct_hosts;
+}
+
+void ExtensionPermissionSet::InitEffectiveHosts() {
+ effective_hosts_.ClearPatterns();
+
+ if (HasEffectiveAccessToAllHosts()) {
+ URLPattern all_urls(URLPattern::SCHEME_ALL);
+ all_urls.set_match_all_urls(true);
+ effective_hosts_.AddPattern(all_urls);
+ return;
+ }
+
+ URLPatternSet::CreateUnion(
+ explicit_hosts(), scriptable_hosts(), &effective_hosts_);
+}
+
+void ExtensionPermissionSet::InitImplicitExtensionPermissions(
+ const Extension* extension) {
+ // Add the implied permissions.
+ if (!extension->plugins().empty())
+ apis_.insert(ExtensionAPIPermission::kPlugin);
+
+ if (!extension->devtools_url().is_empty())
+ apis_.insert(ExtensionAPIPermission::kDevtools);
+
+ // Add the scriptable hosts.
+ for (UserScriptList::const_iterator content_script =
+ extension->content_scripts().begin();
+ content_script != extension->content_scripts().end(); ++content_script) {
+ URLPatternList::const_iterator pattern =
+ content_script->url_patterns().begin();
+ for (; pattern != content_script->url_patterns().end(); ++pattern)
+ scriptable_hosts_.AddPattern(*pattern);
+ }
+}
+
+std::set<ExtensionPermissionMessage>
+ ExtensionPermissionSet::GetSimplePermissionMessages() const {
+ std::set<ExtensionPermissionMessage> messages;
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin();
+ i != apis_.end(); ++i) {
+ DCHECK_GT(ExtensionPermissionMessage::kNone,
+ ExtensionPermissionMessage::kUnknown);
+ ExtensionAPIPermission* perm = info->GetByID(*i);
+ if (perm && perm->message_id() > ExtensionPermissionMessage::kNone)
+ messages.insert(perm->GetMessage());
+ }
+ return messages;
+}
+
+bool ExtensionPermissionSet::HasLessAPIPrivilegesThan(
+ const ExtensionPermissionSet* permissions) const {
+ if (permissions == NULL)
+ return false;
+
+ ExtensionAPIPermissionSet new_apis = permissions->apis_;
+ ExtensionAPIPermissionSet new_apis_only;
+ std::set_difference(new_apis.begin(), new_apis.end(),
+ apis_.begin(), apis_.end(),
+ std::inserter(new_apis_only, new_apis_only.begin()));
+
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+
+ // Ignore API permissions that don't require user approval when deciding if
+ // an extension has increased its privileges.
+ for (ExtensionAPIPermissionSet::iterator i = new_apis_only.begin();
+ i != new_apis_only.end(); ++i) {
+ ExtensionAPIPermission* perm = info->GetByID(*i);
+ if (perm && perm->message_id() > ExtensionPermissionMessage::kNone)
+ return true;
+ }
+ return false;
+}
+
+bool ExtensionPermissionSet::HasLessHostPrivilegesThan(
+ const ExtensionPermissionSet* permissions) const {
+ // If this permission set can access any host, then it can't be elevated.
+ if (HasEffectiveAccessToAllHosts())
+ return false;
+
+ // Likewise, if the other permission set has full host access, then it must be
+ // a privilege increase.
+ if (permissions->HasEffectiveAccessToAllHosts())
+ return true;
+
+ const URLPatternList old_list = effective_hosts().patterns();
+ const URLPatternList new_list = permissions->effective_hosts().patterns();
+
+ // TODO(jstritar): This is overly conservative with respect to subdomains.
+ // For example, going from *.google.com to www.google.com will be
+ // considered an elevation, even though it is not (http://crbug.com/65337).
+ std::vector<std::string> new_hosts = GetDistinctHosts(new_list, false);
+ std::vector<std::string> old_hosts = GetDistinctHosts(old_list, false);
+
+ std::set<std::string> old_hosts_set(old_hosts.begin(), old_hosts.end());
+ std::set<std::string> new_hosts_set(new_hosts.begin(), new_hosts.end());
+ std::set<std::string> new_hosts_only;
+
+ std::set_difference(new_hosts_set.begin(), new_hosts_set.end(),
+ old_hosts_set.begin(), old_hosts_set.end(),
+ std::inserter(new_hosts_only, new_hosts_only.begin()));
+
+ return !new_hosts_only.empty();
+}
diff --git a/chrome/common/extensions/extension_permission_set.h b/chrome/common/extensions/extension_permission_set.h
new file mode 100644
index 0000000..14f58a90
--- /dev/null
+++ b/chrome/common/extensions/extension_permission_set.h
@@ -0,0 +1,389 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_EXTENSIONS_EXTENSION_PERMISSION_SET_H_
+#define CHROME_COMMON_EXTENSIONS_EXTENSION_PERMISSION_SET_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/gtest_prod_util.h"
+#include "base/memory/singleton.h"
+#include "base/scoped_ptr.h"
+#include "base/string16.h"
+#include "chrome/common/extensions/url_pattern_set.h"
+
+class DictionaryValue;
+class Extension;
+class ExtensionPrefs;
+class ListValue;
+
+// When prompting the user to install or approve permissions, we display
+// messages describing the effects of the permissions rather than listing the
+// permissions themselves. Each ExtensionPermissionMessage represents one of the
+// messages shown to the user.
+class ExtensionPermissionMessage {
+ public:
+ // Do not reorder this enumeration. If you need to add a new enum, add it just
+ // prior to kEnumBoundary.
+ enum ID {
+ kUnknown,
+ kNone,
+ kBookmarks,
+ kGeolocation,
+ kBrowsingHistory,
+ kTabs,
+ kManagement,
+ kDebugger,
+ kHosts1,
+ kHosts2,
+ kHosts3,
+ kHosts4OrMore,
+ kHostsAll,
+ kFullAccess,
+ kClipboard,
+ kEnumBoundary
+ };
+
+ // Creates the corresponding permission message for a list of hosts. This is
+ // simply a convenience method around the constructor, since the messages
+ // change depending on what hosts are present.
+ static ExtensionPermissionMessage CreateFromHostList(
+ const std::vector<std::string>& hosts);
+
+ // Creates the corresponding permission message.
+ ExtensionPermissionMessage(ID id, const string16& message);
+ ~ExtensionPermissionMessage();
+
+ // Gets the id of the permission message, which can be used in UMA
+ // histograms.
+ ID id() const { return id_; }
+
+ // Gets a localized message describing this permission. Please note that
+ // the message will be empty for message types TYPE_NONE and TYPE_UNKNOWN.
+ const string16& message() const { return message_; }
+
+ // Comparator to work with std::set.
+ bool operator<(const ExtensionPermissionMessage& that) const {
+ return id_ < that.id_;
+ }
+
+ private:
+ ID id_;
+ string16 message_;
+};
+
+typedef std::vector<ExtensionPermissionMessage> ExtensionPermissionMessages;
+
+// The ExtensionAPIPermission is an immutable class that describes a single
+// named permission (API permission).
+class ExtensionAPIPermission {
+ public:
+ enum ID {
+ // Error codes.
+ kInvalid = -2,
+ kUnknown = -1,
+
+ // Default permission that every extension has implicity.
+ kDefault,
+
+ // Real permissions.
+ kBackground,
+ kBookmark,
+ kClipboardRead,
+ kClipboardWrite,
+ kContentSettings,
+ kContextMenus,
+ kCookie,
+ kChromePrivate,
+ kChromeosInfoPrivate,
+ kDebugger,
+ kExperimental,
+ kFileBrowserHandler,
+ kFileBrowserPrivate,
+ kGeolocation,
+ kHistory,
+ kIdle,
+ kManagement,
+ kMediaPlayerPrivate,
+ kNotification,
+ kProxy,
+ kTab,
+ kUnlimitedStorage,
+ kWebSocketProxyPrivate,
+ kWebstorePrivate,
+ kDevtools,
+ kPlugin,
+ kEnumBoundary
+ };
+
+ typedef std::set<ID> IDSet;
+
+ ~ExtensionAPIPermission();
+
+ // Returns the localized permission message associated with this api.
+ ExtensionPermissionMessage GetMessage() const;
+
+ ID id() const { return id_; }
+
+ // Returns the message id associated with this permission.
+ ExtensionPermissionMessage::ID message_id() const {
+ return message_id_;
+ }
+
+ // Returns the name of this permission.
+ const char* name() const { return name_; }
+
+ // Returns true if this permission implies full access (e.g., native code).
+ bool implies_full_access() const { return implies_full_access_; }
+
+ // Returns true if this permission implies full URL access.
+ bool implies_full_url_access() const { return implies_full_url_access_; }
+
+ // Returns true if this permission can be accessed by hosted apps.
+ bool is_hosted_app() const { return is_hosted_app_; }
+
+ // Returns true if this permission can only be acquired by COMPONENT
+ // extensions.
+ bool is_component_only() const { return is_component_only_; }
+
+ private:
+ // Instances should only be constructed from within ExtensionPermissionsInfo.
+ friend class ExtensionPermissionsInfo;
+
+ explicit ExtensionAPIPermission(
+ ID id,
+ const char* name,
+ bool is_hosted_app,
+ bool is_component_only,
+ int l10n_message_id,
+ ExtensionPermissionMessage::ID message_id,
+ bool implies_full_access,
+ bool implies_full_url_access);
+
+ ID id_;
+ const char* name_;
+ bool implies_full_access_;
+ bool implies_full_url_access_;
+ bool is_hosted_app_;
+ bool is_component_only_;
+ int l10n_message_id_;
+ ExtensionPermissionMessage::ID message_id_;
+};
+
+typedef std::set<ExtensionAPIPermission::ID> ExtensionAPIPermissionSet;
+
+// Singleton that holds the extension permission instances and provides static
+// methods for accessing them.
+class ExtensionPermissionsInfo {
+ public:
+ // Returns a pointer to the singleton instance.
+ static ExtensionPermissionsInfo* GetInstance();
+
+ // Returns the permission with the given |id|, and NULL if it doesn't exist.
+ ExtensionAPIPermission* GetByID(ExtensionAPIPermission::ID id);
+
+ // Returns the permission with the given |name|, and NULL if none
+ // exists.
+ ExtensionAPIPermission* GetByName(std::string name);
+
+ // Returns a set containing all valid api permission ids.
+ ExtensionAPIPermissionSet GetAll();
+
+ // Converts all the permission names in |permission_names| to permission ids.
+ ExtensionAPIPermissionSet GetAllByName(
+ const std::set<std::string>& permission_names);
+
+ // Gets the total number of API permissions available to hosted apps.
+ size_t get_hosted_app_permission_count() {
+ return hosted_app_permission_count_;
+ }
+
+ // Gets the total number of API permissions.
+ size_t get_permission_count() { return permission_count_; }
+
+ private:
+ ~ExtensionPermissionsInfo();
+ ExtensionPermissionsInfo();
+
+ // Registers an |alias| for a given permission |name|.
+ void RegisterAlias(const char* name, const char* alias);
+
+ // Registers a standard extension permission.
+ void RegisterExtensionPermission(
+ ExtensionAPIPermission::ID id,
+ const char* name,
+ int l10n_message_id,
+ ExtensionPermissionMessage::ID message_id);
+
+ // Registers a permission that can be accessed by hosted apps.
+ void RegisterHostedAppPermission(
+ ExtensionAPIPermission::ID id,
+ const char* name,
+ int l10n_message_id,
+ ExtensionPermissionMessage::ID message_id);
+
+ // Registers a permission accessible only by COMPONENT extensions.
+ void RegisterPrivatePermission(
+ ExtensionAPIPermission::ID id,
+ const char* name);
+
+ // Registers a permission with a custom set of attributes not satisfied
+ // by the other registration functions.
+ void RegisterPermission(
+ ExtensionAPIPermission::ID id,
+ const char* name,
+ int l10n_message_id,
+ ExtensionPermissionMessage::ID message_id,
+ bool is_hosted_app,
+ bool is_component_only,
+ bool implies_full_access,
+ bool implies_full_url_access);
+
+ // Maps permission ids to permissions.
+ typedef std::map<ExtensionAPIPermission::ID, ExtensionAPIPermission*> IDMap;
+
+ // Maps names and aliases to permissions.
+ typedef std::map<std::string, ExtensionAPIPermission*> NameMap;
+
+ IDMap id_map_;
+ NameMap name_map_;
+
+ size_t hosted_app_permission_count_;
+ size_t permission_count_;
+
+ friend struct DefaultSingletonTraits<ExtensionPermissionsInfo>;
+ DISALLOW_COPY_AND_ASSIGN(ExtensionPermissionsInfo);
+};
+
+// The ExtensionPermissionSet is an immutable class that encapsulates an
+// extension's permissions. The class exposes set operations for combining and
+// manipulating the permissions.
+class ExtensionPermissionSet {
+ public:
+ // Creates an empty permission set (e.g. default permissions).
+ ExtensionPermissionSet();
+
+ // Creates a new permission set based on the |extension| manifest data, and
+ // the api and host permissions (|apis| and |hosts|). The effective hosts
+ // of the newly created permission set will be inferred from the |extension|
+ // manifest, |apis| and |hosts|.
+ ExtensionPermissionSet(const Extension* extension,
+ const ExtensionAPIPermissionSet& apis,
+ const URLPatternSet& explicit_hosts);
+
+ // Creates a new permission set based on the specified data.
+ ExtensionPermissionSet(const ExtensionAPIPermissionSet& apis,
+ const URLPatternSet& explicit_hosts,
+ const URLPatternSet& scriptable_hosts);
+
+ ~ExtensionPermissionSet();
+
+ // Creates a new permission set equal to the union of |set1| and |set2|.
+ // Passes ownership of the new set to the caller.
+ static ExtensionPermissionSet* CreateUnion(
+ const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2);
+
+ // Gets the API permissions in this set as a set of strings.
+ std::set<std::string> GetAPIsAsStrings() const;
+
+ // Gets a list of the distinct hosts for displaying to the user.
+ // NOTE: do not use this for comparing permissions, since this disgards some
+ // information.
+ std::vector<std::string> GetDistinctHostsForDisplay() const;
+
+ // Gets the localized permission messages that represent this set.
+ ExtensionPermissionMessages GetPermissionMessages() const;
+
+ // Gets the localized permission messages that represent this set (represented
+ // as strings).
+ std::vector<string16> GetWarningMessages() const;
+
+ // Returns true if this is an empty set (e.g., the default permission set).
+ bool IsEmpty() const;
+
+ // Returns true if the set has the specified API permission.
+ bool HasAPIPermission(ExtensionAPIPermission::ID permission) const;
+
+ // Returns true if the permissions in this set grant access to the specified
+ // |function_name|.
+ bool HasAccessToFunction(const std::string& function_name) const;
+
+ // Returns true if this includes permission to access |origin|.
+ bool HasExplicitAccessToOrigin(const GURL& origin) const;
+
+ // Returns true if this permission set includes access to script |url|.
+ bool HasScriptableAccessToURL(const GURL& url) const;
+
+ // Returns true if this permission set includes effective access to all
+ // origins.
+ bool HasEffectiveAccessToAllHosts() const;
+
+ // Returns true if this permission set includes effective access to |url|.
+ bool HasEffectiveAccessToURL(const GURL& url) const;
+
+ // Returns ture if this permission set effectively represents full access
+ // (e.g. native code).
+ bool HasEffectiveFullAccess() const;
+
+ // Returns true if this permission set includes permissions that are
+ // restricted to internal extensions.
+ bool HasPrivatePermissions() const;
+
+ // Returns true if |permissions| has a greater privilege level than this
+ // permission set (e.g., this permission set has less permissions).
+ bool HasLessPrivilegesThan(const ExtensionPermissionSet* permissions) const;
+
+ const ExtensionAPIPermissionSet& apis() const { return apis_; }
+
+ const URLPatternSet& effective_hosts() const { return effective_hosts_; }
+
+ const URLPatternSet& explicit_hosts() const { return explicit_hosts_; }
+
+ const URLPatternSet& scriptable_hosts() const { return scriptable_hosts_; }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(ExtensionPermissionSetTest,
+ HasLessHostPrivilegesThan);
+
+ static std::vector<std::string> GetDistinctHosts(
+ const URLPatternList& host_patterns, bool include_rcd);
+
+ // Initializes the set based on |extension|'s manifest data.
+ void InitImplicitExtensionPermissions(const Extension* extension);
+
+ // Initializes the effective host permission based on the data in this set.
+ void InitEffectiveHosts();
+
+ // Gets the permission messages for the API permissions.
+ std::set<ExtensionPermissionMessage> GetSimplePermissionMessages() const;
+
+ // Returns true if |permissions| has an elevated API privilege level than
+ // this set.
+ bool HasLessAPIPrivilegesThan(
+ const ExtensionPermissionSet* permissions) const;
+
+ // Returns true if |permissions| has more host permissions compared to this
+ // set.
+ bool HasLessHostPrivilegesThan(
+ const ExtensionPermissionSet* permissions) const;
+
+ // The api list is used when deciding if an extension can access certain
+ // extension APIs and features.
+ ExtensionAPIPermissionSet apis_;
+
+ // The list of hosts that can be accessed directly from the extension.
+ URLPatternSet explicit_hosts_;
+
+ // The list of hosts that can be scripted by content scripts.
+ URLPatternSet scriptable_hosts_;
+
+ // The list of hosts this effectively grants access to.
+ URLPatternSet effective_hosts_;
+};
+
+#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_PERMISSION_SET_H_
diff --git a/chrome/common/extensions/extension_permission_set_unittest.cc b/chrome/common/extensions/extension_permission_set_unittest.cc
new file mode 100644
index 0000000..9cfa698
--- /dev/null
+++ b/chrome/common/extensions/extension_permission_set_unittest.cc
@@ -0,0 +1,941 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/extensions/extension_permission_set.h"
+
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/extension.h"
+#include "content/common/json_value_serializer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+static scoped_refptr<Extension> LoadManifest(const std::string& dir,
+ const std::string& test_file,
+ int extra_flags) {
+ FilePath path;
+ PathService::Get(chrome::DIR_TEST_DATA, &path);
+ path = path.AppendASCII("extensions")
+ .AppendASCII(dir)
+ .AppendASCII(test_file);
+
+ JSONFileValueSerializer serializer(path);
+ std::string error;
+ scoped_ptr<Value> result(serializer.Deserialize(NULL, &error));
+ if (!result.get()) {
+ EXPECT_EQ("", error);
+ return NULL;
+ }
+
+ scoped_refptr<Extension> extension = Extension::Create(
+ path.DirName(), Extension::INVALID,
+ *static_cast<DictionaryValue*>(result.get()),
+ Extension::STRICT_ERROR_CHECKS | extra_flags, &error);
+ EXPECT_TRUE(extension) << error;
+ return extension;
+}
+
+static scoped_refptr<Extension> LoadManifest(const std::string& dir,
+ const std::string& test_file) {
+ return LoadManifest(dir, test_file, Extension::NO_FLAGS);
+}
+
+void CompareLists(const std::vector<std::string>& expected,
+ const std::vector<std::string>& actual) {
+ ASSERT_EQ(expected.size(), actual.size());
+
+ for (size_t i = 0; i < expected.size(); ++i) {
+ EXPECT_EQ(expected[i], actual[i]);
+ }
+}
+
+static void AddPattern(URLPatternSet* extent, const std::string& pattern) {
+ int schemes = URLPattern::SCHEME_ALL;
+ extent->AddPattern(URLPattern(schemes, pattern));
+}
+
+static void AssertEqualExtents(const URLPatternSet& extent1,
+ const URLPatternSet& extent2) {
+ URLPatternList patterns1 = extent1.patterns();
+ URLPatternList patterns2 = extent2.patterns();
+ std::set<std::string> strings1;
+ EXPECT_EQ(patterns1.size(), patterns2.size());
+
+ for (size_t i = 0; i < patterns1.size(); ++i)
+ strings1.insert(patterns1.at(i).GetAsString());
+
+ std::set<std::string> strings2;
+ for (size_t i = 0; i < patterns2.size(); ++i)
+ strings2.insert(patterns2.at(i).GetAsString());
+
+ EXPECT_EQ(strings1, strings2);
+}
+
+} // namespace
+
+class ExtensionAPIPermissionTest : public testing::Test {
+};
+
+class ExtensionPermissionSetTest : public testing::Test {
+};
+
+
+// Tests GetByID.
+TEST(ExtensionPermissionsInfoTest, GetByID) {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ ExtensionAPIPermissionSet ids = info->GetAll();
+ for (ExtensionAPIPermissionSet::iterator i = ids.begin();
+ i != ids.end(); ++i) {
+ EXPECT_EQ(*i, info->GetByID(*i)->id());
+ }
+}
+
+// Tests that GetByName works with normal permission names and aliases.
+TEST(ExtensionPermissionsInfoTest, GetByName) {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ EXPECT_EQ(ExtensionAPIPermission::kTab, info->GetByName("tabs")->id());
+ EXPECT_EQ(ExtensionAPIPermission::kManagement,
+ info->GetByName("management")->id());
+ EXPECT_FALSE(info->GetByName("alsdkfjasldkfj"));
+}
+
+TEST(ExtensionPermissionsInfoTest, GetAll) {
+ size_t count = 0;
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ ExtensionAPIPermissionSet apis = info->GetAll();
+ for (ExtensionAPIPermissionSet::iterator api = apis.begin();
+ api != apis.end(); ++api) {
+ // Make sure only the valid permission IDs get returned.
+ EXPECT_NE(ExtensionAPIPermission::kInvalid, *api);
+ EXPECT_NE(ExtensionAPIPermission::kUnknown, *api);
+ count++;
+ }
+ EXPECT_EQ(count, info->get_permission_count());
+}
+
+TEST(ExtensionPermissionInfoTest, GetAllByName) {
+ std::set<std::string> names;
+ names.insert("background");
+ names.insert("management");
+
+ // This is an alias of kTab
+ names.insert("windows");
+
+ // This unknown name should get dropped.
+ names.insert("sdlkfjasdlkfj");
+
+ ExtensionAPIPermissionSet expected;
+ expected.insert(ExtensionAPIPermission::kBackground);
+ expected.insert(ExtensionAPIPermission::kManagement);
+ expected.insert(ExtensionAPIPermission::kTab);
+
+ EXPECT_EQ(expected,
+ ExtensionPermissionsInfo::GetInstance()->GetAllByName(names));
+}
+
+// Tests that the aliases are properly mapped.
+TEST(ExtensionAPIPermissionTest, Aliases) {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ // tabs: tabs, windows
+ std::string tabs_name = "tabs";
+ EXPECT_EQ(tabs_name, info->GetByID(ExtensionAPIPermission::kTab)->name());
+ EXPECT_EQ(ExtensionAPIPermission::kTab, info->GetByName("tabs")->id());
+ EXPECT_EQ(ExtensionAPIPermission::kTab, info->GetByName("windows")->id());
+
+ // unlimitedStorage: unlimitedStorage, unlimited_storage
+ std::string storage_name = "unlimitedStorage";
+ EXPECT_EQ(storage_name, info->GetByID(
+ ExtensionAPIPermission::kUnlimitedStorage)->name());
+ EXPECT_EQ(ExtensionAPIPermission::kUnlimitedStorage,
+ info->GetByName("unlimitedStorage")->id());
+ EXPECT_EQ(ExtensionAPIPermission::kUnlimitedStorage,
+ info->GetByName("unlimited_storage")->id());
+}
+
+TEST(ExtensionAPIPermissionTest, HostedAppPermissions) {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ ExtensionAPIPermissionSet hosted_perms;
+ hosted_perms.insert(ExtensionAPIPermission::kBackground);
+ hosted_perms.insert(ExtensionAPIPermission::kClipboardRead);
+ hosted_perms.insert(ExtensionAPIPermission::kClipboardWrite);
+ hosted_perms.insert(ExtensionAPIPermission::kChromePrivate);
+ hosted_perms.insert(ExtensionAPIPermission::kExperimental);
+ hosted_perms.insert(ExtensionAPIPermission::kGeolocation);
+ hosted_perms.insert(ExtensionAPIPermission::kNotification);
+ hosted_perms.insert(ExtensionAPIPermission::kUnlimitedStorage);
+ hosted_perms.insert(ExtensionAPIPermission::kWebstorePrivate);
+
+ ExtensionAPIPermissionSet perms = info->GetAll();
+ size_t count = 0;
+ for (ExtensionAPIPermissionSet::iterator i = perms.begin();
+ i != perms.end(); ++i) {
+ count += hosted_perms.count(*i);
+ EXPECT_EQ(hosted_perms.count(*i) > 0, info->GetByID(*i)->is_hosted_app());
+ }
+
+ EXPECT_EQ(9u, count);
+ EXPECT_EQ(9u, info->get_hosted_app_permission_count());
+}
+
+TEST(ExtensionAPIPermissionTest, ComponentOnlyPermissions) {
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ ExtensionAPIPermissionSet private_perms;
+ private_perms.insert(ExtensionAPIPermission::kChromeosInfoPrivate);
+ private_perms.insert(ExtensionAPIPermission::kFileBrowserPrivate);
+ private_perms.insert(ExtensionAPIPermission::kMediaPlayerPrivate);
+ private_perms.insert(ExtensionAPIPermission::kWebstorePrivate);
+
+ ExtensionAPIPermissionSet perms = info->GetAll();
+ int count = 0;
+ for (ExtensionAPIPermissionSet::iterator i = perms.begin();
+ i != perms.end(); ++i) {
+ count += private_perms.count(*i);
+ EXPECT_EQ(private_perms.count(*i) > 0,
+ info->GetByID(*i)->is_component_only());
+ }
+
+ EXPECT_EQ(4, count);
+}
+
+TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) {
+ scoped_refptr<Extension> extension;
+ const ExtensionPermissionSet* permissions = NULL;
+
+ extension = LoadManifest("effective_host_permissions", "empty.json");
+ permissions = extension->permission_set();
+ EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().patterns().size());
+ EXPECT_FALSE(permissions->HasEffectiveAccessToURL(
+ GURL("http://www.google.com")));
+ EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
+
+ extension = LoadManifest("effective_host_permissions", "one_host.json");
+ permissions = extension->permission_set();
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
+ GURL("http://www.google.com")));
+ EXPECT_FALSE(permissions->HasEffectiveAccessToURL(
+ GURL("https://www.google.com")));
+ EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
+
+ extension = LoadManifest("effective_host_permissions",
+ "one_host_wildcard.json");
+ permissions = extension->permission_set();
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
+ GURL("http://foo.google.com")));
+ EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
+
+ extension = LoadManifest("effective_host_permissions", "two_hosts.json");
+ permissions = extension->permission_set();
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
+ GURL("http://www.google.com")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
+ GURL("http://www.reddit.com")));
+ EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
+
+ extension = LoadManifest("effective_host_permissions",
+ "https_not_considered.json");
+ permissions = extension->permission_set();
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://google.com")));
+ EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
+
+ extension = LoadManifest("effective_host_permissions",
+ "two_content_scripts.json");
+ permissions = extension->permission_set();
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
+ GURL("http://www.reddit.com")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(
+ GURL("http://news.ycombinator.com")));
+ EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts());
+
+ extension = LoadManifest("effective_host_permissions", "all_hosts.json");
+ permissions = extension->permission_set();
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
+ EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("https://test/")));
+ EXPECT_TRUE(
+ permissions->HasEffectiveAccessToURL(GURL("http://www.google.com")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts());
+
+ extension = LoadManifest("effective_host_permissions", "all_hosts2.json");
+ permissions = extension->permission_set();
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
+ EXPECT_TRUE(
+ permissions->HasEffectiveAccessToURL(GURL("http://www.google.com")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts());
+
+ extension = LoadManifest("effective_host_permissions", "all_hosts3.json");
+ permissions = extension->permission_set();
+ EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("http://test/")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://test/")));
+ EXPECT_TRUE(
+ permissions->HasEffectiveAccessToURL(GURL("http://www.google.com")));
+ EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts());
+}
+
+TEST(ExtensionPermissionSetTest, ExplicitAccessToOrigin) {
+ ExtensionAPIPermissionSet apis;
+ URLPatternSet explicit_hosts;
+ URLPatternSet scriptable_hosts;
+
+ AddPattern(&explicit_hosts, "http://*.google.com/*");
+ // The explicit host paths should get set to /*.
+ AddPattern(&explicit_hosts, "http://www.example.com/a/particular/path/*");
+
+ ExtensionPermissionSet perm_set(apis, explicit_hosts, scriptable_hosts);
+ ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin(
+ GURL("http://www.google.com/")));
+ ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin(
+ GURL("http://test.google.com/")));
+ ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin(
+ GURL("http://www.example.com")));
+ ASSERT_TRUE(perm_set.HasEffectiveAccessToURL(
+ GURL("http://www.example.com")));
+ ASSERT_FALSE(perm_set.HasExplicitAccessToOrigin(
+ GURL("http://test.example.com")));
+}
+
+TEST(ExtensionPermissionSetTest, CreateUnion) {
+ ExtensionAPIPermissionSet apis1;
+ ExtensionAPIPermissionSet apis2;
+ ExtensionAPIPermissionSet expected_apis;
+
+ URLPatternSet explicit_hosts1;
+ URLPatternSet explicit_hosts2;
+ URLPatternSet expected_explicit_hosts;
+
+ URLPatternSet scriptable_hosts1;
+ URLPatternSet scriptable_hosts2;
+ URLPatternSet expected_scriptable_hosts;
+
+ URLPatternSet effective_hosts;
+
+ scoped_ptr<ExtensionPermissionSet> set1;
+ scoped_ptr<ExtensionPermissionSet> set2;
+ scoped_ptr<ExtensionPermissionSet> union_set;
+
+ // Union with an empty set.
+ apis1.insert(ExtensionAPIPermission::kTab);
+ apis1.insert(ExtensionAPIPermission::kBackground);
+ expected_apis.insert(ExtensionAPIPermission::kTab);
+ expected_apis.insert(ExtensionAPIPermission::kBackground);
+
+ AddPattern(&explicit_hosts1, "http://*.google.com/*");
+ AddPattern(&expected_explicit_hosts, "http://*.google.com/*");
+ AddPattern(&effective_hosts, "http://*.google.com/*");
+
+ set1.reset(new ExtensionPermissionSet(
+ apis1, explicit_hosts1, scriptable_hosts1));
+ set2.reset(new ExtensionPermissionSet(
+ apis2, explicit_hosts2, scriptable_hosts2));
+ union_set.reset(ExtensionPermissionSet::CreateUnion(set1.get(), set2.get()));
+
+ EXPECT_FALSE(union_set->HasEffectiveFullAccess());
+ EXPECT_EQ(expected_apis, union_set->apis());
+ AssertEqualExtents(expected_explicit_hosts, union_set->explicit_hosts());
+ AssertEqualExtents(expected_scriptable_hosts, union_set->scriptable_hosts());
+ AssertEqualExtents(expected_explicit_hosts, union_set->effective_hosts());
+
+ // Now use a real second set.
+ apis2.insert(ExtensionAPIPermission::kTab);
+ apis2.insert(ExtensionAPIPermission::kProxy);
+ apis2.insert(ExtensionAPIPermission::kClipboardWrite);
+ apis2.insert(ExtensionAPIPermission::kPlugin);
+ expected_apis.insert(ExtensionAPIPermission::kTab);
+ expected_apis.insert(ExtensionAPIPermission::kProxy);
+ expected_apis.insert(ExtensionAPIPermission::kClipboardWrite);
+ expected_apis.insert(ExtensionAPIPermission::kPlugin);
+
+ AddPattern(&explicit_hosts2, "http://*.example.com/*");
+ AddPattern(&scriptable_hosts2, "http://*.google.com/*");
+ AddPattern(&expected_explicit_hosts, "http://*.example.com/*");
+ AddPattern(&expected_scriptable_hosts, "http://*.google.com/*");
+
+ effective_hosts.ClearPatterns();
+ AddPattern(&effective_hosts, "<all_urls>");
+
+ set2.reset(new ExtensionPermissionSet(
+ apis2, explicit_hosts2, scriptable_hosts2));
+ union_set.reset(ExtensionPermissionSet::CreateUnion(set1.get(), set2.get()));
+ EXPECT_TRUE(union_set->HasEffectiveFullAccess());
+ EXPECT_TRUE(union_set->HasEffectiveAccessToAllHosts());
+ EXPECT_EQ(expected_apis, union_set->apis());
+ AssertEqualExtents(expected_explicit_hosts, union_set->explicit_hosts());
+ AssertEqualExtents(expected_scriptable_hosts, union_set->scriptable_hosts());
+ AssertEqualExtents(effective_hosts, union_set->effective_hosts());
+}
+
+TEST(ExtensionPermissionSetTest, HasLessPrivilegesThan) {
+ const struct {
+ const char* base_name;
+ // Increase these sizes if you have more than 10.
+ const char* granted_apis[10];
+ const char* granted_hosts[10];
+ bool full_access;
+ bool expect_increase;
+ } kTests[] = {
+ { "allhosts1", {NULL}, {"http://*/", NULL}, false,
+ false }, // all -> all
+ { "allhosts2", {NULL}, {"http://*/", NULL}, false,
+ false }, // all -> one
+ { "allhosts3", {NULL}, {NULL}, false, true }, // one -> all
+ { "hosts1", {NULL},
+ {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
+ false }, // http://a,http://b -> http://a,http://b
+ { "hosts2", {NULL},
+ {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
+ true }, // http://a,http://b -> https://a,http://*.b
+ { "hosts3", {NULL},
+ {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
+ false }, // http://a,http://b -> http://a
+ { "hosts4", {NULL},
+ {"http://www.google.com/", NULL}, false,
+ true }, // http://a -> http://a,http://b
+ { "hosts5", {"tabs", "notifications", NULL},
+ {"http://*.example.com/", "http://*.example.com/*",
+ "http://*.example.co.uk/*", "http://*.example.com.au/*",
+ NULL}, false,
+ false }, // http://a,b,c -> http://a,b,c + https://a,b,c
+ { "hosts6", {"tabs", "notifications", NULL},
+ {"http://*.example.com/", "http://*.example.com/*", NULL}, false,
+ false }, // http://a.com -> http://a.com + http://a.co.uk
+ { "permissions1", {"tabs", NULL},
+ {NULL}, false, false }, // tabs -> tabs
+ { "permissions2", {"tabs", NULL},
+ {NULL}, false, true }, // tabs -> tabs,bookmarks
+ { "permissions3", {NULL},
+ {"http://*/*", NULL},
+ false, true }, // http://a -> http://a,tabs
+ { "permissions5", {"bookmarks", NULL},
+ {NULL}, false, true }, // bookmarks -> bookmarks,history
+#if !defined(OS_CHROMEOS) // plugins aren't allowed in ChromeOS
+ { "permissions4", {NULL},
+ {NULL}, true, false }, // plugin -> plugin,tabs
+ { "plugin1", {NULL},
+ {NULL}, true, false }, // plugin -> plugin
+ { "plugin2", {NULL},
+ {NULL}, true, false }, // plugin -> none
+ { "plugin3", {NULL},
+ {NULL}, false, true }, // none -> plugin
+#endif
+ { "storage", {NULL},
+ {NULL}, false, false }, // none -> storage
+ { "notifications", {NULL},
+ {NULL}, false, false } // none -> notifications
+ };
+
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
+ scoped_refptr<Extension> old_extension(
+ LoadManifest("allow_silent_upgrade",
+ std::string(kTests[i].base_name) + "_old.json"));
+ scoped_refptr<Extension> new_extension(
+ LoadManifest("allow_silent_upgrade",
+ std::string(kTests[i].base_name) + "_new.json"));
+
+ ExtensionAPIPermissionSet granted_apis;
+ for (size_t j = 0; kTests[i].granted_apis[j] != NULL; ++j) {
+ granted_apis.insert(info->GetByName(kTests[i].granted_apis[j])->id());
+ }
+
+ URLPatternSet granted_hosts;
+ for (size_t j = 0; kTests[i].granted_hosts[j] != NULL; ++j)
+ AddPattern(&granted_hosts, kTests[i].granted_hosts[j]);
+
+ EXPECT_TRUE(new_extension.get()) << kTests[i].base_name << "_new.json";
+ if (!new_extension.get())
+ continue;
+
+ const ExtensionPermissionSet* old_p = old_extension->permission_set();
+ const ExtensionPermissionSet* new_p = new_extension->permission_set();
+
+ EXPECT_EQ(kTests[i].expect_increase, old_p->HasLessPrivilegesThan(new_p))
+ << kTests[i].base_name;
+ }
+}
+
+TEST(ExtensionPermissionSetTest, PermissionMessages) {
+ // Ensure that all permissions that needs to show install UI actually have
+ // strings associated with them.
+ ExtensionAPIPermissionSet skip;
+
+ skip.insert(ExtensionAPIPermission::kDefault);
+
+ // These are considered "nuisance" or "trivial" permissions that don't need
+ // a prompt.
+ skip.insert(ExtensionAPIPermission::kContextMenus);
+ skip.insert(ExtensionAPIPermission::kIdle);
+ skip.insert(ExtensionAPIPermission::kNotification);
+ skip.insert(ExtensionAPIPermission::kUnlimitedStorage);
+ skip.insert(ExtensionAPIPermission::kContentSettings);
+
+ // TODO(erikkay) add a string for this permission.
+ skip.insert(ExtensionAPIPermission::kBackground);
+
+ skip.insert(ExtensionAPIPermission::kClipboardWrite);
+
+ // The cookie permission does nothing unless you have associated host
+ // permissions.
+ skip.insert(ExtensionAPIPermission::kCookie);
+
+ // The proxy permission is warned as part of host permission checks.
+ skip.insert(ExtensionAPIPermission::kProxy);
+
+ // This permission requires explicit user action (context menu handler)
+ // so we won't prompt for it for now.
+ skip.insert(ExtensionAPIPermission::kFileBrowserHandler);
+
+ // If you've turned on the experimental command-line flag, we don't need
+ // to warn you further.
+ skip.insert(ExtensionAPIPermission::kExperimental);
+
+ // These are private.
+ skip.insert(ExtensionAPIPermission::kWebstorePrivate);
+ skip.insert(ExtensionAPIPermission::kFileBrowserPrivate);
+ skip.insert(ExtensionAPIPermission::kMediaPlayerPrivate);
+ skip.insert(ExtensionAPIPermission::kChromePrivate);
+ skip.insert(ExtensionAPIPermission::kChromeosInfoPrivate);
+ skip.insert(ExtensionAPIPermission::kWebSocketProxyPrivate);
+
+ // Warned as part of host permissions.
+ skip.insert(ExtensionAPIPermission::kDevtools);
+ ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance();
+ ExtensionAPIPermissionSet permissions = info->GetAll();
+ for (ExtensionAPIPermissionSet::const_iterator i = permissions.begin();
+ i != permissions.end(); ++i) {
+ ExtensionAPIPermission* permission = info->GetByID(*i);
+ EXPECT_TRUE(permission);
+ if (skip.count(*i)) {
+ EXPECT_EQ(ExtensionPermissionMessage::kNone, permission->message_id())
+ << "unexpected message_id for " << permission->name();
+ } else {
+ EXPECT_NE(ExtensionPermissionMessage::kNone, permission->message_id())
+ << "missing message_id for " << permission->name();
+ }
+ }
+}
+
+// Tests the default permissions (empty API permission set).
+TEST(ExtensionPermissionSetTest, DefaultFunctionAccess) {
+ const struct {
+ const char* permission_name;
+ bool expect_success;
+ } kTests[] = {
+ // Negative test.
+ { "non_existing_permission", false },
+ // Test default module/package permission.
+ { "browserAction", true },
+ { "browserActions", true },
+ { "devtools", true },
+ { "extension", true },
+ { "i18n", true },
+ { "pageAction", true },
+ { "pageActions", true },
+ { "test", true },
+ // Some negative tests.
+ { "bookmarks", false },
+ { "cookies", false },
+ { "history", false },
+ { "tabs.onUpdated", false },
+ // Make sure we find the module name after stripping '.' and '/'.
+ { "browserAction/abcd/onClick", true },
+ { "browserAction.abcd.onClick", true },
+ // Test Tabs functions.
+ { "tabs.create", true},
+ { "tabs.update", true},
+ { "tabs.getSelected", false},
+ };
+
+ ExtensionPermissionSet permissions;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
+ EXPECT_EQ(kTests[i].expect_success,
+ permissions.HasAccessToFunction(kTests[i].permission_name));
+ }
+}
+
+TEST(ExtensionPermissionSetTest, GetWarningMessages_ManyHosts) {
+ scoped_refptr<Extension> extension;
+
+ extension = LoadManifest("permissions", "many-hosts.json");
+ std::vector<string16> warnings =
+ extension->permission_set()->GetWarningMessages();
+ ASSERT_EQ(1u, warnings.size());
+ EXPECT_EQ("Your data on www.google.com and encrypted.google.com",
+ UTF16ToUTF8(warnings[0]));
+}
+
+TEST(ExtensionPermissionSetTest, GetWarningMessages_Plugins) {
+ scoped_refptr<Extension> extension;
+ scoped_ptr<ExtensionPermissionSet> permissions;
+
+ extension = LoadManifest("permissions", "plugins.json");
+ std::vector<string16> warnings =
+ extension->permission_set()->GetWarningMessages();
+ // We don't parse the plugins key on Chrome OS, so it should not ask for any
+ // permissions.
+#if defined(OS_CHROMEOS)
+ ASSERT_EQ(0u, warnings.size());
+#else
+ ASSERT_EQ(1u, warnings.size());
+ EXPECT_EQ("All data on your computer and the websites you visit",
+ UTF16ToUTF8(warnings[0]));
+#endif
+}
+
+TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) {
+ scoped_ptr<ExtensionPermissionSet> perm_set;
+ ExtensionAPIPermissionSet empty_perms;
+ std::vector<std::string> expected;
+ expected.push_back("www.foo.com");
+ expected.push_back("www.bar.com");
+ expected.push_back("www.baz.com");
+ URLPatternSet explicit_hosts;
+ URLPatternSet scriptable_hosts;
+
+ {
+ SCOPED_TRACE("no dupes");
+
+ // Simple list with no dupes.
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+ }
+
+ {
+ SCOPED_TRACE("two dupes");
+
+ // Add some dupes.
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+ }
+
+ {
+ SCOPED_TRACE("schemes differ");
+
+ // Add a pattern that differs only by scheme. This should be filtered out.
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path"));
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+ }
+
+ {
+ SCOPED_TRACE("paths differ");
+
+ // Add some dupes by path.
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath"));
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+ }
+
+ {
+ SCOPED_TRACE("subdomains differ");
+
+ // We don't do anything special for subdomains.
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://monkey.www.bar.com/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://bar.com/path"));
+
+ expected.push_back("monkey.www.bar.com");
+ expected.push_back("bar.com");
+
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+ }
+
+ {
+ SCOPED_TRACE("RCDs differ");
+
+ // Now test for RCD uniquing.
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.de/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca.us/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com.my/path"));
+
+ // This is an unknown RCD, which shouldn't be uniqued out.
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));
+ // But it should only occur once.
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));
+
+ expected.push_back("www.foo.xyzzy");
+
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+ }
+
+ {
+ SCOPED_TRACE("wildcards");
+
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*"));
+
+ expected.push_back("*.google.com");
+
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+ }
+
+ {
+ SCOPED_TRACE("scriptable hosts");
+ explicit_hosts.ClearPatterns();
+ scriptable_hosts.ClearPatterns();
+ expected.clear();
+
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*"));
+ scriptable_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://*.example.com/*"));
+
+ expected.push_back("*.google.com");
+ expected.push_back("*.example.com");
+
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+ }
+}
+
+TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_ComIsBestRcd) {
+ scoped_ptr<ExtensionPermissionSet> perm_set;
+ ExtensionAPIPermissionSet empty_perms;
+ URLPatternSet explicit_hosts;
+ URLPatternSet scriptable_hosts;
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
+
+ std::vector<std::string> expected;
+ expected.push_back("www.foo.com");
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+}
+
+TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) {
+ scoped_ptr<ExtensionPermissionSet> perm_set;
+ ExtensionAPIPermissionSet empty_perms;
+ URLPatternSet explicit_hosts;
+ URLPatternSet scriptable_hosts;
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
+ // No http://www.foo.com/path
+
+ std::vector<std::string> expected;
+ expected.push_back("www.foo.net");
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+}
+
+TEST(ExtensionPermissionSetTest,
+ GetDistinctHostsForDisplay_OrgIs3rdBestRcd) {
+ scoped_ptr<ExtensionPermissionSet> perm_set;
+ ExtensionAPIPermissionSet empty_perms;
+ URLPatternSet explicit_hosts;
+ URLPatternSet scriptable_hosts;
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
+ // No http://www.foo.net/path
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
+ // No http://www.foo.com/path
+
+ std::vector<std::string> expected;
+ expected.push_back("www.foo.org");
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+}
+
+TEST(ExtensionPermissionSetTest,
+ GetDistinctHostsForDisplay_FirstInListIs4thBestRcd) {
+ scoped_ptr<ExtensionPermissionSet> perm_set;
+ ExtensionAPIPermissionSet empty_perms;
+ URLPatternSet explicit_hosts;
+ URLPatternSet scriptable_hosts;
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
+ // No http://www.foo.org/path
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
+ // No http://www.foo.net/path
+ explicit_hosts.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
+ // No http://www.foo.com/path
+
+ std::vector<std::string> expected;
+ expected.push_back("www.foo.ca");
+ perm_set.reset(new ExtensionPermissionSet(
+ empty_perms, explicit_hosts, scriptable_hosts));
+ CompareLists(expected, perm_set->GetDistinctHostsForDisplay());
+}
+
+TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) {
+ URLPatternSet elist1;
+ URLPatternSet elist2;
+ URLPatternSet slist1;
+ URLPatternSet slist2;
+ scoped_ptr<ExtensionPermissionSet> set1;
+ scoped_ptr<ExtensionPermissionSet> set2;
+ ExtensionAPIPermissionSet empty_perms;
+ elist1.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
+ elist1.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
+
+ // Test that the host order does not matter.
+ elist2.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
+ elist2.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
+
+ set1.reset(new ExtensionPermissionSet(empty_perms, elist1, slist1));
+ set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+
+ EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get()));
+ EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
+
+ // Test that paths are ignored.
+ elist2.ClearPatterns();
+ elist2.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*"));
+ set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get()));
+ EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
+
+ // Test that RCDs are ignored.
+ elist2.ClearPatterns();
+ elist2.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*"));
+ set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get()));
+ EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
+
+ // Test that subdomain wildcards are handled properly.
+ elist2.ClearPatterns();
+ elist2.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*"));
+ set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get()));
+ //TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337
+ //EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
+
+ // Test that different domains count as different hosts.
+ elist2.ClearPatterns();
+ elist2.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
+ elist2.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path"));
+ set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get()));
+ EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get()));
+
+ // Test that different subdomains count as different hosts.
+ elist2.ClearPatterns();
+ elist2.AddPattern(
+ URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*"));
+ set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2));
+ EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get()));
+ EXPECT_TRUE(set2->HasLessHostPrivilegesThan(set1.get()));
+}
+
+TEST(ExtensionPermissionSetTest, GetAPIsAsStrings) {
+ ExtensionAPIPermissionSet apis;
+ URLPatternSet empty_set;
+
+ apis.insert(ExtensionAPIPermission::kProxy);
+ apis.insert(ExtensionAPIPermission::kBackground);
+ apis.insert(ExtensionAPIPermission::kNotification);
+ apis.insert(ExtensionAPIPermission::kTab);
+
+ ExtensionPermissionSet perm_set(apis, empty_set, empty_set);
+ std::set<std::string> api_names = perm_set.GetAPIsAsStrings();
+
+ // The result is correct if it has the same number of elements
+ // and we can convert it back to the id set.
+ EXPECT_EQ(4u, api_names.size());
+ EXPECT_EQ(apis,
+ ExtensionPermissionsInfo::GetInstance()->GetAllByName(api_names));
+}
+
+TEST(ExtensionPermissionSetTest, IsEmpty) {
+ ExtensionAPIPermissionSet empty_apis;
+ URLPatternSet empty_extent;
+
+ ExtensionPermissionSet perm_set;
+ EXPECT_TRUE(perm_set.IsEmpty());
+
+ perm_set = ExtensionPermissionSet(empty_apis, empty_extent, empty_extent);
+ EXPECT_TRUE(perm_set.IsEmpty());
+
+ ExtensionAPIPermissionSet non_empty_apis;
+ non_empty_apis.insert(ExtensionAPIPermission::kBackground);
+ perm_set = ExtensionPermissionSet(
+ non_empty_apis, empty_extent, empty_extent);
+ EXPECT_FALSE(perm_set.IsEmpty());
+
+ // Try non standard host
+ URLPatternSet non_empty_extent;
+ AddPattern(&non_empty_extent, "http://www.google.com/*");
+
+ perm_set = ExtensionPermissionSet(
+ empty_apis, non_empty_extent, empty_extent);
+ EXPECT_FALSE(perm_set.IsEmpty());
+
+ perm_set = ExtensionPermissionSet(
+ empty_apis, empty_extent, non_empty_extent);
+ EXPECT_FALSE(perm_set.IsEmpty());
+}
diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc
index c74f6f8..aa8555c 100644
--- a/chrome/common/extensions/extension_unittest.cc
+++ b/chrome/common/extensions/extension_unittest.cc
@@ -42,11 +42,6 @@ void CompareLists(const std::vector<std::string>& expected,
}
}
-static void AddPattern(URLPatternSet* extent, const std::string& pattern) {
- int schemes = URLPattern::SCHEME_ALL;
- extent->AddPattern(URLPattern(schemes, pattern));
-}
-
static scoped_refptr<Extension> LoadManifestUnchecked(
const std::string& dir,
const std::string& test_file,
@@ -394,7 +389,11 @@ TEST(ExtensionTest, EffectiveHostPermissions) {
hosts = extension->GetEffectiveHostPermissions();
EXPECT_TRUE(hosts.MatchesURL(GURL("http://google.com")));
EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.reddit.com")));
+ EXPECT_TRUE(extension->permission_set()->HasEffectiveAccessToURL(
+ GURL("http://www.reddit.com")));
EXPECT_TRUE(hosts.MatchesURL(GURL("http://news.ycombinator.com")));
+ EXPECT_TRUE(extension->permission_set()->HasEffectiveAccessToURL(
+ GURL("http://news.ycombinator.com")));
EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts());
extension = LoadManifest("effective_host_permissions", "all_hosts.json");
@@ -418,151 +417,6 @@ TEST(ExtensionTest, EffectiveHostPermissions) {
EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts());
}
-TEST(ExtensionTest, IsPrivilegeIncrease) {
- const struct {
- const char* base_name;
- // Increase these sizes if you have more than 10.
- const char* granted_apis[10];
- const char* granted_hosts[10];
- bool full_access;
- bool expect_increase;
- } kTests[] = {
- { "allhosts1", {NULL}, {"http://*/", NULL}, false,
- false }, // all -> all
- { "allhosts2", {NULL}, {"http://*/", NULL}, false,
- false }, // all -> one
- { "allhosts3", {NULL}, {NULL}, false, true }, // one -> all
- { "hosts1", {NULL},
- {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
- false }, // http://a,http://b -> http://a,http://b
- { "hosts2", {NULL},
- {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
- true }, // http://a,http://b -> https://a,http://*.b
- { "hosts3", {NULL},
- {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
- false }, // http://a,http://b -> http://a
- { "hosts4", {NULL},
- {"http://www.google.com/", NULL}, false,
- true }, // http://a -> http://a,http://b
- { "hosts5", {"tabs", "notifications", NULL},
- {"http://*.example.com/", "http://*.example.com/*",
- "http://*.example.co.uk/*", "http://*.example.com.au/*",
- NULL}, false,
- false }, // http://a,b,c -> http://a,b,c + https://a,b,c
- { "hosts6", {"tabs", "notifications", NULL},
- {"http://*.example.com/", "http://*.example.com/*", NULL}, false,
- false }, // http://a.com -> http://a.com + http://a.co.uk
- { "permissions1", {"tabs", NULL},
- {NULL}, false, false }, // tabs -> tabs
- { "permissions2", {"tabs", NULL},
- {NULL}, false, true }, // tabs -> tabs,bookmarks
- { "permissions3", {NULL},
- {"http://*/*", NULL},
- false, true }, // http://a -> http://a,tabs
- { "permissions5", {"bookmarks", NULL},
- {NULL}, false, true }, // bookmarks -> bookmarks,history
-#if !defined(OS_CHROMEOS) // plugins aren't allowed in ChromeOS
- { "permissions4", {NULL},
- {NULL}, true, false }, // plugin -> plugin,tabs
- { "plugin1", {NULL},
- {NULL}, true, false }, // plugin -> plugin
- { "plugin2", {NULL},
- {NULL}, true, false }, // plugin -> none
- { "plugin3", {NULL},
- {NULL}, false, true }, // none -> plugin
-#endif
- { "storage", {NULL},
- {NULL}, false, false }, // none -> storage
- { "notifications", {NULL},
- {NULL}, false, false } // none -> notifications
- };
-
- for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
- scoped_refptr<Extension> old_extension(
- LoadManifest("allow_silent_upgrade",
- std::string(kTests[i].base_name) + "_old.json"));
- scoped_refptr<Extension> new_extension(
- LoadManifest("allow_silent_upgrade",
- std::string(kTests[i].base_name) + "_new.json"));
-
- std::set<std::string> granted_apis;
- for (size_t j = 0; kTests[i].granted_apis[j] != NULL; ++j)
- granted_apis.insert(kTests[i].granted_apis[j]);
-
- URLPatternSet granted_hosts;
- for (size_t j = 0; kTests[i].granted_hosts[j] != NULL; ++j)
- AddPattern(&granted_hosts, kTests[i].granted_hosts[j]);
-
- EXPECT_TRUE(new_extension.get()) << kTests[i].base_name << "_new.json";
- if (!new_extension.get())
- continue;
-
- EXPECT_EQ(kTests[i].expect_increase,
- Extension::IsPrivilegeIncrease(kTests[i].full_access,
- granted_apis,
- granted_hosts,
- new_extension.get()))
- << kTests[i].base_name;
- }
-}
-
-TEST(ExtensionTest, PermissionMessages) {
- // Ensure that all permissions that needs to show install UI actually have
- // strings associated with them.
-
- std::set<std::string> skip;
-
- // These are considered "nuisance" or "trivial" permissions that don't need
- // a prompt.
- skip.insert(Extension::kContextMenusPermission);
- skip.insert(Extension::kIdlePermission);
- skip.insert(Extension::kNotificationPermission);
- skip.insert(Extension::kUnlimitedStoragePermission);
- skip.insert(Extension::kContentSettingsPermission);
-
- // TODO(erikkay) add a string for this permission.
- skip.insert(Extension::kBackgroundPermission);
-
- skip.insert(Extension::kClipboardWritePermission);
-
- // The cookie permission does nothing unless you have associated host
- // permissions.
- skip.insert(Extension::kCookiePermission);
-
- // The proxy permission is warned as part of host permission checks.
- skip.insert(Extension::kProxyPermission);
-
- // This permission requires explicit user action (context menu handler)
- // so we won't prompt for it for now.
- skip.insert(Extension::kFileBrowserHandlerPermission);
-
- // If you've turned on the experimental command-line flag, we don't need
- // to warn you further.
- skip.insert(Extension::kExperimentalPermission);
-
- // These are private.
- skip.insert(Extension::kWebstorePrivatePermission);
- skip.insert(Extension::kFileBrowserPrivatePermission);
- skip.insert(Extension::kMediaPlayerPrivatePermission);
- skip.insert(Extension::kChromePrivatePermission);
- skip.insert(Extension::kChromeosInfoPrivatePermission);
- skip.insert(Extension::kWebSocketProxyPrivatePermission);
-
- const Extension::PermissionMessage::MessageId ID_NONE =
- Extension::PermissionMessage::ID_NONE;
-
- for (size_t i = 0; i < Extension::kNumPermissions; ++i) {
- Extension::Permission permission = Extension::kPermissions[i];
- if (skip.count(permission.name)) {
- EXPECT_EQ(ID_NONE, permission.message_id)
- << "unexpected message_id for " << permission.name;
- } else {
- EXPECT_NE(ID_NONE, permission.message_id)
- << "missing message_id for " << permission.name;
- }
- }
-}
-
// Returns a copy of |source| resized to |size| x |size|.
static SkBitmap ResizedCopy(const SkBitmap& source, int size) {
return skia::ImageOperations::Resize(source,
@@ -687,7 +541,7 @@ TEST(ExtensionTest, ApiPermissions) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
EXPECT_EQ(kTests[i].expect_success,
- extension->HasApiPermission(kTests[i].permission_name))
+ extension->HasAPIPermission(kTests[i].permission_name))
<< "Permission being tested: " << kTests[i].permission_name;
}
}
@@ -904,257 +758,6 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) {
EXPECT_FALSE(extension->HasHostPermission(settings_url));
}
-TEST(ExtensionTest, GetDistinctHostsForDisplay) {
- std::vector<std::string> expected;
- expected.push_back("www.foo.com");
- expected.push_back("www.bar.com");
- expected.push_back("www.baz.com");
- URLPatternList actual;
-
- {
- SCOPED_TRACE("no dupes");
-
- // Simple list with no dupes.
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
- }
-
- {
- SCOPED_TRACE("two dupes");
-
- // Add some dupes.
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path"));
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
- }
-
- {
- SCOPED_TRACE("schemes differ");
-
- // Add a pattern that differs only by scheme. This should be filtered out.
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path"));
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
- }
-
- {
- SCOPED_TRACE("paths differ");
-
- // Add some dupes by path.
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath"));
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
- }
-
- {
- SCOPED_TRACE("subdomains differ");
-
- // We don't do anything special for subdomains.
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://monkey.www.bar.com/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://bar.com/path"));
-
- expected.push_back("monkey.www.bar.com");
- expected.push_back("bar.com");
-
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
- }
-
- {
- SCOPED_TRACE("RCDs differ");
-
- // Now test for RCD uniquing.
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.de/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca.us/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com.my/path"));
-
- // This is an unknown RCD, which shouldn't be uniqued out.
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));
- // But it should only occur once.
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path"));
-
- expected.push_back("www.foo.xyzzy");
-
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
- }
-
- {
- SCOPED_TRACE("wildcards");
-
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*"));
-
- expected.push_back("*.google.com");
-
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
- }
-}
-
-TEST(ExtensionTest, GetDistinctHostsForDisplay_ComIsBestRcd) {
- URLPatternList actual;
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path"));
-
- std::vector<std::string> expected;
- expected.push_back("www.foo.com");
-
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
-}
-
-TEST(ExtensionTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) {
- URLPatternList actual;
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
- // No http://www.foo.com/path
-
- std::vector<std::string> expected;
- expected.push_back("www.foo.net");
-
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
-}
-
-TEST(ExtensionTest, GetDistinctHostsForDisplay_OrgIs3rdBestRcd) {
- URLPatternList actual;
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path"));
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
- // No http://www.foo.net/path
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
- // No http://www.foo.com/path
-
- std::vector<std::string> expected;
- expected.push_back("www.foo.org");
-
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
-}
-
-TEST(ExtensionTest, GetDistinctHostsForDisplay_FirstInListIs4thBestRcd) {
- URLPatternList actual;
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path"));
- // No http://www.foo.org/path
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path"));
- // No http://www.foo.net/path
- actual.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path"));
- // No http://www.foo.com/path
-
- std::vector<std::string> expected;
- expected.push_back("www.foo.ca");
-
- CompareLists(expected,
- Extension::GetDistinctHostsForDisplay(actual));
-}
-
-TEST(ExtensionTest, IsElevatedHostList) {
- URLPatternList list1;
- URLPatternList list2;
-
- list1.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
- list1.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
-
- // Test that the host order does not matter.
- list2.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
- list2.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path"));
-
- EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
- EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
-
- // Test that paths are ignored.
- list2.clear();
- list2.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*"));
- EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
- EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
-
- // Test that RCDs are ignored.
- list2.clear();
- list2.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*"));
- EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2));
- EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
-
- // Test that subdomain wildcards are handled properly.
- list2.clear();
- list2.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*"));
- EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
- //TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337
- //EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
-
- // Test that different domains count as different hosts.
- list2.clear();
- list2.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path"));
- list2.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path"));
- EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
- EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1));
-
- // Test that different subdomains count as different hosts.
- list2.clear();
- list2.push_back(
- URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*"));
- EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2));
- EXPECT_TRUE(Extension::IsElevatedHostList(list2, list1));
-}
-
TEST(ExtensionTest, GenerateId) {
std::string result;
EXPECT_TRUE(Extension::GenerateId("", &result));
diff --git a/chrome/common/extensions/url_pattern_set.cc b/chrome/common/extensions/url_pattern_set.cc
index c8374ef..65f8635 100644
--- a/chrome/common/extensions/url_pattern_set.cc
+++ b/chrome/common/extensions/url_pattern_set.cc
@@ -7,6 +7,22 @@
#include "chrome/common/extensions/url_pattern.h"
#include "googleurl/src/gurl.h"
+// static
+void URLPatternSet::CreateUnion(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out) {
+ const URLPatternList list1 = set1.patterns();
+ const URLPatternList list2 = set2.patterns();
+
+ out->ClearPatterns();
+
+ for (size_t i = 0; i < list1.size(); ++i)
+ out->AddPattern(list1.at(i));
+
+ for (size_t i = 0; i < list2.size(); ++i)
+ out->AddPattern(list2.at(i));
+}
+
URLPatternSet::URLPatternSet() {
}
diff --git a/chrome/common/extensions/url_pattern_set.h b/chrome/common/extensions/url_pattern_set.h
index ac50afc..2120e547 100644
--- a/chrome/common/extensions/url_pattern_set.h
+++ b/chrome/common/extensions/url_pattern_set.h
@@ -15,6 +15,12 @@ class GURL;
// Represents the set of URLs an extension uses for web content.
class URLPatternSet {
public:
+ // Clears |out| and populates the set with the union of |set1| and |set2|.
+ // NOTE: this does not discard duplicates.
+ static void CreateUnion(const URLPatternSet& set1,
+ const URLPatternSet& set2,
+ URLPatternSet* out);
+
URLPatternSet();
URLPatternSet(const URLPatternSet& rhs);
~URLPatternSet();
diff --git a/chrome/common/extensions/url_pattern_set_unittest.cc b/chrome/common/extensions/url_pattern_set_unittest.cc
index 5336410..8c05f2c 100644
--- a/chrome/common/extensions/url_pattern_set_unittest.cc
+++ b/chrome/common/extensions/url_pattern_set_unittest.cc
@@ -7,6 +7,27 @@
#include "googleurl/src/gurl.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace {
+
+static void AssertEqualExtents(const URLPatternSet& extent1,
+ const URLPatternSet& extent2) {
+ URLPatternList patterns1 = extent1.patterns();
+ URLPatternList patterns2 = extent2.patterns();
+ std::set<std::string> strings1;
+ EXPECT_EQ(patterns1.size(), patterns2.size());
+
+ for (size_t i = 0; i < patterns1.size(); ++i)
+ strings1.insert(patterns1.at(i).GetAsString());
+
+ std::set<std::string> strings2;
+ for (size_t i = 0; i < patterns2.size(); ++i)
+ strings2.insert(patterns2.at(i).GetAsString());
+
+ EXPECT_EQ(strings1, strings2);
+}
+
+} // namespace
+
static const int kAllSchemes =
URLPattern::SCHEME_HTTP |
URLPattern::SCHEME_HTTPS |
@@ -60,3 +81,35 @@ TEST(URLPatternSetTest, OverlapsWith) {
EXPECT_TRUE(extent1.OverlapsWith(extent3));
EXPECT_TRUE(extent3.OverlapsWith(extent1));
}
+
+TEST(URLPatternSetTest, CreateUnion) {
+ URLPatternSet empty_extent;
+
+ URLPatternSet extent1;
+ extent1.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
+ extent1.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*"));
+
+ URLPatternSet expected;
+ expected.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
+ expected.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*"));
+
+ // Union with an empty set.
+ URLPatternSet result;
+ URLPatternSet::CreateUnion(extent1, empty_extent, &result);
+ AssertEqualExtents(expected, result);
+
+ // Union with a real set (including a duplicate).
+ URLPatternSet extent2;
+ extent2.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*"));
+ extent2.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*"));
+ extent2.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
+
+ expected.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*"));
+ expected.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*"));
+ // CreateUnion does not filter out duplicates right now.
+ expected.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*"));
+
+ result.ClearPatterns();
+ URLPatternSet::CreateUnion(extent1, extent2, &result);
+ AssertEqualExtents(expected, result);
+}