Commit 953620b6 authored by jstritar@chromium.org's avatar jstritar@chromium.org

Reland restrict extension features based on the extension type.

The "chrome_url_overrides" manifest key is now accessible by packaged apps.

BUG=101992, 104103
TEST=existing, ManifestTest

Review URL: http://codereview.chromium.org/8654001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112914 0039d316-1c4b-4281-b951-d872f2087c98
parent 10724810
......@@ -13,6 +13,7 @@
#include "chrome/browser/ui/browser.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/manifest.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "googleurl/src/gurl.h"
......@@ -98,10 +99,10 @@ IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, IsInstalled) {
scoped_ptr<DictionaryValue> app_details(
static_cast<DictionaryValue*>(
base::JSONReader::Read(result, false /* allow trailing comma */)));
// extension->manifest_value() does not contain the id.
// extension->manifest() does not contain the id.
app_details->Remove("id", NULL);
EXPECT_TRUE(app_details.get());
EXPECT_TRUE(app_details->Equals(extension->manifest_value()));
EXPECT_TRUE(app_details->Equals(extension->manifest()->value()));
// Try to change app.isInstalled. Should silently fail, so
// that isInstalled should have the initial value.
......@@ -177,8 +178,8 @@ IN_PROC_BROWSER_TEST_F(ChromeAppAPITest, GetDetailsForFrame) {
scoped_ptr<DictionaryValue> app_details(
static_cast<DictionaryValue*>(
base::JSONReader::Read(json, false /* allow trailing comma */)));
// extension->manifest_value() does not contain the id.
// extension->manifest() does not contain the id.
app_details->Remove("id", NULL);
EXPECT_TRUE(app_details.get());
EXPECT_TRUE(app_details->Equals(extension->manifest_value()));
EXPECT_TRUE(app_details->Equals(extension->manifest()->value()));
}
......@@ -12,6 +12,7 @@
#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/manifest.h"
#include "chrome/common/extensions/url_pattern.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
......@@ -1106,7 +1107,7 @@ void ExtensionPrefs::OnExtensionInstalled(
// since it may change on disk.
if (extension->location() != Extension::LOAD) {
extension_dict->Set(kPrefManifest,
extension->manifest_value()->DeepCopy());
extension->manifest()->value()->DeepCopy());
}
if (extension->is_app()) {
......@@ -1196,10 +1197,10 @@ void ExtensionPrefs::UpdateManifest(const Extension* extension) {
DictionaryValue* old_manifest = NULL;
bool update_required =
!extension_dict->GetDictionary(kPrefManifest, &old_manifest) ||
!extension->manifest_value()->Equals(old_manifest);
!extension->manifest()->value()->Equals(old_manifest);
if (update_required) {
UpdateExtensionPref(extension->id(), kPrefManifest,
extension->manifest_value()->DeepCopy());
extension->manifest()->value()->DeepCopy());
}
}
}
......
......@@ -15,6 +15,7 @@
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_file_util.h"
#include "chrome/common/extensions/extension_l10n_util.h"
#include "chrome/common/extensions/manifest.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_service.h"
#include "content/browser/user_metrics.h"
......@@ -141,7 +142,7 @@ void InstalledLoader::LoadAllExtensions() {
if (extension.get()) {
extensions_info->at(i)->extension_manifest.reset(
static_cast<DictionaryValue*>(
extension->manifest_value()->DeepCopy()));
extension->manifest()->value()->DeepCopy()));
should_write_prefs = true;
}
}
......
......@@ -138,6 +138,8 @@
'common/extensions/extension_unpacker.h',
'common/extensions/file_browser_handler.cc',
'common/extensions/file_browser_handler.h',
'common/extensions/manifest.cc',
'common/extensions/manifest.h',
'common/extensions/update_manifest.cc',
'common/extensions/update_manifest.h',
'common/extensions/url_pattern.cc',
......
......@@ -1959,6 +1959,7 @@
'common/extensions/extension_test_util.cc',
'common/extensions/extension_unittest.cc',
'common/extensions/extension_unpacker_unittest.cc',
'common/extensions/manifest_unittest.cc',
'common/extensions/update_manifest_unittest.cc',
'common/extensions/url_pattern_set_unittest.cc',
'common/extensions/url_pattern_unittest.cc',
......
......@@ -34,6 +34,7 @@
#include "chrome/common/extensions/extension_sidebar_defaults.h"
#include "chrome/common/extensions/extension_sidebar_utils.h"
#include "chrome/common/extensions/file_browser_handler.h"
#include "chrome/common/extensions/manifest.h"
#include "chrome/common/extensions/user_script.h"
#include "chrome/common/url_constants.h"
#include "googleurl/src/url_util.h"
......@@ -85,29 +86,6 @@ static void ConvertHexadecimalToIDAlphabet(std::string* id) {
}
}
// These keys are allowed by all crx files (apps, extensions, themes, etc).
static const char* kBaseCrxKeys[] = {
keys::kCurrentLocale,
keys::kDefaultLocale,
keys::kDescription,
keys::kIcons,
keys::kManifestVersion,
keys::kName,
keys::kPublicKey,
keys::kSignature,
keys::kUpdateURL,
keys::kVersion,
};
bool IsBaseCrxKey(const std::string& key) {
for (size_t i = 0; i < arraysize(kBaseCrxKeys); ++i) {
if (key == kBaseCrxKeys[i])
return true;
}
return false;
}
// A singleton object containing global data needed by the extension objects.
class ExtensionConfig {
public:
......@@ -253,7 +231,8 @@ scoped_refptr<Extension> Extension::Create(const FilePath& path,
DCHECK(error);
scoped_refptr<Extension> extension = new Extension(path, location);
if (!extension->InitFromValue(value, flags, error))
if (!extension->InitFromValue(new extensions::Manifest(value.DeepCopy()),
flags, error))
return NULL;
return extension;
}
......@@ -900,31 +879,7 @@ ExtensionSidebarDefaults* Extension::LoadExtensionSidebarDefaults(
return result.release();
}
bool Extension::ContainsNonThemeKeys(const DictionaryValue& source) const {
for (DictionaryValue::key_iterator key = source.begin_keys();
key != source.end_keys(); ++key) {
if (!IsBaseCrxKey(*key) && *key != keys::kTheme)
return true;
}
return false;
}
bool Extension::LoadIsApp(const DictionaryValue* manifest,
std::string* error) {
if (manifest->HasKey(keys::kApp))
is_app_ = true;
if (CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnablePlatformApps)) {
if (manifest->HasKey(keys::kPlatformApp)) {
manifest->GetBoolean(keys::kPlatformApp, &is_platform_app_);
}
}
return true;
}
bool Extension::LoadExtent(const DictionaryValue* manifest,
bool Extension::LoadExtent(const extensions::Manifest* manifest,
const char* key,
URLPatternSet* extent,
const char* list_error,
......@@ -1001,7 +956,7 @@ bool Extension::LoadExtent(const DictionaryValue* manifest,
return true;
}
bool Extension::LoadLaunchURL(const DictionaryValue* manifest,
bool Extension::LoadLaunchURL(const extensions::Manifest* manifest,
std::string* error) {
Value* temp = NULL;
......@@ -1099,7 +1054,7 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest,
return true;
}
bool Extension::LoadLaunchContainer(const DictionaryValue* manifest,
bool Extension::LoadLaunchContainer(const extensions::Manifest* manifest,
std::string* error) {
Value* temp = NULL;
if (!manifest->Get(keys::kLaunchContainer, &temp))
......@@ -1154,7 +1109,7 @@ bool Extension::LoadLaunchContainer(const DictionaryValue* manifest,
return true;
}
bool Extension::LoadAppIsolation(const DictionaryValue* manifest,
bool Extension::LoadAppIsolation(const extensions::Manifest* manifest,
std::string* error) {
Value* temp = NULL;
if (!manifest->Get(keys::kIsolation, &temp))
......@@ -1186,18 +1141,18 @@ bool Extension::LoadAppIsolation(const DictionaryValue* manifest,
return true;
}
bool Extension::LoadWebIntentServices(const base::DictionaryValue& manifest,
bool Extension::LoadWebIntentServices(const extensions::Manifest* manifest,
std::string* error) {
DCHECK(error);
if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableWebIntents))
return true;
if (!manifest.HasKey(keys::kIntents))
if (!manifest->HasKey(keys::kIntents))
return true;
DictionaryValue* all_services = NULL;
if (!manifest.GetDictionary(keys::kIntents, &all_services)) {
if (!manifest->GetDictionary(keys::kIntents, &all_services)) {
*error = errors::kInvalidIntents;
return false;
}
......@@ -1256,32 +1211,6 @@ bool Extension::LoadWebIntentServices(const base::DictionaryValue& manifest,
return true;
}
bool Extension::EnsureNotHybridApp(const DictionaryValue* manifest,
std::string* error) {
if (web_extent().is_empty())
return true;
for (DictionaryValue::key_iterator key = manifest->begin_keys();
key != manifest->end_keys(); ++key) {
if (!IsBaseCrxKey(*key) &&
*key != keys::kApp &&
*key != keys::kPermissions &&
*key != keys::kOptionalPermissions &&
*key != keys::kOptionsPage &&
*key != keys::kBackground &&
*key != keys::kOfflineEnabled &&
*key != keys::kMinimumChromeVersion &&
*key != keys::kRequirements) {
*error = ExtensionErrorUtils::FormatErrorMessage(
errors::kHostedAppsCannotIncludeExtensionFeatures, *key);
return false;
}
}
return true;
}
// static
bool Extension::IsTrustedId(const std::string& id) {
// See http://b/4946060 for more details.
......@@ -1294,9 +1223,6 @@ Extension::Extension(const FilePath& path, Location location)
offline_enabled_(false),
location_(location),
converted_from_user_script_(false),
is_theme_(false),
is_app_(false),
is_platform_app_(false),
is_storage_isolated_(false),
launch_container_(extension_misc::LAUNCH_TAB),
launch_width_(0),
......@@ -1455,10 +1381,14 @@ GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
chrome::kStandardSchemeSeparator + extension_id + "/");
}
bool Extension::InitFromValue(const DictionaryValue& source, int flags,
bool Extension::InitFromValue(extensions::Manifest* manifest, int flags,
std::string* error) {
DCHECK(error);
base::AutoLock auto_lock(runtime_data_lock_);
if (!manifest->ValidateManifest(error))
return false;
// When strict error checks are enabled, make URL pattern parsing strict.
URLPattern::ParseOption parse_strictness =
(flags & STRICT_ERROR_CHECKS ? URLPattern::ERROR_ON_PORTS
......@@ -1469,9 +1399,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
optional_permission_set_ = new ExtensionPermissionSet();
required_permission_set_ = new ExtensionPermissionSet();
if (source.HasKey(keys::kManifestVersion)) {
if (manifest->HasKey(keys::kManifestVersion)) {
int manifest_version = 0;
if (!source.GetInteger(keys::kManifestVersion, &manifest_version) ||
if (!manifest->GetInteger(keys::kManifestVersion, &manifest_version) ||
manifest_version < 1) {
*error = errors::kInvalidManifestVersion;
return false;
......@@ -1490,9 +1420,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
return false;
}
if (source.HasKey(keys::kPublicKey)) {
if (manifest->HasKey(keys::kPublicKey)) {
std::string public_key_bytes;
if (!source.GetString(keys::kPublicKey,
if (!manifest->GetString(keys::kPublicKey,
&public_key_) ||
!ParsePEMKeyBytes(public_key_,
&public_key_bytes) ||
......@@ -1516,15 +1446,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
creation_flags_ = flags;
// Make a copy of the manifest so we can store it in prefs.
manifest_value_.reset(source.DeepCopy());
manifest_.reset(manifest);
// Initialize the URL.
extension_url_ = Extension::GetBaseURLFromExtensionId(id());
// Initialize version.
std::string version_str;
if (!source.GetString(keys::kVersion, &version_str)) {
if (!manifest->GetString(keys::kVersion, &version_str)) {
*error = errors::kInvalidVersion;
return false;
}
......@@ -1537,7 +1466,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
// Initialize name.
string16 localized_name;
if (!source.GetString(keys::kName, &localized_name)) {
if (!manifest->GetString(keys::kName, &localized_name)) {
*error = errors::kInvalidName;
return false;
}
......@@ -1547,18 +1476,17 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
// Load App settings. LoadExtent at least has to be done before
// ParsePermissions(), because the valid permissions depend on what type of
// package this is.
if (!LoadIsApp(manifest_value_.get(), error) ||
!LoadExtent(manifest_value_.get(), keys::kWebURLs,
if (is_app() &&
(!LoadExtent(manifest_.get(), keys::kWebURLs,
&extent_,
errors::kInvalidWebURLs, errors::kInvalidWebURL,
parse_strictness, error) ||
!EnsureNotHybridApp(manifest_value_.get(), error) ||
!LoadLaunchURL(manifest_value_.get(), error) ||
!LoadLaunchContainer(manifest_value_.get(), error)) {
!LoadLaunchURL(manifest_.get(), error) ||
!LoadLaunchContainer(manifest_.get(), error))) {
return false;
}
if (is_platform_app_) {
if (is_platform_app()) {
if (launch_container() != extension_misc::LAUNCH_SHELL) {
*error = errors::kInvalidLaunchContainerForPlatform;
return false;
......@@ -1571,7 +1499,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
// Initialize the permissions (optional).
ExtensionAPIPermissionSet api_permissions;
URLPatternSet host_permissions;
if (!ParsePermissions(&source,
if (!ParsePermissions(manifest_.get(),
keys::kPermissions,
flags,
error,
......@@ -1583,7 +1511,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
// Initialize the optional permissions (optional).
ExtensionAPIPermissionSet optional_api_permissions;
URLPatternSet optional_host_permissions;
if (!ParsePermissions(&source,
if (!ParsePermissions(manifest_.get(),
keys::kOptionalPermissions,
flags,
error,
......@@ -1593,8 +1521,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize description (if present).
if (source.HasKey(keys::kDescription)) {
if (!source.GetString(keys::kDescription,
if (manifest->HasKey(keys::kDescription)) {
if (!manifest->GetString(keys::kDescription,
&description_)) {
*error = errors::kInvalidDescription;
return false;
......@@ -1602,9 +1530,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize homepage url (if present).
if (source.HasKey(keys::kHomepageURL)) {
if (manifest->HasKey(keys::kHomepageURL)) {
std::string tmp;
if (!source.GetString(keys::kHomepageURL, &tmp)) {
if (!manifest->GetString(keys::kHomepageURL, &tmp)) {
*error = ExtensionErrorUtils::FormatErrorMessage(
errors::kInvalidHomepageURL, "");
return false;
......@@ -1620,9 +1548,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize update url (if present).
if (source.HasKey(keys::kUpdateURL)) {
if (manifest->HasKey(keys::kUpdateURL)) {
std::string tmp;
if (!source.GetString(keys::kUpdateURL, &tmp)) {
if (!manifest->GetString(keys::kUpdateURL, &tmp)) {
*error = ExtensionErrorUtils::FormatErrorMessage(
errors::kInvalidUpdateURL, "");
return false;
......@@ -1638,9 +1566,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
// Validate minimum Chrome version (if present). We don't need to store this,
// since the extension is not valid if it is incorrect.
if (source.HasKey(keys::kMinimumChromeVersion)) {
if (manifest->HasKey(keys::kMinimumChromeVersion)) {
std::string minimum_version_string;
if (!source.GetString(keys::kMinimumChromeVersion,
if (!manifest->GetString(keys::kMinimumChromeVersion,
&minimum_version_string)) {
*error = errors::kInvalidMinimumChromeVersion;
return false;
......@@ -1676,13 +1604,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize converted_from_user_script (if present)
source.GetBoolean(keys::kConvertedFromUserScript,
if (manifest->HasKey(keys::kConvertedFromUserScript))
manifest->GetBoolean(keys::kConvertedFromUserScript,
&converted_from_user_script_);
// Initialize icons (if present).
if (source.HasKey(keys::kIcons)) {
if (manifest->HasKey(keys::kIcons)) {
DictionaryValue* icons_value = NULL;
if (!source.GetDictionary(keys::kIcons, &icons_value)) {
if (!manifest->GetDictionary(keys::kIcons, &icons_value)) {
*error = errors::kInvalidIcons;
return false;
}
......@@ -1712,20 +1641,12 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize themes (if present).
is_theme_ = false;
if (source.HasKey(keys::kTheme)) {
// Themes cannot contain extension keys.
if (ContainsNonThemeKeys(source)) {
*error = errors::kThemesCannotContainExtensions;
return false;
}
if (manifest->HasKey(keys::kTheme)) {
DictionaryValue* theme_value = NULL;
if (!source.GetDictionary(keys::kTheme, &theme_value)) {
if (!manifest->GetDictionary(keys::kTheme, &theme_value)) {
*error = errors::kInvalidTheme;
return false;
}
is_theme_ = true;
DictionaryValue* images_value = NULL;
if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) {
......@@ -1798,9 +1719,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize plugins (optional).
if (source.HasKey(keys::kPlugins)) {
if (manifest->HasKey(keys::kPlugins)) {
ListValue* list_value = NULL;
if (!source.GetList(keys::kPlugins, &list_value)) {
if (!manifest->GetList(keys::kPlugins, &list_value)) {
*error = errors::kInvalidPlugins;
return false;
}
......@@ -1842,9 +1763,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
}
if (source.HasKey(keys::kNaClModules)) {
if (manifest->HasKey(keys::kNaClModules)) {
ListValue* list_value = NULL;
if (!source.GetList(keys::kNaClModules, &list_value)) {
if (!manifest->GetList(keys::kNaClModules, &list_value)) {
*error = errors::kInvalidNaClModules;
return false;
}
......@@ -1880,9 +1801,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize content scripts (optional).
if (source.HasKey(keys::kContentScripts)) {
if (manifest->HasKey(keys::kContentScripts)) {
ListValue* list_value;
if (!source.GetList(keys::kContentScripts, &list_value)) {
if (!manifest->GetList(keys::kContentScripts, &list_value)) {
*error = errors::kInvalidContentScriptsList;
return false;
}
......@@ -1910,9 +1831,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
// Initialize page action (optional).
DictionaryValue* page_action_value = NULL;
if (source.HasKey(keys::kPageActions)) {
if (manifest->HasKey(keys::kPageActions)) {
ListValue* list_value = NULL;
if (!source.GetList(keys::kPageActions, &list_value)) {
if (!manifest->GetList(keys::kPageActions, &list_value)) {
*error = errors::kInvalidPageActionsList;
return false;
}
......@@ -1931,8 +1852,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
*error = errors::kInvalidPageActionsListSize;
return false;
}
} else if (source.HasKey(keys::kPageAction)) {
if (!source.GetDictionary(keys::kPageAction, &page_action_value)) {
} else if (manifest->HasKey(keys::kPageAction)) {
if (!manifest->GetDictionary(keys::kPageAction, &page_action_value)) {
*error = errors::kInvalidPageAction;
return false;
}
......@@ -1947,9 +1868,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize browser action (optional).
if (source.HasKey(keys::kBrowserAction)) {
if (manifest->HasKey(keys::kBrowserAction)) {
DictionaryValue* browser_action_value = NULL;
if (!source.GetDictionary(keys::kBrowserAction, &browser_action_value)) {
if (!manifest->GetDictionary(keys::kBrowserAction, &browser_action_value)) {
*error = errors::kInvalidBrowserAction;
return false;
}
......@@ -1961,9 +1882,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize file browser actions (optional).
if (source.HasKey(keys::kFileBrowserHandlers)) {
if (manifest->HasKey(keys::kFileBrowserHandlers)) {
ListValue* file_browser_handlers_value = NULL;
if (!source.GetList(keys::kFileBrowserHandlers,
if (!manifest->GetList(keys::kFileBrowserHandlers,
&file_browser_handlers_value)) {
*error = errors::kInvalidFileBrowserHandler;
return false;
......@@ -1977,15 +1898,14 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
// App isolation.
if (api_permissions.count(ExtensionAPIPermission::kExperimental)) {
if (!LoadAppIsolation(manifest_value_.get(), error))
if (is_app() && !LoadAppIsolation(manifest_.get(), error))
return false;
}
// Initialize options page url (optional).
// Function LoadIsApp() set is_app_ above.
if (source.HasKey(keys::kOptionsPage)) {
if (manifest->HasKey(keys::kOptionsPage)) {
std::string options_str;
if (!source.GetString(keys::kOptionsPage, &options_str)) {
if (!manifest->GetString(keys::kOptionsPage, &options_str)) {
*error = errors::kInvalidOptionsPage;
return false;
}
......@@ -2014,9 +1934,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize background url (optional).
if (source.HasKey(keys::kBackground)) {
if (manifest->HasKey(keys::kBackground)) {
std::string background_str;
if (!source.GetString(keys::kBackground, &background_str)) {
if (!manifest->GetString(keys::kBackground, &background_str)) {
*error = errors::kInvalidBackground;
return false;
}
......@@ -2047,8 +1967,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
}
if (source.HasKey(keys::kDefaultLocale)) {
if (!source.GetString(keys::kDefaultLocale, &default_locale_) ||
if (manifest->HasKey(keys::kDefaultLocale)) {
if (!manifest->GetString(keys::kDefaultLocale, &default_locale_) ||
!l10n_util::IsValidLocaleSyntax(default_locale_)) {
*error = errors::kInvalidDefaultLocale;
return false;
......@@ -2056,9 +1976,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Chrome URL overrides (optional)
if (source.HasKey(keys::kChromeURLOverrides)) {
if (manifest->HasKey(keys::kChromeURLOverrides)) {
DictionaryValue* overrides = NULL;
if (!source.GetDictionary(keys::kChromeURLOverrides, &overrides)) {
if (!manifest->GetDictionary(keys::kChromeURLOverrides, &overrides)) {
*error = errors::kInvalidChromeURLOverrides;
return false;
}
......@@ -2100,9 +2020,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
if (api_permissions.count(ExtensionAPIPermission::kExperimental) &&
source.HasKey(keys::kInputComponents)) {
manifest->HasKey(keys::kInputComponents)) {
ListValue* list_value = NULL;
if (!source.GetList(keys::kInputComponents, &list_value)) {
if (!manifest->GetList(keys::kInputComponents, &list_value)) {
*error = errors::kInvalidInputComponents;
return false;
}
......@@ -2231,17 +2151,17 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
}
if (source.HasKey(keys::kOmnibox)) {
if (!source.GetString(keys::kOmniboxKeyword, &omnibox_keyword_) ||
if (manifest->HasKey(keys::kOmnibox)) {
if (!manifest->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) ||
omnibox_keyword_.empty()) {
*error = errors::kInvalidOmniboxKeyword;
return false;
}
}
if (source.HasKey(keys::kContentSecurityPolicy)) {
if (manifest->HasKey(keys::kContentSecurityPolicy)) {
std::string content_security_policy;
if (!source.GetString(keys::kContentSecurityPolicy,
if (!manifest->GetString(keys::kContentSecurityPolicy,
&content_security_policy)) {
*error = errors::kInvalidContentSecurityPolicy;
return false;
......@@ -2266,9 +2186,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize devtools page url (optional).
if (source.HasKey(keys::kDevToolsPage)) {
if (manifest->HasKey(keys::kDevToolsPage)) {
std::string devtools_str;
if (!source.GetString(keys::kDevToolsPage, &devtools_str)) {
if (!manifest->GetString(keys::kDevToolsPage, &devtools_str)) {
*error = errors::kInvalidDevToolsPage;
return false;
}
......@@ -2280,9 +2200,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize sidebar action (optional).
if (source.HasKey(keys::kSidebar)) {
if (manifest->HasKey(keys::kSidebar)) {
DictionaryValue* sidebar_value = NULL;
if (!source.GetDictionary(keys::kSidebar, &sidebar_value)) {
if (!manifest->GetDictionary(keys::kSidebar, &sidebar_value)) {
*error = errors::kInvalidSidebar;
return false;
}
......@@ -2296,9 +2216,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize text-to-speech voices (optional).
if (source.HasKey(keys::kTtsEngine)) {
if (manifest->HasKey(keys::kTtsEngine)) {
DictionaryValue* tts_dict = NULL;
if (!source.GetDictionary(keys::kTtsEngine, &tts_dict)) {
if (!manifest->GetDictionary(keys::kTtsEngine, &tts_dict)) {
*error = errors::kInvalidTts;
return false;
}
......@@ -2379,15 +2299,15 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize web intents (optional).
if (!LoadWebIntentServices(source, error))
if (!LoadWebIntentServices(manifest, error))
return false;
// Initialize incognito behavior. Apps default to split mode, extensions
// default to spanning.
incognito_split_mode_ = is_app();
if (source.HasKey(keys::kIncognito)) {
if (manifest->HasKey(keys::kIncognito)) {
std::string value;
if (!source.GetString(keys::kIncognito, &value)) {
if (!manifest->GetString(keys::kIncognito, &value)) {
*error = errors::kInvalidIncognitoBehavior;
return false;
}
......@@ -2402,8 +2322,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
}
// Initialize offline-enabled status. Defaults to false.
if (source.HasKey(keys::kOfflineEnabled)) {
if (!source.GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) {
if (manifest->HasKey(keys::kOfflineEnabled)) {
if (!manifest->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) {
*error = errors::kInvalidOfflineEnabled;
return false;
}
......@@ -2411,9 +2331,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
// Initialize requirements (optional). Not actually persisted (they're only
// used by the store), but still validated.
if (source.HasKey(keys::kRequirements)) {
if (manifest->HasKey(keys::kRequirements)) {
DictionaryValue* requirements_value = NULL;
if (!source.GetDictionary(keys::kRequirements, &requirements_value)) {
if (!manifest->GetDictionary(keys::kRequirements, &requirements_value)) {
*error = errors::kInvalidRequirements;
return false;
}
......@@ -2442,13 +2362,6 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags,
optional_permission_set_ = new ExtensionPermissionSet(
optional_api_permissions, optional_host_permissions, URLPatternSet());
// 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
// it calls InitFromValue, passing it up to the browser process which calls
// InitFromValue again. As a result, we need to make sure that nobody
// accidentally modifies it.
DCHECK(source.Equals(manifest_value_.get()));
return true;
}
......@@ -2604,7 +2517,7 @@ GURL Extension::GetIconURL(int size,
return GetResourceURL(path);
}
bool Extension::ParsePermissions(const DictionaryValue* source,
bool Extension::ParsePermissions(const extensions::Manifest* source,
const char* key,
int flags,
std::string* error,
......
......@@ -20,6 +20,7 @@
#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/manifest.h"
#include "chrome/common/extensions/user_script.h"
#include "chrome/common/extensions/url_pattern.h"
#include "chrome/common/extensions/url_pattern_set.h"
......@@ -374,7 +375,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// Parses the host and api permissions from the specified permission |key|
// in the manifest |source|.
bool ParsePermissions(const base::DictionaryValue* source,
bool ParsePermissions(const extensions::Manifest* source,
const char* key,
int flags,
std::string* error,
......@@ -531,8 +532,8 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
}
const GURL& update_url() const { return update_url_; }
const ExtensionIconSet& icons() const { return icons_; }
const base::DictionaryValue* manifest_value() const {
return manifest_value_.get();
const extensions::Manifest* manifest() const {
return manifest_.get();
}
const std::string default_locale() const { return default_locale_; }
const URLOverrideMap& GetChromeURLOverrides() const {
......@@ -557,12 +558,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
}
// App-related.
bool is_app() const { return is_app_; }
bool is_platform_app() const { return is_platform_app_; }
bool is_hosted_app() const { return is_app() && !web_extent().is_empty(); }
bool is_packaged_app() const {
return !is_platform_app() && is_app() && web_extent().is_empty();
bool is_app() const {
return is_packaged_app() || is_hosted_app() || is_platform_app();
}
bool is_platform_app() const { return manifest()->IsPlatformApp(); }
bool is_hosted_app() const { return manifest()->IsHostedApp(); }
bool is_packaged_app() const { return manifest()->IsPackagedApp(); }
bool is_storage_isolated() const { return is_app() && is_storage_isolated_; }
const URLPatternSet& web_extent() const { return extent_; }
const std::string& launch_local_path() const { return launch_local_path_; }
......@@ -574,7 +575,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
int launch_height() const { return launch_height_; }
// Theme-related.
bool is_theme() const { return is_theme_; }
bool is_theme() const { return manifest()->IsTheme(); }
base::DictionaryValue* GetThemeImages() const { return theme_images_.get(); }
base::DictionaryValue* GetThemeColors() const {return theme_colors_.get(); }
base::DictionaryValue* GetThemeTints() const { return theme_tints_.get(); }
......@@ -616,7 +617,8 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
~Extension();
// Initialize the extension from a parsed manifest.
bool InitFromValue(const base::DictionaryValue& value, int flags,
// Takes ownership of the manifest |value|.
bool InitFromValue(extensions::Manifest* value, int flags,
std::string* error);
// Helper function for implementing HasCachedImage/GetCachedImage. A return
......@@ -643,23 +645,20 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
UserScript *instance);
// Helpers to load various chunks of the manifest.
bool LoadIsApp(const base::DictionaryValue* manifest, std::string* error);
bool LoadExtent(const base::DictionaryValue* manifest,
bool LoadExtent(const extensions::Manifest* manifest,
const char* key,
URLPatternSet* extent,
const char* list_error,
const char* value_error,
URLPattern::ParseOption parse_strictness,
std::string* error);
bool LoadLaunchContainer(const base::DictionaryValue* manifest,
bool LoadLaunchContainer(const extensions::Manifest* manifest,
std::string* error);
bool LoadLaunchURL(const base::DictionaryValue* manifest,
bool LoadLaunchURL(const extensions::Manifest* manifest,
std::string* error);
bool LoadAppIsolation(const base::DictionaryValue* manifest,
bool LoadAppIsolation(const extensions::Manifest* manifest,
std::string* error);
bool LoadWebIntentServices(const base::DictionaryValue& manifest,
std::string* error);
bool EnsureNotHybridApp(const base::DictionaryValue* manifest,
bool LoadWebIntentServices(const extensions::Manifest* manifest,
std::string* error);
// Helper method to load an ExtensionAction from the page_action or
......@@ -683,10 +682,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// an extension that has a browser action and a page action.
bool HasMultipleUISurfaces() const;
// Figures out if a source contains keys not associated with themes - we
// don't want to allow scripts and such to be bundled with themes.
bool ContainsNonThemeKeys(const base::DictionaryValue& source) const;
// Updates the launch URL and extents for the extension using the given
// |override_url|.
void OverrideLaunchUrl(const GURL& override_url);
......@@ -820,9 +815,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// A map of display properties.
scoped_ptr<base::DictionaryValue> theme_display_properties_;
// Whether the extension is a theme.
bool is_theme_;
// The homepage for this extension. Useful if it is not hosted by Google and
// therefore does not have a Gallery URL.
GURL homepage_url_;
......@@ -830,19 +822,13 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// URL for fetching an update manifest
GURL update_url_;
// A copy of the manifest that this extension was created from.
scoped_ptr<base::DictionaryValue> manifest_value_;
// The manifest that this extension was created from.
scoped_ptr<extensions::Manifest> manifest_;
// A map of chrome:// hostnames (newtab, downloads, etc.) to Extension URLs
// which override the handling of those URLs. (see ExtensionOverrideUI).
URLOverrideMap chrome_url_overrides_;
// Whether this extension uses app features.
bool is_app_;
// Whether this app uses platform features.
bool is_platform_app_;
// Whether this extension requests isolated storage.
bool is_storage_isolated_;
......
......@@ -166,8 +166,8 @@ const char kExpectString[] = "Expect string value.";
const char kExperimentalFlagRequired[] =
"Loading extensions with 'experimental' permission requires"
" --enable-experimental-extension-apis command line flag.";
const char kHostedAppsCannotIncludeExtensionFeatures[] =
"Hosted apps cannot use the extension feature '*'.";
const char kFeatureNotAllowed[] =
"Feature '*' is not allowed in this type of manifest.";
const char kInvalidAllFrames[] =
"Invalid value for 'content_scripts[*].all_frames'.";
const char kInvalidBackground[] =
......@@ -429,8 +429,6 @@ const char kReservedMessageFound[] =
const char kSidebarExperimental[] =
"You must request the 'experimental' permission in order to use the"
" Sidebar API.";
const char kThemesCannotContainExtensions[] =
"A theme cannot contain extensions code.";
#if defined(OS_CHROMEOS)
const char kIllegalPlugins[] =
"Extensions cannot install plugins on Chrome OS";
......
......@@ -150,7 +150,7 @@ namespace extension_manifest_errors {
extern const char kDisabledByPolicy[];
extern const char kExperimentalFlagRequired[];
extern const char kExpectString[];
extern const char kHostedAppsCannotIncludeExtensionFeatures[];
extern const char kFeatureNotAllowed[];
extern const char kInvalidAllFrames[];
extern const char kInvalidBackground[];
extern const char kInvalidBackgroundInHostedApp[];
......@@ -280,7 +280,6 @@ namespace extension_manifest_errors {
extern const char kOneUISurfaceOnly[];
extern const char kReservedMessageFound[];
extern const char kSidebarExperimental[];
extern const char kThemesCannotContainExtensions[];
extern const char kWebContentMustBeEnabled[];
#if defined(OS_CHROMEOS)
extern const char kIllegalPlugins[];
......
......@@ -588,12 +588,8 @@ TEST_F(ExtensionManifestTest, Sidebar) {
extension->sidebar_defaults()->default_page().spec());
}
TEST_F(ExtensionManifestTest, DisallowHybridApps) {
LoadAndExpectError("disallow_hybrid_1.json",
ExtensionErrorUtils::FormatErrorMessage(
errors::kHostedAppsCannotIncludeExtensionFeatures,
keys::kBrowserAction));
LoadAndExpectError("disallow_hybrid_2.json",
TEST_F(ExtensionManifestTest, BackgroundPermission) {
LoadAndExpectError("background_permission.json",
errors::kBackgroundPermissionNeeded);
}
......@@ -742,9 +738,7 @@ TEST_F(ExtensionManifestTest, NormalizeIconPaths) {
}
TEST_F(ExtensionManifestTest, DisallowMultipleUISurfaces) {
LoadAndExpectError("multiple_ui_surfaces_1.json", errors::kOneUISurfaceOnly);
LoadAndExpectError("multiple_ui_surfaces_2.json", errors::kOneUISurfaceOnly);
LoadAndExpectError("multiple_ui_surfaces_3.json", errors::kOneUISurfaceOnly);
LoadAndExpectError("multiple_ui_surfaces.json", errors::kOneUISurfaceOnly);
}
TEST_F(ExtensionManifestTest, ParseHomepageURLs) {
......
......@@ -5,6 +5,7 @@
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/manifest.h"
#include "content/public/common/common_param_traits.h"
ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params()
......@@ -49,10 +50,11 @@ ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params(
extension_manifest_keys::kVersion,
};
// Copy only the data we need.
// Copy only the data we need and bypass the manifest type checks.
DictionaryValue* source = extension->manifest()->value();
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kRendererExtensionKeys); ++i) {
Value* temp = NULL;
if (extension->manifest_value()->Get(kRendererExtensionKeys[i], &temp))
if (source->Get(kRendererExtensionKeys[i], &temp))
manifest->Set(kRendererExtensionKeys[i], temp->DeepCopy());
}
}
......
// 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/manifest.h"
#include "base/basictypes.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/string_split.h"
#include "base/values.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_error_utils.h"
namespace errors = extension_manifest_errors;
namespace keys = extension_manifest_keys;
namespace extensions {
namespace {
typedef std::map<std::string, int> RestrictionMap;
struct Restrictions {
Restrictions() {
// Base keys that all manifests can specify.
map[keys::kName] = Manifest::kTypeAll;
map[keys::kVersion] = Manifest::kTypeAll;
map[keys::kManifestVersion] = Manifest::kTypeAll;
map[keys::kDescription] = Manifest::kTypeAll;
map[keys::kIcons] = Manifest::kTypeAll;
map[keys::kCurrentLocale] = Manifest::kTypeAll;
map[keys::kDefaultLocale] = Manifest::kTypeAll;
map[keys::kSignature] = Manifest::kTypeAll;
map[keys::kUpdateURL] = Manifest::kTypeAll;
map[keys::kPublicKey] = Manifest::kTypeAll;
// Type specific.
map[keys::kApp] = Manifest::kTypeHostedApp | Manifest::kTypePackagedApp |
Manifest::kTypePlatformApp;
map[keys::kTheme] = Manifest::kTypeTheme;
// keys::kPlatformApp holds a boolean, so all types can define it.
map[keys::kPlatformApp] = Manifest::kTypeAll;
// Extensions only.
map[keys::kBrowserAction] = Manifest::kTypeExtension;
map[keys::kPageAction] = Manifest::kTypeExtension;
map[keys::kPageActions] = Manifest::kTypeExtension;
// Everything except themes.
int all_but_themes = Manifest::kTypeAll - Manifest::kTypeTheme;
map[keys::kPermissions] = all_but_themes;
map[keys::kOptionalPermissions] = all_but_themes;
map[keys::kOptionsPage] = all_but_themes;
map[keys::kBackground] = all_but_themes;
map[keys::kOfflineEnabled] = all_but_themes;
map[keys::kMinimumChromeVersion] = all_but_themes;
map[keys::kRequirements] = all_but_themes;
map[keys::kConvertedFromUserScript] = all_but_themes;
map[keys::kNaClModules] = all_but_themes;
map[keys::kPlugins] = all_but_themes;
// Extensions and packaged apps.
int ext_and_packaged =
Manifest::kTypeExtension | Manifest::kTypePackagedApp;
map[keys::kContentScripts] = ext_and_packaged;
map[keys::kOmnibox] = ext_and_packaged;
map[keys::kDevToolsPage] = ext_and_packaged;
map[keys::kSidebar] = ext_and_packaged;
map[keys::kHomepageURL] = ext_and_packaged;
map[keys::kChromeURLOverrides] = ext_and_packaged;
// Extensions, packaged apps and platform apps.
int local_apps_and_ext = ext_and_packaged | Manifest::kTypePlatformApp;
map[keys::kContentSecurityPolicy] = local_apps_and_ext;
map[keys::kFileBrowserHandlers] = local_apps_and_ext;
map[keys::kIncognito] = local_apps_and_ext;
map[keys::kInputComponents] = local_apps_and_ext;
map[keys::kTtsEngine] = local_apps_and_ext;
map[keys::kIntents] = local_apps_and_ext;
}
// Returns true if the |key| is recognized.
bool IsKnownKey(const std::string& key) const {
RestrictionMap::const_iterator i = map.find(key);
return i != map.end();
}
// Returns true if the given |key| can be specified by the manifest |type|.
bool CanAccessKey(const std::string& key, Manifest::Type type) const {
RestrictionMap::const_iterator i = map.find(key);
return (i != map.end() && (type & i->second) != 0);
}
RestrictionMap map;
};
base::LazyInstance<Restrictions> g_restrictions;
} // namespace
// static
std::set<std::string> Manifest::GetAllKnownKeys() {
std::set<std::string> keys;
const RestrictionMap& map = g_restrictions.Get().map;
for (RestrictionMap::const_iterator i = map.begin(); i != map.end(); i++)
keys.insert(i->first);
return keys;
}
Manifest::Manifest(DictionaryValue* value) : value_(value) {}
Manifest::~Manifest() {}
bool Manifest::ValidateManifest(std::string* error) const {
Restrictions restrictions = g_restrictions.Get();
Type type = GetType();
for (DictionaryValue::key_iterator key = value_->begin_keys();
key != value_->end_keys(); ++key) {
// When validating the extension manifests, we ignore keys that are not
// recognized for forward compatibility.
if (!restrictions.IsKnownKey(*key)) {
// TODO(aa): Consider having an error here in the case of strict error
// checking to let developers know when they screw up.
continue;
}
if (!restrictions.CanAccessKey(*key, type)) {
*error = ExtensionErrorUtils::FormatErrorMessage(
errors::kFeatureNotAllowed, *key);
return false;
}
}
return true;
}
bool Manifest::HasKey(const std::string& key) const {
Restrictions restrictions = g_restrictions.Get();
return restrictions.CanAccessKey(key, GetType()) && value_->HasKey(key);
}
bool Manifest::Get(
const std::string& path, Value** out_value) const {
return CanAccessPath(path) && value_->Get(path, out_value);
}
bool Manifest::GetBoolean(
const std::string& path, bool* out_value) const {
return CanAccessPath(path) && value_->GetBoolean(path, out_value);
}
bool Manifest::GetInteger(
const std::string& path, int* out_value) const {
return CanAccessPath(path) && value_->GetInteger(path, out_value);
}
bool Manifest::GetString(
const std::string& path, std::string* out_value) const {
return CanAccessPath(path) && value_->GetString(path, out_value);
}
bool Manifest::GetString(
const std::string& path, string16* out_value) const {
return CanAccessPath(path) && value_->GetString(path, out_value);
}
bool Manifest::GetDictionary(
const std::string& path, DictionaryValue** out_value) const {
return CanAccessPath(path) && value_->GetDictionary(path, out_value);
}
bool Manifest::GetList(
const std::string& path, ListValue** out_value) const {
return CanAccessPath(path) && value_->GetList(path, out_value);
}
Manifest* Manifest::DeepCopy() const {
return new Manifest(value_->DeepCopy());
}
bool Manifest::Equals(const Manifest* other) const {
return other && value_->Equals(other->value());
}
Manifest::Type Manifest::GetType() const {
if (value_->HasKey(keys::kTheme))
return kTypeTheme;
bool is_platform_app = false;
if (value_->GetBoolean(keys::kPlatformApp, &is_platform_app) &&
is_platform_app)
return kTypePlatformApp;
if (value_->HasKey(keys::kApp)) {
if (value_->Get(keys::kWebURLs, NULL) ||
value_->Get(keys::kLaunchWebURL, NULL))
return kTypeHostedApp;
else
return kTypePackagedApp;
} else {
return kTypeExtension;
}
}
bool Manifest::IsTheme() const {
return GetType() == kTypeTheme;
}
bool Manifest::IsPlatformApp() const {
return GetType() == kTypePlatformApp;
}
bool Manifest::IsPackagedApp() const {
return GetType() == kTypePackagedApp;
}
bool Manifest::IsHostedApp() const {
return GetType() == kTypeHostedApp;
}
bool Manifest::CanAccessPath(const std::string& path) const {
std::vector<std::string> components;
base::SplitString(path, '.', &components);
Restrictions restrictions = g_restrictions.Get();
return restrictions.CanAccessKey(components[0], GetType());
}
} // namespace extensions
// 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_MANIFEST_H_
#define CHROME_COMMON_EXTENSIONS_MANIFEST_H_
#pragma once
#include <map>
#include <string>
#include <set>
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
namespace base {
class DictionaryValue;
class ListValue;
class Value;
}
namespace extensions {
// Lightweight wrapper around a DictionaryValue representing an extension's
// manifest. Currently enforces access to properties of the manifest based
// on manifest type.
//
// TODO(aa): Move more smarts about mmanifest into this class over time.
class Manifest {
public:
// Flags for matching types of extension manifests.
enum Type {
kTypeNone = 0,
// Extension::TYPE_EXTENSION and Extension::TYPE_USER_SCRIPT
kTypeExtension = 1 << 0,
// Extension::TYPE_THEME
kTypeTheme = 1 << 1,
// Extension::TYPE_HOSTED_APP
kTypeHostedApp = 1 << 2,
// Extension::TYPE_PACKAGED_APP
kTypePackagedApp = 1 << 3,
// Extension::TYPE_PLATFORM_APP
kTypePlatformApp = 1 << 4,
// All types
kTypeAll = (1 << 5) - 1,
};
// Returns all known keys (this is used for testing).
static std::set<std::string> GetAllKnownKeys();
// Takes over ownership of |value|.
explicit Manifest(base::DictionaryValue* value);
virtual ~Manifest();
// Returns true if all keys in the manifest can be specified by
// the extension type.
bool ValidateManifest(std::string* error) const;
// Returns the manifest type.
Type GetType() const;
// Returns true if the manifest represents an Extension::TYPE_THEME.
bool IsTheme() const;
// Returns true for Extension::TYPE_PLATFORM_APP
bool IsPlatformApp() const;
// Returns true for Extension::TYPE_PACKAGED_APP.
bool IsPackagedApp() const;
// Returns true for Extension::TYPE_HOSTED_APP.
bool IsHostedApp() const;
// These access the wrapped manifest value, returning false when the property
// does not exist or if the manifest type can't access it.
bool HasKey(const std::string& key) const;
bool Get(const std::string& path, base::Value** out_value) const;
bool GetBoolean(const std::string& path, bool* out_value) const;
bool GetInteger(const std::string& path, int* out_value) const;
bool GetString(const std::string& path, std::string* out_value) const;
bool GetString(const std::string& path, string16* out_value) const;
bool GetDictionary(const std::string& path,
base::DictionaryValue** out_value) const;
bool GetList(const std::string& path, base::ListValue** out_value) const;
// Returns a new Manifest equal to this one, passing ownership to
// the caller.
Manifest* DeepCopy() const;
// Returns true if this equals the |other| manifest.
bool Equals(const Manifest* other) const;
// Gets the underlying DictionaryValue representing the manifest.
// Note: only know this when you KNOW you don't need the validation.
base::DictionaryValue* value() const { return value_.get(); }
private:
// Returns true if the extension can specify the given |path|.
bool CanAccessPath(const std::string& path) const;
scoped_ptr<base::DictionaryValue> value_;
};
} // namespace extensions
#endif // CHROME_COMMON_EXTENSIONS_MANIFEST_H_
// 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/manifest.h"
#include <algorithm>
#include <set>
#include <string>
#include "base/memory/scoped_ptr.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_error_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace keys = extension_manifest_keys;
namespace errors = extension_manifest_errors;
namespace extensions {
namespace {
// Keys that define types.
const char* kTypeKeys[] = {
keys::kApp,
keys::kTheme,
keys::kPlatformApp
};
// Keys that are not accesible by themes.
const char* kNotThemeKeys[] = {
keys::kBrowserAction,
keys::kPageAction,
keys::kPageActions,
keys::kChromeURLOverrides,
keys::kPermissions,
keys::kOptionalPermissions,
keys::kOptionsPage,
keys::kBackground,
keys::kOfflineEnabled,
keys::kMinimumChromeVersion,
keys::kRequirements,
keys::kConvertedFromUserScript,
keys::kNaClModules,
keys::kPlugins,
keys::kContentScripts,
keys::kOmnibox,
keys::kDevToolsPage,
keys::kSidebar,
keys::kHomepageURL,
keys::kContentSecurityPolicy,
keys::kFileBrowserHandlers,
keys::kIncognito,
keys::kInputComponents,
keys::kTtsEngine,
keys::kIntents
};
// Keys that are not accessible by hosted apps.
const char* kNotHostedAppKeys[] = {
keys::kBrowserAction,
keys::kPageAction,
keys::kPageActions,
keys::kChromeURLOverrides,
keys::kContentScripts,
keys::kOmnibox,
keys::kDevToolsPage,
keys::kSidebar,
keys::kHomepageURL,
keys::kContentSecurityPolicy,
keys::kFileBrowserHandlers,
keys::kIncognito,
keys::kInputComponents,
keys::kTtsEngine,
keys::kIntents
};
// Keys not accessible by packaged aps.
const char* kNotPackagedAppKeys[] = {
keys::kBrowserAction,
keys::kPageAction,
keys::kPageActions
};
// Keys not accessible by platform apps.
const char* kNotPlatformAppKeys[] = {
keys::kBrowserAction,
keys::kPageAction,
keys::kPageActions,
keys::kChromeURLOverrides,
keys::kContentScripts,
keys::kOmnibox,
keys::kDevToolsPage,
keys::kSidebar,
keys::kHomepageURL,
};
// Returns all the manifest keys not including those in |filtered| or kTypeKeys.
std::set<std::string> GetAccessibleKeys(const char* filtered[], size_t length) {
std::set<std::string> all_keys = Manifest::GetAllKnownKeys();
std::set<std::string> filtered_keys(filtered, filtered + length);
// Starting with all possible manfiest keys, remove the keys that aren't
// accessible for the given type.
std::set<std::string> intermediate;
std::set_difference(all_keys.begin(), all_keys.end(),
filtered_keys.begin(), filtered_keys.end(),
std::insert_iterator<std::set<std::string> >(
intermediate, intermediate.begin()));
// Then remove the keys that specify types (app, platform_app, etc.).
std::set<std::string> result;
std::set<std::string> type_keys(
kTypeKeys, kTypeKeys + ARRAYSIZE_UNSAFE(kTypeKeys));
std::set_difference(intermediate.begin(), intermediate.end(),
type_keys.begin(), type_keys.end(),
std::insert_iterator<std::set<std::string> >(
result, result.begin()));
return result;
}
} // namespace
class ManifestTest : public testing::Test {
public:
ManifestTest() : default_value_("test") {}
protected:
void AssertType(Manifest* manifest, Manifest::Type type) {
EXPECT_EQ(type, manifest->GetType());
EXPECT_EQ(type == Manifest::kTypeTheme, manifest->IsTheme());
EXPECT_EQ(type == Manifest::kTypePlatformApp, manifest->IsPlatformApp());
EXPECT_EQ(type == Manifest::kTypePackagedApp, manifest->IsPackagedApp());
EXPECT_EQ(type == Manifest::kTypeHostedApp, manifest->IsHostedApp());
}
void TestRestrictedKeys(Manifest* manifest,
const char* restricted_keys[],
size_t restricted_keys_length) {
// Verify that the keys on the restricted key list for the given manifest
// fail validation and are filtered out.
DictionaryValue* value = manifest->value();
for (size_t i = 0; i < restricted_keys_length; ++i) {
std::string error, str;
value->Set(restricted_keys[i], Value::CreateStringValue(default_value_));
EXPECT_FALSE(manifest->ValidateManifest(&error));
EXPECT_EQ(error, ExtensionErrorUtils::FormatErrorMessage(
errors::kFeatureNotAllowed, restricted_keys[i]));
EXPECT_FALSE(manifest->GetString(restricted_keys[i], &str));
EXPECT_TRUE(value->Remove(restricted_keys[i], NULL));
}
}
std::string default_value_;
};
// Verifies that extensions can access the correct keys.
TEST_F(ManifestTest, Extension) {
// Generate the list of keys accessible by extensions.
std::set<std::string> extension_keys = GetAccessibleKeys(NULL, 0u);
// Construct the underlying value using every single key other than those
// on the restricted list.. We can use the same value for every key because we
// validate only by checking the presence of the keys.
DictionaryValue* value = new DictionaryValue();
for (std::set<std::string>::iterator i = extension_keys.begin();
i != extension_keys.end(); ++i)
value->Set(*i, Value::CreateStringValue(default_value_));
scoped_ptr<Manifest> manifest(new Manifest(value));
std::string error;
EXPECT_TRUE(manifest->ValidateManifest(&error));
EXPECT_EQ("", error);
AssertType(manifest.get(), Manifest::kTypeExtension);
// Verify that all the extension keys are accessible.
for (std::set<std::string>::iterator i = extension_keys.begin();
i != extension_keys.end(); ++i) {
std::string value;
manifest->GetString(*i, &value);
EXPECT_EQ(default_value_, value) << *i;
}
// Test DeepCopy and Equals.
scoped_ptr<Manifest> manifest2(manifest->DeepCopy());
EXPECT_TRUE(manifest->Equals(manifest2.get()));
EXPECT_TRUE(manifest2->Equals(manifest.get()));
value->Set("foo", Value::CreateStringValue("blah"));
EXPECT_FALSE(manifest->Equals(manifest2.get()));
}
// Verifies that themes can access the right keys.
TEST_F(ManifestTest, Theme) {
std::set<std::string> theme_keys =
GetAccessibleKeys(kNotThemeKeys, ARRAYSIZE_UNSAFE(kNotThemeKeys));
DictionaryValue* value = new DictionaryValue();
for (std::set<std::string>::iterator i = theme_keys.begin();
i != theme_keys.end(); ++i)
value->Set(*i, Value::CreateStringValue(default_value_));
std::string theme_key = keys::kTheme + std::string(".test");
value->Set(theme_key, Value::CreateStringValue(default_value_));
scoped_ptr<Manifest> manifest(new Manifest(value));
std::string error;
EXPECT_TRUE(manifest->ValidateManifest(&error));
EXPECT_EQ("", error);
AssertType(manifest.get(), Manifest::kTypeTheme);
// Verify that all the theme keys are accessible.
std::string str;
for (std::set<std::string>::iterator i = theme_keys.begin();
i != theme_keys.end(); ++i) {
EXPECT_TRUE(manifest->GetString(*i, &str));
EXPECT_EQ(default_value_, str) << *i;
}
EXPECT_TRUE(manifest->GetString(theme_key, &str));
EXPECT_EQ(default_value_, str) << theme_key;
// And that all the other keys fail validation and are filtered out
TestRestrictedKeys(manifest.get(), kNotThemeKeys,
ARRAYSIZE_UNSAFE(kNotThemeKeys));
};
// Verifies that platform apps can access the right keys.
TEST_F(ManifestTest, PlatformApp) {
std::set<std::string> platform_keys = GetAccessibleKeys(
kNotPlatformAppKeys,
ARRAYSIZE_UNSAFE(kNotPlatformAppKeys));
DictionaryValue* value = new DictionaryValue();
for (std::set<std::string>::iterator i = platform_keys.begin();
i != platform_keys.end(); ++i)
value->Set(*i, Value::CreateStringValue(default_value_));
value->Set(keys::kPlatformApp, Value::CreateBooleanValue(true));
scoped_ptr<Manifest> manifest(new Manifest(value));
std::string error;
EXPECT_TRUE(manifest->ValidateManifest(&error));
EXPECT_EQ("", error);
AssertType(manifest.get(), Manifest::kTypePlatformApp);
// Verify that all the platform app keys are accessible.
std::string str;
for (std::set<std::string>::iterator i = platform_keys.begin();
i != platform_keys.end(); ++i) {
EXPECT_TRUE(manifest->GetString(*i, &str));
EXPECT_EQ(default_value_, str) << *i;
}
bool is_platform_app = false;
EXPECT_TRUE(manifest->GetBoolean(keys::kPlatformApp, &is_platform_app));
EXPECT_TRUE(is_platform_app) << keys::kPlatformApp;
// And that all the other keys fail validation and are filtered out.
TestRestrictedKeys(manifest.get(), kNotPlatformAppKeys,
ARRAYSIZE_UNSAFE(kNotPlatformAppKeys));
};
// Verifies that hosted apps can access the right keys.
TEST_F(ManifestTest, HostedApp) {
std::set<std::string> keys = GetAccessibleKeys(
kNotHostedAppKeys,
ARRAYSIZE_UNSAFE(kNotHostedAppKeys));
DictionaryValue* value = new DictionaryValue();
for (std::set<std::string>::iterator i = keys.begin();
i != keys.end(); ++i)
value->Set(*i, Value::CreateStringValue(default_value_));
value->Set(keys::kWebURLs, Value::CreateStringValue(default_value_));
scoped_ptr<Manifest> manifest(new Manifest(value));
std::string error;
EXPECT_TRUE(manifest->ValidateManifest(&error));
EXPECT_EQ("", error);
AssertType(manifest.get(), Manifest::kTypeHostedApp);
// Verify that all the hosted app keys are accessible.
std::string str;
for (std::set<std::string>::iterator i = keys.begin();
i != keys.end(); ++i) {
EXPECT_TRUE(manifest->GetString(*i, &str));
EXPECT_EQ(default_value_, str) << *i;
}
EXPECT_TRUE(manifest->GetString(keys::kWebURLs, &str));
EXPECT_EQ(default_value_, str) << keys::kWebURLs;
// And that all the other keys fail validation and are filtered out.
TestRestrictedKeys(manifest.get(), kNotHostedAppKeys,
ARRAYSIZE_UNSAFE(kNotHostedAppKeys));
};
// Verifies that packaged apps can access the right keys.
TEST_F(ManifestTest, PackagedApp) {
std::set<std::string> keys = GetAccessibleKeys(
kNotPackagedAppKeys,
ARRAYSIZE_UNSAFE(kNotPackagedAppKeys));
DictionaryValue* value = new DictionaryValue();
for (std::set<std::string>::iterator i = keys.begin();
i != keys.end(); ++i)
value->Set(*i, Value::CreateStringValue(default_value_));
value->Set(keys::kApp, Value::CreateStringValue(default_value_));
scoped_ptr<Manifest> manifest(new Manifest(value));
std::string error;
EXPECT_TRUE(manifest->ValidateManifest(&error));
EXPECT_EQ("", error);
AssertType(manifest.get(), Manifest::kTypePackagedApp);
// Verify that all the packaged app keys are accessible.
std::string str;
for (std::set<std::string>::iterator i = keys.begin();
i != keys.end(); ++i) {
EXPECT_TRUE(manifest->GetString(*i, &str));
EXPECT_EQ(default_value_, str) << *i;
}
EXPECT_TRUE(manifest->GetString(keys::kApp, &str));
EXPECT_EQ(default_value_, str) << keys::kApp;
// And that all the other keys fail validation and are filtered out.
TestRestrictedKeys(manifest.get(), kNotPackagedAppKeys,
ARRAYSIZE_UNSAFE(kNotPackagedAppKeys));
};
// Verifies that the various getters filter unknown and restricted keys.
TEST_F(ManifestTest, Getters) {
DictionaryValue* value = new DictionaryValue();
scoped_ptr<Manifest> manifest(new Manifest(value));
std::string unknown_key = "asdfaskldjf";
// Verify that the key filtering works for each of the getters.
// Get and GetBoolean
bool expected_bool = true, actual_bool = false;
value->Set(unknown_key, Value::CreateBooleanValue(expected_bool));
EXPECT_FALSE(manifest->HasKey(unknown_key));
EXPECT_FALSE(manifest->GetBoolean(unknown_key, &actual_bool));
EXPECT_FALSE(actual_bool);
Value* actual_value = NULL;
EXPECT_FALSE(manifest->Get(unknown_key, &actual_value));
EXPECT_TRUE(value->Remove(unknown_key, NULL));
// GetInteger
int expected_int = 5, actual_int = 0;
value->Set(unknown_key, Value::CreateIntegerValue(expected_int));
EXPECT_FALSE(manifest->GetInteger(unknown_key, &actual_int));
EXPECT_NE(expected_int, actual_int);
EXPECT_TRUE(value->Remove(unknown_key, NULL));
// GetString
std::string expected_str = "hello", actual_str;
value->Set(unknown_key, Value::CreateStringValue(expected_str));
EXPECT_FALSE(manifest->GetString(unknown_key, &actual_str));
EXPECT_NE(expected_str, actual_str);
EXPECT_TRUE(value->Remove(unknown_key, NULL));
// GetString (string16)
string16 expected_str16(UTF8ToUTF16("hello")), actual_str16;
value->Set(unknown_key, Value::CreateStringValue(expected_str16));
EXPECT_FALSE(manifest->GetString(unknown_key, &actual_str16));
EXPECT_NE(expected_str16, actual_str16);
EXPECT_TRUE(value->Remove(unknown_key, NULL));
// GetDictionary
DictionaryValue* expected_dict = new DictionaryValue();
DictionaryValue* actual_dict = NULL;
expected_dict->Set("foo", Value::CreateStringValue("bar"));
value->Set(unknown_key, expected_dict);
EXPECT_FALSE(manifest->GetDictionary(unknown_key, &actual_dict));
EXPECT_EQ(NULL, actual_dict);
std::string path = unknown_key + ".foo";
EXPECT_FALSE(manifest->GetString(path, &actual_str));
EXPECT_NE("bar", actual_str);
EXPECT_TRUE(value->Remove(unknown_key, NULL));
// GetList
ListValue* expected_list = new ListValue();
ListValue* actual_list = NULL;
expected_list->Append(Value::CreateStringValue("blah"));
value->Set(unknown_key, expected_list);
EXPECT_FALSE(manifest->GetList(unknown_key, &actual_list));
EXPECT_EQ(NULL, actual_list);
EXPECT_TRUE(value->Remove(unknown_key, NULL));
}
} // namespace extensions
......@@ -12,6 +12,7 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_messages.h"
#include "chrome/common/extensions/extension_set.h"
#include "chrome/common/extensions/manifest.h"
#include "chrome/renderer/extensions/chrome_v8_context.h"
#include "chrome/renderer/extensions/extension_dispatcher.h"
#include "chrome/renderer/extensions/extension_helper.h"
......@@ -194,7 +195,7 @@ v8::Handle<v8::Value> AppBindingsHandler::GetDetailsForFrameImpl(
return v8::Null();
scoped_ptr<DictionaryValue> manifest_copy(
extension->manifest_value()->DeepCopy());
extension->manifest()->value()->DeepCopy());
manifest_copy->SetString("id", extension->id());
scoped_ptr<V8ValueConverter> converter(V8ValueConverter::create());
return converter->ToV8Value(manifest_copy.get(),
......
{
"name": "test",
"version": "1",
"app": {
"urls": [
"http://www.google.com/mail/",
"http://www.google.com/foobar/"
],
"launch": {
"web_url": "http://www.google.com/mail/"
}
},
"permissions": [
"notifications"
],
"browser_action": {}
}
{
"name": "foo",
"version": "1",
"browser_action": {},
"app": {
"launch": {
"local_path": "foo.html"
}
}
}
{
"name": "foo",
"version": "1",
"browser_action": {},
"page_action": {},
"app": {
"launch": {
"local_path": "foo.html"
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment