| Index: chrome/common/extensions/extension.cc
|
| diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
|
| index 0e8530073461c0a1c11e5fa92a560fb36b2f0114..a14bf66dee60b5b9908539dcb0fc3fd673dd3591 100644
|
| --- a/chrome/common/extensions/extension.cc
|
| +++ b/chrome/common/extensions/extension.cc
|
| @@ -172,6 +172,30 @@ int GetLocationRank(Extension::Location location) {
|
| return rank;
|
| }
|
|
|
| +bool ReadLaunchDimension(const extensions::Manifest* manifest,
|
| + const char* key,
|
| + int* target,
|
| + bool is_valid_container,
|
| + string16* error) {
|
| + Value* temp = NULL;
|
| + if (manifest->Get(key, &temp)) {
|
| + if (!is_valid_container) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidLaunchValueContainer,
|
| + key);
|
| + return false;
|
| + }
|
| + if (!temp->GetAsInteger(target) || *target < 0) {
|
| + *target = 0;
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidLaunchValue,
|
| + key);
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| } // namespace
|
|
|
| const FilePath::CharType Extension::kManifestFilename[] =
|
| @@ -333,7 +357,6 @@ scoped_refptr<Extension> Extension::Create(const FilePath& path,
|
| const std::string& explicit_id,
|
| std::string* utf8_error) {
|
| DCHECK(utf8_error);
|
| -
|
| string16 error;
|
| scoped_ptr<extensions::Manifest> manifest(
|
| new extensions::Manifest(
|
| @@ -480,11 +503,11 @@ bool Extension::is_platform_app() const {
|
| }
|
|
|
| bool Extension::is_hosted_app() const {
|
| - return manifest()->IsHostedApp();
|
| + return manifest()->IsHostedApp();
|
| }
|
|
|
| bool Extension::is_packaged_app() const {
|
| - return manifest()->IsPackagedApp();
|
| + return manifest()->IsPackagedApp();
|
| }
|
|
|
| bool Extension::is_theme() const {
|
| @@ -535,7 +558,6 @@ bool Extension::GenerateId(const std::string& input, std::string* output) {
|
| // content_script list of the manifest.
|
| bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script,
|
| int definition_index,
|
| - int flags,
|
| string16* error,
|
| UserScript* result) {
|
| // run_at
|
| @@ -616,7 +638,7 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script,
|
| if (pattern.MatchesScheme(chrome::kFileScheme) &&
|
| !CanExecuteScriptEverywhere()) {
|
| wants_file_access_ = true;
|
| - if (!(flags & ALLOW_FILE_ACCESS))
|
| + if (!(creation_flags_ & ALLOW_FILE_ACCESS))
|
| pattern.SetValidSchemes(
|
| pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
|
| }
|
| @@ -892,98 +914,123 @@ ExtensionAction* Extension::LoadExtensionActionHelper(
|
| return result.release();
|
| }
|
|
|
| -Extension::FileBrowserHandlerList* Extension::LoadFileBrowserHandlers(
|
| - const ListValue* extension_actions, string16* error) {
|
| - scoped_ptr<FileBrowserHandlerList> result(
|
| - new FileBrowserHandlerList());
|
| - for (ListValue::const_iterator iter = extension_actions->begin();
|
| - iter != extension_actions->end();
|
| - ++iter) {
|
| - if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
|
| - return NULL;
|
| +// static
|
| +bool Extension::InitExtensionID(extensions::Manifest* manifest,
|
| + const FilePath& path,
|
| + const std::string& explicit_id,
|
| + int creation_flags,
|
| + string16* error) {
|
| + if (!explicit_id.empty()) {
|
| + manifest->set_extension_id(explicit_id);
|
| + return true;
|
| + }
|
| +
|
| + if (manifest->HasKey(keys::kPublicKey)) {
|
| + std::string public_key;
|
| + std::string public_key_bytes;
|
| + std::string extension_id;
|
| + if (!manifest->GetString(keys::kPublicKey, &public_key) ||
|
| + !ParsePEMKeyBytes(public_key, &public_key_bytes) ||
|
| + !GenerateId(public_key_bytes, &extension_id)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidKey);
|
| + return false;
|
| }
|
| - scoped_ptr<FileBrowserHandler> action(
|
| - LoadFileBrowserHandler(
|
| - reinterpret_cast<DictionaryValue*>(*iter), error));
|
| - if (!action.get())
|
| - return NULL; // Failed to parse file browser action definition.
|
| - result->push_back(linked_ptr<FileBrowserHandler>(action.release()));
|
| + manifest->set_extension_id(extension_id);
|
| + return true;
|
| + }
|
| +
|
| + if (creation_flags & REQUIRE_KEY) {
|
| + *error = ASCIIToUTF16(errors::kInvalidKey);
|
| + return false;
|
| + } else {
|
| + // If there is a path, we generate the ID from it. This is useful for
|
| + // development mode, because it keeps the ID stable across restarts and
|
| + // reloading the extension.
|
| + std::string extension_id = GenerateIdForPath(path);
|
| + if (extension_id.empty()) {
|
| + NOTREACHED() << "Could not create ID from path.";
|
| + return false;
|
| + }
|
| + manifest->set_extension_id(extension_id);
|
| + return true;
|
| }
|
| - return result.release();
|
| }
|
|
|
| -FileBrowserHandler* Extension::LoadFileBrowserHandler(
|
| - const DictionaryValue* file_browser_handler, string16* error) {
|
| - scoped_ptr<FileBrowserHandler> result(
|
| - new FileBrowserHandler());
|
| - result->set_extension_id(id());
|
| +bool Extension::CheckMinimumChromeVersion(string16* error) {
|
| + if (!manifest_->HasKey(keys::kMinimumChromeVersion))
|
| + return true;
|
| + std::string minimum_version_string;
|
| + if (!manifest_->GetString(keys::kMinimumChromeVersion,
|
| + &minimum_version_string)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion);
|
| + return false;
|
| + }
|
|
|
| - std::string id;
|
| - // Read the file action |id| (mandatory).
|
| - if (!file_browser_handler->HasKey(keys::kPageActionId) ||
|
| - !file_browser_handler->GetString(keys::kPageActionId, &id)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidPageActionId);
|
| - return NULL;
|
| + scoped_ptr<Version> minimum_version(
|
| + Version::GetVersionFromString(minimum_version_string));
|
| + if (!minimum_version.get()) {
|
| + *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion);
|
| + return false;
|
| }
|
| - result->set_id(id);
|
|
|
| - // Read the page action title from |default_title| (mandatory).
|
| - std::string title;
|
| - if (!file_browser_handler->HasKey(keys::kPageActionDefaultTitle) ||
|
| - !file_browser_handler->GetString(keys::kPageActionDefaultTitle, &title)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle);
|
| - return NULL;
|
| + chrome::VersionInfo current_version_info;
|
| + if (!current_version_info.is_valid()) {
|
| + NOTREACHED();
|
| + return false;
|
| }
|
| - result->set_title(title);
|
|
|
| - // Initialize file filters (mandatory).
|
| - ListValue* list_value = NULL;
|
| - if (!file_browser_handler->HasKey(keys::kFileFilters) ||
|
| - !file_browser_handler->GetList(keys::kFileFilters, &list_value) ||
|
| - list_value->empty()) {
|
| - *error = ASCIIToUTF16(errors::kInvalidFileFiltersList);
|
| - return NULL;
|
| + scoped_ptr<Version> current_version(
|
| + Version::GetVersionFromString(current_version_info.Version()));
|
| + if (!current_version.get()) {
|
| + DCHECK(false);
|
| + return false;
|
| }
|
| - for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| - std::string filter;
|
| - if (!list_value->GetString(i, &filter)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidFileFilterValue, base::IntToString(i));
|
| - return NULL;
|
| - }
|
| - StringToLowerASCII(&filter);
|
| - URLPattern pattern(URLPattern::SCHEME_FILESYSTEM);
|
| - if (pattern.Parse(filter) != URLPattern::PARSE_SUCCESS) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidURLPatternError, filter);
|
| - return NULL;
|
| - }
|
| - std::string path = pattern.path();
|
| - bool allowed = path == "*" || path == "*.*" ||
|
| - (path.compare(0, 2, "*.") == 0 &&
|
| - path.find_first_of('*', 2) == std::string::npos);
|
| - if (!allowed) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidURLPatternError, filter);
|
| - return NULL;
|
| - }
|
| - result->AddPattern(pattern);
|
| +
|
| + if (current_version->CompareTo(*minimum_version) < 0) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kChromeVersionTooLow,
|
| + l10n_util::GetStringUTF8(IDS_PRODUCT_NAME),
|
| + minimum_version_string);
|
| + return false;
|
| }
|
| + return true;
|
| +}
|
|
|
| - std::string default_icon;
|
| - // Read the file browser action |default_icon| (optional).
|
| - if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) {
|
| - if (!file_browser_handler->GetString(
|
| - keys::kPageActionDefaultIcon, &default_icon) ||
|
| - default_icon.empty()) {
|
| - *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath);
|
| - return NULL;
|
| - }
|
| - result->set_icon_path(default_icon);
|
| +bool Extension::LoadRequiredFeatures(string16* error) {
|
| + if (!LoadName(error) ||
|
| + !LoadVersion(error))
|
| + return false;
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadName(string16* error) {
|
| + string16 localized_name;
|
| + if (!manifest_->GetString(keys::kName, &localized_name)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidName);
|
| + return false;
|
| + }
|
| + base::i18n::AdjustStringForLocaleDirection(&localized_name);
|
| + name_ = UTF16ToUTF8(localized_name);
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadDescription(string16* error) {
|
| + if (manifest_->HasKey(keys::kDescription) &&
|
| + !manifest_->GetString(keys::kDescription, &description_)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidDescription);
|
| + return false;
|
| }
|
| + return true;
|
| +}
|
|
|
| - return result.release();
|
| +bool Extension::LoadAppFeatures(string16* error) {
|
| + if (!LoadExtent(keys::kWebURLs, &extent_,
|
| + errors::kInvalidWebURLs, errors::kInvalidWebURL, error) ||
|
| + !LoadLaunchURL(error) ||
|
| + !LoadLaunchContainer(error))
|
| + return false;
|
| +
|
| + return true;
|
| }
|
|
|
| bool Extension::LoadExtent(const char* key,
|
| @@ -1164,30 +1211,7 @@ bool Extension::LoadLaunchURL(string16* error) {
|
| OverrideLaunchUrl(cloud_print_enable_connector_url);
|
| }
|
| }
|
| - return true;
|
| -}
|
|
|
| -bool ReadLaunchDimension(const extensions::Manifest* manifest,
|
| - const char* key,
|
| - int* target,
|
| - bool is_valid_container,
|
| - string16* error) {
|
| - Value* temp = NULL;
|
| - if (manifest->Get(key, &temp)) {
|
| - if (!is_valid_container) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidLaunchValueContainer,
|
| - key);
|
| - return false;
|
| - }
|
| - if (!temp->GetAsInteger(target) || *target < 0) {
|
| - *target = 0;
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidLaunchValue,
|
| - key);
|
| - return false;
|
| - }
|
| - }
|
| return true;
|
| }
|
|
|
| @@ -1214,9 +1238,9 @@ bool Extension::LoadLaunchContainer(string16* error) {
|
| }
|
|
|
| bool can_specify_initial_size =
|
| - launch_container() == extension_misc::LAUNCH_PANEL ||
|
| - launch_container() == extension_misc::LAUNCH_WINDOW ||
|
| - launch_container() == extension_misc::LAUNCH_SHELL;
|
| + launch_container_ == extension_misc::LAUNCH_PANEL ||
|
| + launch_container_ == extension_misc::LAUNCH_WINDOW ||
|
| + launch_container_ == extension_misc::LAUNCH_SHELL;
|
|
|
| // Validate the container width if present.
|
| if (!ReadLaunchDimension(manifest_,
|
| @@ -1235,7 +1259,7 @@ bool Extension::LoadLaunchContainer(string16* error) {
|
| return false;
|
|
|
| bool can_specify_size_range =
|
| - launch_container() == extension_misc::LAUNCH_SHELL;
|
| + launch_container_ == extension_misc::LAUNCH_SHELL;
|
|
|
| // Validate min size if present.
|
| if (!ReadLaunchDimension(manifest_,
|
| @@ -1263,7 +1287,7 @@ bool Extension::LoadLaunchContainer(string16* error) {
|
| error))
|
| return false;
|
|
|
| - if (launch_container() == extension_misc::LAUNCH_SHELL) {
|
| + if (launch_container_ == extension_misc::LAUNCH_SHELL) {
|
| if (!manifest_->Get(keys::kLaunchWidth, &temp)) {
|
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| errors::kInvalidLaunchValue,
|
| @@ -1290,173 +1314,373 @@ bool Extension::LoadLaunchContainer(string16* error) {
|
| }
|
| }
|
|
|
| + if (is_platform_app()) {
|
| + if (launch_container_ != extension_misc::LAUNCH_SHELL) {
|
| + *error = ASCIIToUTF16(errors::kInvalidLaunchContainerForPlatform);
|
| + return false;
|
| + }
|
| + } else if (launch_container_ == extension_misc::LAUNCH_SHELL) {
|
| + *error = ASCIIToUTF16(errors::kInvalidLaunchContainerForNonPlatform);
|
| + return false;
|
| + }
|
| +
|
| return true;
|
| }
|
|
|
| -bool Extension::LoadAppIsolation(string16* error) {
|
| - Value* temp = NULL;
|
| - if (!manifest_->Get(keys::kIsolation, &temp))
|
| - return true;
|
| +bool Extension::LoadSharedFeatures(
|
| + const ExtensionAPIPermissionSet& api_permissions,
|
| + string16* error) {
|
| + if (!LoadDescription(error) ||
|
| + !LoadManifestVersion(error) ||
|
| + !LoadHomepageURL(error) ||
|
| + !LoadUpdateURL(error) ||
|
| + !LoadIcons(error) ||
|
| + !LoadCommands(error) ||
|
| + !LoadPlugins(error) ||
|
| + !LoadNaClModules(error) ||
|
| + !LoadWebAccessibleResources(error) ||
|
| + !CheckRequirements(error) ||
|
| + !LoadDefaultLocale(error) ||
|
| + !LoadOfflineEnabled(error) ||
|
| + !LoadOptionsPage(error) ||
|
| + // LoadBackgroundScripts() must be called before LoadBackgroundPage().
|
| + !LoadBackgroundScripts(error) ||
|
| + !LoadBackgroundPage(api_permissions, error) ||
|
| + !LoadBackgroundPersistent(api_permissions, error) ||
|
| + !LoadBackgroundAllowJSAccess(api_permissions, error) ||
|
| + !LoadWebIntentServices(error))
|
| + return false;
|
|
|
| - if (temp->GetType() != Value::TYPE_LIST) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIsolation);
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadVersion(string16* error) {
|
| + std::string version_str;
|
| + if (!manifest_->GetString(keys::kVersion, &version_str)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidVersion);
|
| + return false;
|
| + }
|
| + version_.reset(Version::GetVersionFromString(version_str));
|
| + if (!version_.get() ||
|
| + version_->components().size() > 4) {
|
| + *error = ASCIIToUTF16(errors::kInvalidVersion);
|
| return false;
|
| }
|
| + return true;
|
| +}
|
|
|
| - ListValue* isolation_list = static_cast<ListValue*>(temp);
|
| - for (size_t i = 0; i < isolation_list->GetSize(); ++i) {
|
| - std::string isolation_string;
|
| - if (!isolation_list->GetString(i, &isolation_string)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidIsolationValue,
|
| - base::UintToString(i));
|
| +bool Extension::LoadManifestVersion(string16* error) {
|
| + // Get the original value out of the dictionary so that we can validate it
|
| + // more strictly.
|
| + if (manifest_->value()->HasKey(keys::kManifestVersion)) {
|
| + int manifest_version = 1;
|
| + if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) ||
|
| + manifest_version < 1) {
|
| + *error = ASCIIToUTF16(errors::kInvalidManifestVersion);
|
| return false;
|
| }
|
| + }
|
|
|
| - // Check for isolated storage.
|
| - if (isolation_string == values::kIsolatedStorage) {
|
| - is_storage_isolated_ = true;
|
| - } else {
|
| - DLOG(WARNING) << "Did not recognize isolation type: "
|
| - << isolation_string;
|
| - }
|
| + manifest_version_ = manifest_->GetManifestVersion();
|
| + if (creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION &&
|
| + manifest_version_ < kModernManifestVersion &&
|
| + !CommandLine::ForCurrentProcess()->HasSwitch(
|
| + switches::kAllowLegacyExtensionManifests)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidManifestVersion);
|
| + return false;
|
| }
|
| +
|
| return true;
|
| }
|
|
|
| -bool Extension::LoadWebIntentAction(const std::string& action_name,
|
| - const DictionaryValue& intent_service,
|
| - string16* error) {
|
| - DCHECK(error);
|
| - webkit_glue::WebIntentServiceData service;
|
| - std::string value;
|
| -
|
| - service.action = UTF8ToUTF16(action_name);
|
| +bool Extension::LoadHomepageURL(string16* error) {
|
| + if (!manifest_->HasKey(keys::kHomepageURL))
|
| + return true;
|
| + std::string tmp;
|
| + if (!manifest_->GetString(keys::kHomepageURL, &tmp)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidHomepageURL, "");
|
| + return false;
|
| + }
|
| + homepage_url_ = GURL(tmp);
|
| + if (!homepage_url_.is_valid() ||
|
| + (!homepage_url_.SchemeIs("http") &&
|
| + !homepage_url_.SchemeIs("https"))) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidHomepageURL, tmp);
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
|
|
| - ListValue* mime_types = NULL;
|
| - if (!intent_service.HasKey(keys::kIntentType) ||
|
| - !intent_service.GetList(keys::kIntentType, &mime_types) ||
|
| - mime_types->GetSize() == 0) {
|
| +bool Extension::LoadUpdateURL(string16* error) {
|
| + if (!manifest_->HasKey(keys::kUpdateURL))
|
| + return true;
|
| + std::string tmp;
|
| + if (!manifest_->GetString(keys::kUpdateURL, &tmp)) {
|
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidIntentType, action_name);
|
| + errors::kInvalidUpdateURL, "");
|
| + return false;
|
| + }
|
| + update_url_ = GURL(tmp);
|
| + if (!update_url_.is_valid() ||
|
| + update_url_.has_ref()) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidUpdateURL, tmp);
|
| return false;
|
| }
|
| + return true;
|
| +}
|
|
|
| - std::string href;
|
| - if (intent_service.HasKey(keys::kIntentPath)) {
|
| - if (!intent_service.GetString(keys::kIntentPath, &href)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIntentHref);
|
| - return false;
|
| - }
|
| +bool Extension::LoadIcons(string16* error) {
|
| + if (!manifest_->HasKey(keys::kIcons))
|
| + return true;
|
| + DictionaryValue* icons_value = NULL;
|
| + if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIcons);
|
| + return false;
|
| }
|
|
|
| - if (intent_service.HasKey(keys::kIntentHref)) {
|
| - if (!href.empty()) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidIntentHrefOldAndNewKey, action_name,
|
| - keys::kIntentPath, keys::kIntentHref);
|
| - return false;
|
| + for (size_t i = 0; i < ExtensionIconSet::kNumIconSizes; ++i) {
|
| + std::string key = base::IntToString(ExtensionIconSet::kIconSizes[i]);
|
| + if (icons_value->HasKey(key)) {
|
| + std::string icon_path;
|
| + if (!icons_value->GetString(key, &icon_path)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidIconPath, key);
|
| + return false;
|
| + }
|
| +
|
| + if (!icon_path.empty() && icon_path[0] == '/')
|
| + icon_path = icon_path.substr(1);
|
| +
|
| + if (icon_path.empty()) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidIconPath, key);
|
| + return false;
|
| + }
|
| + icons_.Add(ExtensionIconSet::kIconSizes[i], icon_path);
|
| }
|
| - if (!intent_service.GetString(keys::kIntentHref, &href)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIntentHref);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadCommands(string16* error) {
|
| + if (manifest_->HasKey(keys::kCommands)) {
|
| + DictionaryValue* commands = NULL;
|
| + if (!manifest_->GetDictionary(keys::kCommands, &commands)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidCommandsKey);
|
| return false;
|
| }
|
| - }
|
|
|
| - if (!href.empty()) {
|
| - GURL service_url(href);
|
| - if (is_hosted_app()) {
|
| - // Hosted apps require an absolute URL for intents.
|
| - if (!service_url.is_valid() ||
|
| - !(web_extent().MatchesURL(service_url))) {
|
| + int command_index = 0;
|
| + for (DictionaryValue::key_iterator iter = commands->begin_keys();
|
| + iter != commands->end_keys(); ++iter) {
|
| + ++command_index;
|
| +
|
| + DictionaryValue* command = NULL;
|
| + if (!commands->GetDictionary(*iter, &command)) {
|
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidIntentPageInHostedApp, action_name);
|
| + errors::kInvalidKeyBindingDictionary,
|
| + base::IntToString(command_index));
|
| return false;
|
| }
|
| - service.service_url = service_url;
|
| - } else {
|
| - // We do not allow absolute intent URLs in non-hosted apps.
|
| - if (service_url.is_valid()) {
|
| - *error =ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kCannotAccessPage, href);
|
| +
|
| + ExtensionKeybinding binding;
|
| + if (!binding.Parse(command, *iter, command_index, error))
|
| + return false; // |error| already set.
|
| +
|
| + commands_.push_back(binding);
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadPlugins(string16* error) {
|
| + if (!manifest_->HasKey(keys::kPlugins))
|
| + return true;
|
| + ListValue* list_value = NULL;
|
| + if (!manifest_->GetList(keys::kPlugins, &list_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidPlugins);
|
| + return false;
|
| + }
|
| +
|
| + for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| + DictionaryValue* plugin_value = NULL;
|
| + std::string path_str;
|
| + bool is_public = false;
|
| + if (!list_value->GetDictionary(i, &plugin_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidPlugins);
|
| + return false;
|
| + }
|
| + // Get plugins[i].path.
|
| + if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidPluginsPath, base::IntToString(i));
|
| + return false;
|
| + }
|
| +
|
| + // Get plugins[i].content (optional).
|
| + if (plugin_value->HasKey(keys::kPluginsPublic)) {
|
| + if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidPluginsPublic, base::IntToString(i));
|
| return false;
|
| }
|
| - service.service_url = GetResourceURL(href);
|
| }
|
| +
|
| + // We don't allow extension plugins to run on Chrome OS. We still
|
| + // parse the manifest entry so that error messages are consistently
|
| + // displayed across platforms.
|
| +#if !defined(OS_CHROMEOS)
|
| + plugins_.push_back(PluginInfo());
|
| + plugins_.back().path = path().Append(FilePath::FromUTF8Unsafe(path_str));
|
| + plugins_.back().is_public = is_public;
|
| +#endif
|
| }
|
| + return true;
|
| +}
|
|
|
| - if (intent_service.HasKey(keys::kIntentTitle) &&
|
| - !intent_service.GetString(keys::kIntentTitle, &service.title)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIntentTitle);
|
| +bool Extension::LoadNaClModules(string16* error) {
|
| + if (!manifest_->HasKey(keys::kNaClModules))
|
| + return true;
|
| + ListValue* list_value = NULL;
|
| + if (!manifest_->GetList(keys::kNaClModules, &list_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidNaClModules);
|
| return false;
|
| }
|
|
|
| - if (intent_service.HasKey(keys::kIntentDisposition)) {
|
| - if (!intent_service.GetString(keys::kIntentDisposition, &value) ||
|
| - (value != values::kIntentDispositionWindow &&
|
| - value != values::kIntentDispositionInline)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIntentDisposition);
|
| + for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| + DictionaryValue* module_value = NULL;
|
| + std::string path_str;
|
| + std::string mime_type;
|
| +
|
| + if (!list_value->GetDictionary(i, &module_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidNaClModules);
|
| return false;
|
| }
|
| - if (value == values::kIntentDispositionInline) {
|
| - service.disposition =
|
| - webkit_glue::WebIntentServiceData::DISPOSITION_INLINE;
|
| - } else {
|
| - service.disposition =
|
| - webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW;
|
| +
|
| + // Get nacl_modules[i].path.
|
| + if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidNaClModulesPath, base::IntToString(i));
|
| + return false;
|
| }
|
| +
|
| + // Get nacl_modules[i].mime_type.
|
| + if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidNaClModulesMIMEType, base::IntToString(i));
|
| + return false;
|
| + }
|
| +
|
| + nacl_modules_.push_back(NaClModuleInfo());
|
| + nacl_modules_.back().url = GetResourceURL(path_str);
|
| + nacl_modules_.back().mime_type = mime_type;
|
| }
|
|
|
| - for (size_t i = 0; i < mime_types->GetSize(); ++i) {
|
| - if (!mime_types->GetString(i, &service.type)) {
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadWebAccessibleResources(string16* error) {
|
| + if (!manifest_->HasKey(keys::kWebAccessibleResources))
|
| + return true;
|
| + ListValue* list_value;
|
| + if (!manifest_->GetList(keys::kWebAccessibleResources, &list_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList);
|
| + return false;
|
| + }
|
| + for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| + std::string relative_path;
|
| + if (!list_value->GetString(i, &relative_path)) {
|
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidIntentTypeElement, action_name,
|
| - std::string(base::IntToString(i)));
|
| + errors::kInvalidWebAccessibleResource, base::IntToString(i));
|
| return false;
|
| }
|
| - intents_services_.push_back(service);
|
| + if (relative_path[0] != '/')
|
| + relative_path = '/' + relative_path;
|
| + web_accessible_resources_.insert(relative_path);
|
| }
|
| +
|
| return true;
|
| }
|
|
|
| -bool Extension::LoadWebIntentServices(string16* error) {
|
| - DCHECK(error);
|
| +// These are not actually persisted (they're only used by the store), but
|
| +// still validated.
|
| +bool Extension::CheckRequirements(string16* error) {
|
| + if (!manifest_->HasKey(keys::kRequirements))
|
| + return true;
|
| + DictionaryValue* requirements_value = NULL;
|
| + if (!manifest_->GetDictionary(keys::kRequirements, &requirements_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidRequirements);
|
| + return false;
|
| + }
|
|
|
| - if (!manifest_->HasKey(keys::kIntents))
|
| + for (DictionaryValue::key_iterator it = requirements_value->begin_keys();
|
| + it != requirements_value->end_keys(); ++it) {
|
| + DictionaryValue* requirement_value;
|
| + if (!requirements_value->GetDictionaryWithoutPathExpansion(
|
| + *it, &requirement_value)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidRequirement, *it);
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadDefaultLocale(string16* error) {
|
| + if (!manifest_->HasKey(keys::kDefaultLocale))
|
| return true;
|
| + if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) ||
|
| + !l10n_util::IsValidLocaleSyntax(default_locale_)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidDefaultLocale);
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
|
|
| - DictionaryValue* all_services = NULL;
|
| - if (!manifest_->GetDictionary(keys::kIntents, &all_services)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIntents);
|
| +bool Extension::LoadOfflineEnabled(string16* error) {
|
| + // Defaults to false.
|
| + if (manifest_->HasKey(keys::kOfflineEnabled) &&
|
| + !manifest_->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidOfflineEnabled);
|
| return false;
|
| }
|
| + return true;
|
| +}
|
|
|
| - for (DictionaryValue::key_iterator iter(all_services->begin_keys());
|
| - iter != all_services->end_keys(); ++iter) {
|
| - // Any entry in the intents dictionary can either have a list of
|
| - // dictionaries, or just a single dictionary attached to that. Try
|
| - // lists first, fall back to single dictionary.
|
| - ListValue* service_list = NULL;
|
| - DictionaryValue* one_service = NULL;
|
| - if (all_services->GetListWithoutPathExpansion(*iter, &service_list)) {
|
| - for (size_t i = 0; i < service_list->GetSize(); ++i) {
|
| - if (!service_list->GetDictionary(i, &one_service)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIntent);
|
| - return false;
|
| - }
|
| - if (!LoadWebIntentAction(*iter, *one_service, error))
|
| - return false;
|
| - }
|
| - } else {
|
| - if (!all_services->GetDictionaryWithoutPathExpansion(*iter,
|
| - &one_service)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIntent);
|
| - return false;
|
| - }
|
| - if (!LoadWebIntentAction(*iter, *one_service, error))
|
| - return false;
|
| +bool Extension::LoadOptionsPage(string16* error) {
|
| + if (!manifest_->HasKey(keys::kOptionsPage))
|
| + return true;
|
| + std::string options_str;
|
| + if (!manifest_->GetString(keys::kOptionsPage, &options_str)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidOptionsPage);
|
| + return false;
|
| + }
|
| +
|
| + if (is_hosted_app()) {
|
| + // hosted apps require an absolute URL.
|
| + GURL options_url(options_str);
|
| + if (!options_url.is_valid() ||
|
| + !(options_url.SchemeIs("http") || options_url.SchemeIs("https"))) {
|
| + *error = ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp);
|
| + return false;
|
| + }
|
| + options_url_ = options_url;
|
| + } else {
|
| + GURL absolute(options_str);
|
| + if (absolute.is_valid()) {
|
| + *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage);
|
| + return false;
|
| + }
|
| + options_url_ = GetResourceURL(options_str);
|
| + if (!options_url_.is_valid()) {
|
| + *error = ASCIIToUTF16(errors::kInvalidOptionsPage);
|
| + return false;
|
| }
|
| }
|
| +
|
| return true;
|
| }
|
|
|
| @@ -1557,7 +1781,7 @@ bool Extension::LoadBackgroundPersistent(
|
| return true;
|
| }
|
|
|
| -bool Extension::LoadBackgroundAllowJsAccess(
|
| +bool Extension::LoadBackgroundAllowJSAccess(
|
| const ExtensionAPIPermissionSet& api_permissions,
|
| string16* error) {
|
| Value* allow_js_access = NULL;
|
| @@ -1578,1187 +1802,1100 @@ bool Extension::LoadBackgroundAllowJsAccess(
|
| return true;
|
| }
|
|
|
| -// static
|
| -bool Extension::IsTrustedId(const std::string& id) {
|
| - // See http://b/4946060 for more details.
|
| - return id == std::string("nckgahadagoaajjgafhacjanaoiihapd");
|
| -}
|
| -
|
| -Extension::Extension(const FilePath& path,
|
| - scoped_ptr<extensions::Manifest> manifest)
|
| - : manifest_version_(0),
|
| - incognito_split_mode_(false),
|
| - offline_enabled_(false),
|
| - converted_from_user_script_(false),
|
| - background_page_persists_(true),
|
| - allow_background_js_access_(true),
|
| - manifest_(manifest.release()),
|
| - is_storage_isolated_(false),
|
| - launch_container_(extension_misc::LAUNCH_TAB),
|
| - launch_width_(0),
|
| - launch_height_(0),
|
| - launch_min_width_(0),
|
| - launch_min_height_(0),
|
| - launch_max_width_(0),
|
| - launch_max_height_(0),
|
| - wants_file_access_(false),
|
| - creation_flags_(0) {
|
| - DCHECK(path.empty() || path.IsAbsolute());
|
| - path_ = MaybeNormalizePath(path);
|
| -}
|
| +bool Extension::LoadWebIntentAction(const std::string& action_name,
|
| + const DictionaryValue& intent_service,
|
| + string16* error) {
|
| + DCHECK(error);
|
| + webkit_glue::WebIntentServiceData service;
|
| + std::string value;
|
|
|
| -Extension::~Extension() {
|
| - if (manifest_)
|
| - delete manifest_;
|
| -}
|
| + service.action = UTF8ToUTF16(action_name);
|
|
|
| -ExtensionResource Extension::GetResource(
|
| - const std::string& relative_path) const {
|
| -#if defined(OS_POSIX)
|
| - FilePath relative_file_path(relative_path);
|
| -#elif defined(OS_WIN)
|
| - FilePath relative_file_path(UTF8ToWide(relative_path));
|
| -#endif
|
| - return ExtensionResource(id(), path(), relative_file_path);
|
| -}
|
| -
|
| -ExtensionResource Extension::GetResource(
|
| - const FilePath& relative_file_path) const {
|
| - return ExtensionResource(id(), path(), relative_file_path);
|
| -}
|
| -
|
| -// TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a
|
| -// util class in base:
|
| -// http://code.google.com/p/chromium/issues/detail?id=13572
|
| -bool Extension::ParsePEMKeyBytes(const std::string& input,
|
| - std::string* output) {
|
| - DCHECK(output);
|
| - if (!output)
|
| - return false;
|
| - if (input.length() == 0)
|
| - return false;
|
| -
|
| - std::string working = input;
|
| - if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) {
|
| - working = CollapseWhitespaceASCII(working, true);
|
| - size_t header_pos = working.find(kKeyInfoEndMarker,
|
| - sizeof(kKeyBeginHeaderMarker) - 1);
|
| - if (header_pos == std::string::npos)
|
| - return false;
|
| - size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1;
|
| - size_t end_pos = working.rfind(kKeyBeginFooterMarker);
|
| - if (end_pos == std::string::npos)
|
| - return false;
|
| - if (start_pos >= end_pos)
|
| - return false;
|
| -
|
| - working = working.substr(start_pos, end_pos - start_pos);
|
| - if (working.length() == 0)
|
| - return false;
|
| - }
|
| -
|
| - return base::Base64Decode(working, output);
|
| -}
|
| -
|
| -bool Extension::ProducePEM(const std::string& input, std::string* output) {
|
| - DCHECK(output);
|
| - if (input.length() == 0)
|
| - return false;
|
| -
|
| - return base::Base64Encode(input, output);
|
| -}
|
| -
|
| -bool Extension::FormatPEMForFileOutput(const std::string& input,
|
| - std::string* output,
|
| - bool is_public) {
|
| - DCHECK(output);
|
| - if (input.length() == 0)
|
| + ListValue* mime_types = NULL;
|
| + if (!intent_service.HasKey(keys::kIntentType) ||
|
| + !intent_service.GetList(keys::kIntentType, &mime_types) ||
|
| + mime_types->GetSize() == 0) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidIntentType, action_name);
|
| return false;
|
| - *output = "";
|
| - output->append(kKeyBeginHeaderMarker);
|
| - output->append(" ");
|
| - output->append(is_public ? kPublic : kPrivate);
|
| - output->append(" ");
|
| - output->append(kKeyInfoEndMarker);
|
| - output->append("\n");
|
| - for (size_t i = 0; i < input.length(); ) {
|
| - int slice = std::min<int>(input.length() - i, kPEMOutputColumns);
|
| - output->append(input.substr(i, slice));
|
| - output->append("\n");
|
| - i += slice;
|
| - }
|
| - output->append(kKeyBeginFooterMarker);
|
| - output->append(" ");
|
| - output->append(is_public ? kPublic : kPrivate);
|
| - output->append(" ");
|
| - output->append(kKeyInfoEndMarker);
|
| - output->append("\n");
|
| -
|
| - return true;
|
| -}
|
| -
|
| -// static
|
| -void Extension::DecodeIcon(const Extension* extension,
|
| - ExtensionIconSet::Icons preferred_icon_size,
|
| - ExtensionIconSet::MatchType match_type,
|
| - scoped_ptr<SkBitmap>* result) {
|
| - std::string path = extension->icons().Get(preferred_icon_size, match_type);
|
| - ExtensionIconSet::Icons size = extension->icons().GetIconSizeFromPath(path);
|
| - ExtensionResource icon_resource = extension->GetResource(path);
|
| - DecodeIconFromPath(icon_resource.GetFilePath(), size, result);
|
| -}
|
| -
|
| -// static
|
| -void Extension::DecodeIcon(const Extension* extension,
|
| - ExtensionIconSet::Icons icon_size,
|
| - scoped_ptr<SkBitmap>* result) {
|
| - DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result);
|
| -}
|
| -
|
| -// static
|
| -void Extension::DecodeIconFromPath(const FilePath& icon_path,
|
| - ExtensionIconSet::Icons icon_size,
|
| - scoped_ptr<SkBitmap>* result) {
|
| - if (icon_path.empty())
|
| - return;
|
| -
|
| - std::string file_contents;
|
| - if (!file_util::ReadFileToString(icon_path, &file_contents)) {
|
| - DLOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName();
|
| - return;
|
| - }
|
| -
|
| - // Decode the image using WebKit's image decoder.
|
| - const unsigned char* data =
|
| - reinterpret_cast<const unsigned char*>(file_contents.data());
|
| - webkit_glue::ImageDecoder decoder;
|
| - scoped_ptr<SkBitmap> decoded(new SkBitmap());
|
| - *decoded = decoder.Decode(data, file_contents.length());
|
| - if (decoded->empty()) {
|
| - DLOG(ERROR) << "Could not decode icon file: "
|
| - << icon_path.LossyDisplayName();
|
| - return;
|
| - }
|
| -
|
| - if (decoded->width() != icon_size || decoded->height() != icon_size) {
|
| - DLOG(ERROR) << "Icon file has unexpected size: "
|
| - << base::IntToString(decoded->width()) << "x"
|
| - << base::IntToString(decoded->height());
|
| - return;
|
| }
|
|
|
| - result->swap(decoded);
|
| -}
|
| -
|
| -// static
|
| -const SkBitmap& Extension::GetDefaultIcon(bool is_app) {
|
| - if (is_app) {
|
| - return *ResourceBundle::GetSharedInstance().GetBitmapNamed(
|
| - IDR_APP_DEFAULT_ICON);
|
| - } else {
|
| - return *ResourceBundle::GetSharedInstance().GetBitmapNamed(
|
| - IDR_EXTENSION_DEFAULT_ICON);
|
| + std::string href;
|
| + if (intent_service.HasKey(keys::kIntentPath)) {
|
| + if (!intent_service.GetString(keys::kIntentPath, &href)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIntentHref);
|
| + return false;
|
| + }
|
| }
|
| -}
|
| -
|
| -GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
|
| - return GURL(std::string(chrome::kExtensionScheme) +
|
| - chrome::kStandardSchemeSeparator + extension_id + "/");
|
| -}
|
|
|
| -bool Extension::LoadManifestVersion(string16* error) {
|
| - // Get the original value out of the dictionary so that we can validate it
|
| - // more strictly.
|
| - if (manifest_->value()->HasKey(keys::kManifestVersion)) {
|
| - int manifest_version = 1;
|
| - if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) ||
|
| - manifest_version < 1) {
|
| - *error = ASCIIToUTF16(errors::kInvalidManifestVersion);
|
| + if (intent_service.HasKey(keys::kIntentHref)) {
|
| + if (!href.empty()) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidIntentHrefOldAndNewKey, action_name,
|
| + keys::kIntentPath, keys::kIntentHref);
|
| + return false;
|
| + }
|
| + if (!intent_service.GetString(keys::kIntentHref, &href)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIntentHref);
|
| return false;
|
| }
|
| }
|
|
|
| - manifest_version_ = manifest_->GetManifestVersion();
|
| - if (creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION &&
|
| - manifest_version_ < kModernManifestVersion &&
|
| - !CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kAllowLegacyExtensionManifests)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidManifestVersion);
|
| - return false;
|
| + if (!href.empty()) {
|
| + GURL service_url(href);
|
| + if (is_hosted_app()) {
|
| + // Hosted apps require an absolute URL for intents.
|
| + if (!service_url.is_valid() ||
|
| + !(web_extent().MatchesURL(service_url))) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidIntentPageInHostedApp, action_name);
|
| + return false;
|
| + }
|
| + service.service_url = service_url;
|
| + } else {
|
| + // We do not allow absolute intent URLs in non-hosted apps.
|
| + if (service_url.is_valid()) {
|
| + *error =ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kCannotAccessPage, href);
|
| + return false;
|
| + }
|
| + service.service_url = GetResourceURL(href);
|
| + }
|
| }
|
|
|
| - return true;
|
| -}
|
| -
|
| -// static
|
| -bool Extension::InitExtensionID(extensions::Manifest* manifest,
|
| - const FilePath& path,
|
| - const std::string& explicit_id,
|
| - int creation_flags,
|
| - string16* error) {
|
| - if (!explicit_id.empty()) {
|
| - manifest->set_extension_id(explicit_id);
|
| - return true;
|
| + if (intent_service.HasKey(keys::kIntentTitle) &&
|
| + !intent_service.GetString(keys::kIntentTitle, &service.title)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIntentTitle);
|
| + return false;
|
| }
|
|
|
| - if (manifest->HasKey(keys::kPublicKey)) {
|
| - std::string public_key;
|
| - std::string public_key_bytes;
|
| - std::string extension_id;
|
| - if (!manifest->GetString(keys::kPublicKey, &public_key) ||
|
| - !ParsePEMKeyBytes(public_key, &public_key_bytes) ||
|
| - !GenerateId(public_key_bytes, &extension_id)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidKey);
|
| + if (intent_service.HasKey(keys::kIntentDisposition)) {
|
| + if (!intent_service.GetString(keys::kIntentDisposition, &value) ||
|
| + (value != values::kIntentDispositionWindow &&
|
| + value != values::kIntentDispositionInline)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIntentDisposition);
|
| return false;
|
| }
|
| - manifest->set_extension_id(extension_id);
|
| - return true;
|
| + if (value == values::kIntentDispositionInline) {
|
| + service.disposition =
|
| + webkit_glue::WebIntentServiceData::DISPOSITION_INLINE;
|
| + } else {
|
| + service.disposition =
|
| + webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW;
|
| + }
|
| }
|
|
|
| - if (creation_flags & REQUIRE_KEY) {
|
| - *error = ASCIIToUTF16(errors::kInvalidKey);
|
| - return false;
|
| - } else {
|
| - // If there is a path, we generate the ID from it. This is useful for
|
| - // development mode, because it keeps the ID stable across restarts and
|
| - // reloading the extension.
|
| - std::string extension_id = GenerateIdForPath(path);
|
| - if (extension_id.empty()) {
|
| - NOTREACHED() << "Could not create ID from path.";
|
| + for (size_t i = 0; i < mime_types->GetSize(); ++i) {
|
| + if (!mime_types->GetString(i, &service.type)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidIntentTypeElement, action_name,
|
| + std::string(base::IntToString(i)));
|
| return false;
|
| }
|
| - manifest->set_extension_id(extension_id);
|
| - return true;
|
| + intents_services_.push_back(service);
|
| }
|
| + return true;
|
| }
|
|
|
| -bool Extension::InitFromValue(int flags, string16* error) {
|
| +bool Extension::LoadWebIntentServices(string16* error) {
|
| DCHECK(error);
|
|
|
| - base::AutoLock auto_lock(runtime_data_lock_);
|
| -
|
| - // Initialize permissions with an empty, default permission set.
|
| - runtime_data_.SetActivePermissions(new ExtensionPermissionSet());
|
| - optional_permission_set_ = new ExtensionPermissionSet();
|
| - required_permission_set_ = new ExtensionPermissionSet();
|
| -
|
| - creation_flags_ = flags;
|
| -
|
| - if (!LoadManifestVersion(error))
|
| - return false;
|
| -
|
| - // We don't ned to validate because InitExtensionID already did that.
|
| - manifest_->GetString(keys::kPublicKey, &public_key_);
|
| -
|
| - // Initialize the URL.
|
| - extension_url_ = Extension::GetBaseURLFromExtensionId(id());
|
| + if (!manifest_->HasKey(keys::kIntents))
|
| + return true;
|
|
|
| - // Initialize version.
|
| - std::string version_str;
|
| - if (!manifest_->GetString(keys::kVersion, &version_str)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidVersion);
|
| - return false;
|
| - }
|
| - version_.reset(Version::GetVersionFromString(version_str));
|
| - if (!version_.get() ||
|
| - version_->components().size() > 4) {
|
| - *error = ASCIIToUTF16(errors::kInvalidVersion);
|
| + DictionaryValue* all_services = NULL;
|
| + if (!manifest_->GetDictionary(keys::kIntents, &all_services)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIntents);
|
| return false;
|
| }
|
|
|
| - // Initialize name.
|
| - string16 localized_name;
|
| - if (!manifest_->GetString(keys::kName, &localized_name)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidName);
|
| - return false;
|
| + for (DictionaryValue::key_iterator iter(all_services->begin_keys());
|
| + iter != all_services->end_keys(); ++iter) {
|
| + // Any entry in the intents dictionary can either have a list of
|
| + // dictionaries, or just a single dictionary attached to that. Try
|
| + // lists first, fall back to single dictionary.
|
| + ListValue* service_list = NULL;
|
| + DictionaryValue* one_service = NULL;
|
| + if (all_services->GetListWithoutPathExpansion(*iter, &service_list)) {
|
| + for (size_t i = 0; i < service_list->GetSize(); ++i) {
|
| + if (!service_list->GetDictionary(i, &one_service)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIntent);
|
| + return false;
|
| + }
|
| + if (!LoadWebIntentAction(*iter, *one_service, error))
|
| + return false;
|
| + }
|
| + } else {
|
| + if (!all_services->GetDictionaryWithoutPathExpansion(*iter,
|
| + &one_service)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIntent);
|
| + return false;
|
| + }
|
| + if (!LoadWebIntentAction(*iter, *one_service, error))
|
| + return false;
|
| + }
|
| }
|
| - base::i18n::AdjustStringForLocaleDirection(&localized_name);
|
| - name_ = UTF16ToUTF8(localized_name);
|
| + return true;
|
| +}
|
| +bool Extension::LoadExtensionFeatures(
|
| + const ExtensionAPIPermissionSet& api_permissions,
|
| + string16* error) {
|
| + if (manifest_->HasKey(keys::kConvertedFromUserScript))
|
| + manifest_->GetBoolean(keys::kConvertedFromUserScript,
|
| + &converted_from_user_script_);
|
|
|
| - // 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 (is_app() &&
|
| - (!LoadExtent(keys::kWebURLs, &extent_,errors::kInvalidWebURLs,
|
| - errors::kInvalidWebURL, error) ||
|
| - !LoadLaunchURL(error) ||
|
| - !LoadLaunchContainer(error))) {
|
| + if (!LoadDevToolsPage(error) ||
|
| + !LoadInputComponents(api_permissions, error) ||
|
| + !LoadContentScripts(error) ||
|
| + !LoadPageAction(error) ||
|
| + !LoadBrowserAction(error) ||
|
| + !LoadFileBrowserHandlers(error) ||
|
| + !LoadChromeURLOverrides(error) ||
|
| + !LoadOmnibox(error) ||
|
| + !LoadTextToSpeechVoices(error) ||
|
| + !LoadIncognitoMode(error) ||
|
| + !LoadContentSecurityPolicy(error))
|
| return false;
|
| - }
|
|
|
| - if (is_platform_app()) {
|
| - if (launch_container() != extension_misc::LAUNCH_SHELL) {
|
| - *error = ASCIIToUTF16(errors::kInvalidLaunchContainerForPlatform);
|
| - return false;
|
| - }
|
| - } else if (launch_container() == extension_misc::LAUNCH_SHELL) {
|
| - *error = ASCIIToUTF16(errors::kInvalidLaunchContainerForNonPlatform);
|
| - return false;
|
| - }
|
| + return true;
|
| +}
|
|
|
| - // Initialize the permissions (optional).
|
| - ExtensionAPIPermissionSet api_permissions;
|
| - URLPatternSet host_permissions;
|
| - if (!ParsePermissions(keys::kPermissions,
|
| - flags,
|
| - error,
|
| - &api_permissions,
|
| - &host_permissions)) {
|
| +bool Extension::LoadDevToolsPage(string16* error) {
|
| + if (!manifest_->HasKey(keys::kDevToolsPage))
|
| + return true;
|
| + std::string devtools_str;
|
| + if (!manifest_->GetString(keys::kDevToolsPage, &devtools_str)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidDevToolsPage);
|
| return false;
|
| }
|
| -
|
| - // Initialize the optional permissions (optional).
|
| - ExtensionAPIPermissionSet optional_api_permissions;
|
| - URLPatternSet optional_host_permissions;
|
| - if (!ParsePermissions(keys::kOptionalPermissions,
|
| - flags,
|
| - error,
|
| - &optional_api_permissions,
|
| - &optional_host_permissions)) {
|
| + devtools_url_ = GetResourceURL(devtools_str);
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadInputComponents(
|
| + const ExtensionAPIPermissionSet& api_permissions,
|
| + string16* error) {
|
| + if (!manifest_->HasKey(keys::kInputComponents))
|
| + return true;
|
| + ListValue* list_value = NULL;
|
| + if (!manifest_->GetList(keys::kInputComponents, &list_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidInputComponents);
|
| return false;
|
| }
|
|
|
| - // Initialize description (if present).
|
| - if (manifest_->HasKey(keys::kDescription)) {
|
| - if (!manifest_->GetString(keys::kDescription, &description_)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidDescription);
|
| + for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| + DictionaryValue* module_value = NULL;
|
| + std::string name_str;
|
| + InputComponentType type;
|
| + std::string id_str;
|
| + std::string description_str;
|
| + std::string language_str;
|
| + std::set<std::string> layouts;
|
| + std::string shortcut_keycode_str;
|
| + bool shortcut_alt = false;
|
| + bool shortcut_ctrl = false;
|
| + bool shortcut_shift = false;
|
| +
|
| + if (!list_value->GetDictionary(i, &module_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidInputComponents);
|
| return false;
|
| }
|
| - }
|
|
|
| - // Initialize homepage url (if present).
|
| - if (manifest_->HasKey(keys::kHomepageURL)) {
|
| - std::string tmp;
|
| - if (!manifest_->GetString(keys::kHomepageURL, &tmp)) {
|
| + // Get input_components[i].name.
|
| + if (!module_value->GetString(keys::kName, &name_str)) {
|
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidHomepageURL, "");
|
| + errors::kInvalidInputComponentName, base::IntToString(i));
|
| return false;
|
| }
|
| - homepage_url_ = GURL(tmp);
|
| - if (!homepage_url_.is_valid() ||
|
| - (!homepage_url_.SchemeIs("http") &&
|
| - !homepage_url_.SchemeIs("https"))) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidHomepageURL, tmp);
|
| - return false;
|
| - }
|
| - }
|
|
|
| - // Initialize update url (if present).
|
| - if (manifest_->HasKey(keys::kUpdateURL)) {
|
| - std::string tmp;
|
| - if (!manifest_->GetString(keys::kUpdateURL, &tmp)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidUpdateURL, "");
|
| - return false;
|
| - }
|
| - update_url_ = GURL(tmp);
|
| - if (!update_url_.is_valid() ||
|
| - update_url_.has_ref()) {
|
| + // Get input_components[i].type.
|
| + std::string type_str;
|
| + if (module_value->GetString(keys::kType, &type_str)) {
|
| + if (type_str == "ime") {
|
| + type = INPUT_COMPONENT_TYPE_IME;
|
| + } else if (type_str == "virtual_keyboard") {
|
| + if (!api_permissions.count(ExtensionAPIPermission::kExperimental)) {
|
| + // Virtual Keyboards require the experimental flag.
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidInputComponentType, base::IntToString(i));
|
| + return false;
|
| + }
|
| + type = INPUT_COMPONENT_TYPE_VIRTUAL_KEYBOARD;
|
| + } else {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidInputComponentType, base::IntToString(i));
|
| + return false;
|
| + }
|
| + } else {
|
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidUpdateURL, tmp);
|
| - return false;
|
| - }
|
| - }
|
| -
|
| - // Validate minimum Chrome version (if present). We don't need to store this,
|
| - // since the extension is not valid if it is incorrect.
|
| - if (manifest_->HasKey(keys::kMinimumChromeVersion)) {
|
| - std::string minimum_version_string;
|
| - if (!manifest_->GetString(keys::kMinimumChromeVersion,
|
| - &minimum_version_string)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion);
|
| + errors::kInvalidInputComponentType, base::IntToString(i));
|
| return false;
|
| }
|
|
|
| - scoped_ptr<Version> minimum_version(
|
| - Version::GetVersionFromString(minimum_version_string));
|
| - if (!minimum_version.get()) {
|
| - *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion);
|
| - return false;
|
| + // Get input_components[i].id.
|
| + if (!module_value->GetString(keys::kId, &id_str)) {
|
| + id_str = "";
|
| }
|
|
|
| - chrome::VersionInfo current_version_info;
|
| - if (!current_version_info.is_valid()) {
|
| - NOTREACHED();
|
| + // Get input_components[i].description.
|
| + if (!module_value->GetString(keys::kDescription, &description_str)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidInputComponentDescription, base::IntToString(i));
|
| return false;
|
| }
|
| -
|
| - scoped_ptr<Version> current_version(
|
| - Version::GetVersionFromString(current_version_info.Version()));
|
| - if (!current_version.get()) {
|
| - DCHECK(false);
|
| - return false;
|
| + // Get input_components[i].language.
|
| + if (!module_value->GetString(keys::kLanguage, &language_str)) {
|
| + language_str = "";
|
| }
|
|
|
| - if (current_version->CompareTo(*minimum_version) < 0) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kChromeVersionTooLow,
|
| - l10n_util::GetStringUTF8(IDS_PRODUCT_NAME),
|
| - minimum_version_string);
|
| + // Get input_components[i].layouts.
|
| + ListValue* layouts_value = NULL;
|
| + if (!module_value->GetList(keys::kLayouts, &layouts_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidInputComponentLayouts);
|
| return false;
|
| }
|
| - }
|
| -
|
| - // Initialize converted_from_user_script (if present)
|
| - if (manifest_->HasKey(keys::kConvertedFromUserScript))
|
| - manifest_->GetBoolean(keys::kConvertedFromUserScript,
|
| - &converted_from_user_script_);
|
|
|
| - // Initialize commands (if present).
|
| - if (manifest_->HasKey(keys::kCommands)) {
|
| - DictionaryValue* commands = NULL;
|
| - if (!manifest_->GetDictionary(keys::kCommands, &commands)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidCommandsKey);
|
| - return false;
|
| + for (size_t j = 0; j < layouts_value->GetSize(); ++j) {
|
| + std::string layout_name_str;
|
| + if (!layouts_value->GetString(j, &layout_name_str)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidInputComponentLayoutName, base::IntToString(i),
|
| + base::IntToString(j));
|
| + return false;
|
| + }
|
| + layouts.insert(layout_name_str);
|
| }
|
|
|
| - int command_index = 0;
|
| - for (DictionaryValue::key_iterator iter = commands->begin_keys();
|
| - iter != commands->end_keys(); ++iter) {
|
| - ++command_index;
|
| + if (module_value->HasKey(keys::kShortcutKey)) {
|
| + DictionaryValue* shortcut_value = NULL;
|
| + if (!module_value->GetDictionary(keys::kShortcutKey, &shortcut_value)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidInputComponentShortcutKey, base::IntToString(i));
|
| + return false;
|
| + }
|
|
|
| - DictionaryValue* command = NULL;
|
| - if (!commands->GetDictionary(*iter, &command)) {
|
| + // Get input_components[i].shortcut_keycode.
|
| + if (!shortcut_value->GetString(keys::kKeycode, &shortcut_keycode_str)) {
|
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidKeyBindingDictionary,
|
| - base::IntToString(command_index));
|
| + errors::kInvalidInputComponentShortcutKeycode,
|
| + base::IntToString(i));
|
| return false;
|
| }
|
|
|
| - ExtensionKeybinding binding;
|
| - if (!binding.Parse(command, *iter, command_index, error))
|
| - return false; // |error| already set.
|
| + // Get input_components[i].shortcut_alt.
|
| + if (!shortcut_value->GetBoolean(keys::kAltKey, &shortcut_alt)) {
|
| + shortcut_alt = false;
|
| + }
|
|
|
| - commands_.push_back(binding);
|
| - }
|
| - }
|
| + // Get input_components[i].shortcut_ctrl.
|
| + if (!shortcut_value->GetBoolean(keys::kCtrlKey, &shortcut_ctrl)) {
|
| + shortcut_ctrl = false;
|
| + }
|
|
|
| - // Initialize icons (if present).
|
| - if (manifest_->HasKey(keys::kIcons)) {
|
| - DictionaryValue* icons_value = NULL;
|
| - if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIcons);
|
| - return false;
|
| + // Get input_components[i].shortcut_shift.
|
| + if (!shortcut_value->GetBoolean(keys::kShiftKey, &shortcut_shift)) {
|
| + shortcut_shift = false;
|
| + }
|
| }
|
|
|
| - for (size_t i = 0; i < ExtensionIconSet::kNumIconSizes; ++i) {
|
| - std::string key = base::IntToString(ExtensionIconSet::kIconSizes[i]);
|
| - if (icons_value->HasKey(key)) {
|
| - std::string icon_path;
|
| - if (!icons_value->GetString(key, &icon_path)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidIconPath, key);
|
| - return false;
|
| - }
|
| + input_components_.push_back(InputComponentInfo());
|
| + input_components_.back().name = name_str;
|
| + input_components_.back().type = type;
|
| + input_components_.back().id = id_str;
|
| + input_components_.back().description = description_str;
|
| + input_components_.back().language = language_str;
|
| + input_components_.back().layouts.insert(layouts.begin(), layouts.end());
|
| + input_components_.back().shortcut_keycode = shortcut_keycode_str;
|
| + input_components_.back().shortcut_alt = shortcut_alt;
|
| + input_components_.back().shortcut_ctrl = shortcut_ctrl;
|
| + input_components_.back().shortcut_shift = shortcut_shift;
|
| + }
|
|
|
| - if (!icon_path.empty() && icon_path[0] == '/')
|
| - icon_path = icon_path.substr(1);
|
| + return true;
|
| +}
|
|
|
| - if (icon_path.empty()) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidIconPath, key);
|
| - return false;
|
| - }
|
| +bool Extension::LoadContentScripts(string16* error) {
|
| + if (!manifest_->HasKey(keys::kContentScripts))
|
| + return true;
|
| + ListValue* list_value;
|
| + if (!manifest_->GetList(keys::kContentScripts, &list_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidContentScriptsList);
|
| + return false;
|
| + }
|
|
|
| - icons_.Add(ExtensionIconSet::kIconSizes[i], icon_path);
|
| - }
|
| + for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| + DictionaryValue* content_script = NULL;
|
| + if (!list_value->GetDictionary(i, &content_script)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidContentScript, base::IntToString(i));
|
| + return false;
|
| + }
|
| +
|
| + UserScript script;
|
| + if (!LoadUserScriptHelper(content_script, i, error, &script))
|
| + return false; // Failed to parse script context definition.
|
| + script.set_extension_id(id());
|
| + if (converted_from_user_script_) {
|
| + script.set_emulate_greasemonkey(true);
|
| + script.set_match_all_frames(true); // Greasemonkey matches all frames.
|
| }
|
| + content_scripts_.push_back(script);
|
| }
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadPageAction(string16* error) {
|
| + DictionaryValue* page_action_value = NULL;
|
|
|
| - // Initialize themes (if present).
|
| - if (manifest_->HasKey(keys::kTheme)) {
|
| - DictionaryValue* theme_value = NULL;
|
| - if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTheme);
|
| + if (manifest_->HasKey(keys::kPageActions)) {
|
| + ListValue* list_value = NULL;
|
| + if (!manifest_->GetList(keys::kPageActions, &list_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidPageActionsList);
|
| return false;
|
| }
|
|
|
| - DictionaryValue* images_value = NULL;
|
| - if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) {
|
| - // Validate that the images are all strings
|
| - for (DictionaryValue::key_iterator iter = images_value->begin_keys();
|
| - iter != images_value->end_keys(); ++iter) {
|
| - std::string val;
|
| - if (!images_value->GetString(*iter, &val)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidThemeImages);
|
| - return false;
|
| - }
|
| - }
|
| - theme_images_.reset(images_value->DeepCopy());
|
| - }
|
| -
|
| - DictionaryValue* colors_value = NULL;
|
| - if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) {
|
| - // Validate that the colors are RGB or RGBA lists
|
| - for (DictionaryValue::key_iterator iter = colors_value->begin_keys();
|
| - iter != colors_value->end_keys(); ++iter) {
|
| - ListValue* color_list = NULL;
|
| - double alpha = 0.0;
|
| - int color = 0;
|
| - // The color must be a list
|
| - if (!colors_value->GetListWithoutPathExpansion(*iter, &color_list) ||
|
| - // And either 3 items (RGB) or 4 (RGBA)
|
| - ((color_list->GetSize() != 3) &&
|
| - ((color_list->GetSize() != 4) ||
|
| - // For RGBA, the fourth item must be a real or int alpha value.
|
| - // Note that GetDouble() can get an integer value.
|
| - !color_list->GetDouble(3, &alpha))) ||
|
| - // For both RGB and RGBA, the first three items must be ints (R,G,B)
|
| - !color_list->GetInteger(0, &color) ||
|
| - !color_list->GetInteger(1, &color) ||
|
| - !color_list->GetInteger(2, &color)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidThemeColors);
|
| - return false;
|
| - }
|
| - }
|
| - theme_colors_.reset(colors_value->DeepCopy());
|
| - }
|
| -
|
| - DictionaryValue* tints_value = NULL;
|
| - if (theme_value->GetDictionary(keys::kThemeTints, &tints_value)) {
|
| - // Validate that the tints are all reals.
|
| - for (DictionaryValue::key_iterator iter = tints_value->begin_keys();
|
| - iter != tints_value->end_keys(); ++iter) {
|
| - ListValue* tint_list = NULL;
|
| - double v = 0.0;
|
| - if (!tints_value->GetListWithoutPathExpansion(*iter, &tint_list) ||
|
| - tint_list->GetSize() != 3 ||
|
| - !tint_list->GetDouble(0, &v) ||
|
| - !tint_list->GetDouble(1, &v) ||
|
| - !tint_list->GetDouble(2, &v)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidThemeTints);
|
| - return false;
|
| - }
|
| + size_t list_value_length = list_value->GetSize();
|
| +
|
| + if (list_value_length == 0u) {
|
| + // A list with zero items is allowed, and is equivalent to not having
|
| + // a page_actions key in the manifest. Don't set |page_action_value|.
|
| + } else if (list_value_length == 1u) {
|
| + if (!list_value->GetDictionary(0, &page_action_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidPageAction);
|
| + return false;
|
| }
|
| - theme_tints_.reset(tints_value->DeepCopy());
|
| + } else { // list_value_length > 1u.
|
| + *error = ASCIIToUTF16(errors::kInvalidPageActionsListSize);
|
| + return false;
|
| }
|
| -
|
| - DictionaryValue* display_properties_value = NULL;
|
| - if (theme_value->GetDictionary(keys::kThemeDisplayProperties,
|
| - &display_properties_value)) {
|
| - theme_display_properties_.reset(
|
| - display_properties_value->DeepCopy());
|
| + } else if (manifest_->HasKey(keys::kPageAction)) {
|
| + if (!manifest_->GetDictionary(keys::kPageAction, &page_action_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidPageAction);
|
| + return false;
|
| }
|
| + }
|
| +
|
| + // If page_action_value is not NULL, then there was a valid page action.
|
| + if (page_action_value) {
|
| + page_action_.reset(
|
| + LoadExtensionActionHelper(page_action_value, error));
|
| + if (!page_action_.get())
|
| + return false; // Failed to parse page action definition.
|
| + }
|
| +
|
| + return true;
|
| +}
|
|
|
| +bool Extension::LoadBrowserAction(string16* error) {
|
| + if (!manifest_->HasKey(keys::kBrowserAction))
|
| return true;
|
| + DictionaryValue* browser_action_value = NULL;
|
| + if (!manifest_->GetDictionary(keys::kBrowserAction, &browser_action_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidBrowserAction);
|
| + return false;
|
| }
|
|
|
| - // Initialize plugins (optional).
|
| - if (manifest_->HasKey(keys::kPlugins)) {
|
| - ListValue* list_value = NULL;
|
| - if (!manifest_->GetList(keys::kPlugins, &list_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidPlugins);
|
| - return false;
|
| + browser_action_.reset(
|
| + LoadExtensionActionHelper(browser_action_value, error));
|
| + if (!browser_action_.get())
|
| + return false; // Failed to parse browser action definition.
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadFileBrowserHandlers(string16* error) {
|
| + if (!manifest_->HasKey(keys::kFileBrowserHandlers))
|
| + return true;
|
| + ListValue* file_browser_handlers_value = NULL;
|
| + if (!manifest_->GetList(keys::kFileBrowserHandlers,
|
| + &file_browser_handlers_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
|
| + return false;
|
| + }
|
| + file_browser_handlers_.reset(
|
| + LoadFileBrowserHandlersHelper(file_browser_handlers_value, error));
|
| + if (!file_browser_handlers_.get())
|
| + return false; // Failed to parse file browser actions definition.
|
| + return true;
|
| +}
|
| +
|
| +Extension::FileBrowserHandlerList* Extension::LoadFileBrowserHandlersHelper(
|
| + const ListValue* extension_actions, string16* error) {
|
| + scoped_ptr<FileBrowserHandlerList> result(
|
| + new FileBrowserHandlerList());
|
| + for (ListValue::const_iterator iter = extension_actions->begin();
|
| + iter != extension_actions->end();
|
| + ++iter) {
|
| + if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
|
| + return NULL;
|
| }
|
| + scoped_ptr<FileBrowserHandler> action(
|
| + LoadFileBrowserHandler(
|
| + reinterpret_cast<DictionaryValue*>(*iter), error));
|
| + if (!action.get())
|
| + return NULL; // Failed to parse file browser action definition.
|
| + result->push_back(linked_ptr<FileBrowserHandler>(action.release()));
|
| + }
|
| + return result.release();
|
| +}
|
|
|
| - for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| - DictionaryValue* plugin_value = NULL;
|
| - std::string path_str;
|
| - bool is_public = false;
|
| +FileBrowserHandler* Extension::LoadFileBrowserHandler(
|
| + const DictionaryValue* file_browser_handler, string16* error) {
|
| + scoped_ptr<FileBrowserHandler> result(
|
| + new FileBrowserHandler());
|
| + result->set_extension_id(id());
|
|
|
| - if (!list_value->GetDictionary(i, &plugin_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidPlugins);
|
| - return false;
|
| - }
|
| + std::string id;
|
| + // Read the file action |id| (mandatory).
|
| + if (!file_browser_handler->HasKey(keys::kPageActionId) ||
|
| + !file_browser_handler->GetString(keys::kPageActionId, &id)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidPageActionId);
|
| + return NULL;
|
| + }
|
| + result->set_id(id);
|
|
|
| - // Get plugins[i].path.
|
| - if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidPluginsPath, base::IntToString(i));
|
| - return false;
|
| - }
|
| + // Read the page action title from |default_title| (mandatory).
|
| + std::string title;
|
| + if (!file_browser_handler->HasKey(keys::kPageActionDefaultTitle) ||
|
| + !file_browser_handler->GetString(keys::kPageActionDefaultTitle, &title)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle);
|
| + return NULL;
|
| + }
|
| + result->set_title(title);
|
|
|
| - // Get plugins[i].content (optional).
|
| - if (plugin_value->HasKey(keys::kPluginsPublic)) {
|
| - if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidPluginsPublic, base::IntToString(i));
|
| - return false;
|
| - }
|
| - }
|
| + // Initialize file filters (mandatory).
|
| + ListValue* list_value = NULL;
|
| + if (!file_browser_handler->HasKey(keys::kFileFilters) ||
|
| + !file_browser_handler->GetList(keys::kFileFilters, &list_value) ||
|
| + list_value->empty()) {
|
| + *error = ASCIIToUTF16(errors::kInvalidFileFiltersList);
|
| + return NULL;
|
| + }
|
| + for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| + std::string filter;
|
| + if (!list_value->GetString(i, &filter)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidFileFilterValue, base::IntToString(i));
|
| + return NULL;
|
| + }
|
| + StringToLowerASCII(&filter);
|
| + URLPattern pattern(URLPattern::SCHEME_FILESYSTEM);
|
| + if (pattern.Parse(filter) != URLPattern::PARSE_SUCCESS) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidURLPatternError, filter);
|
| + return NULL;
|
| + }
|
| + std::string path = pattern.path();
|
| + bool allowed = path == "*" || path == "*.*" ||
|
| + (path.compare(0, 2, "*.") == 0 &&
|
| + path.find_first_of('*', 2) == std::string::npos);
|
| + if (!allowed) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidURLPatternError, filter);
|
| + return NULL;
|
| + }
|
| + result->AddPattern(pattern);
|
| + }
|
|
|
| - // We don't allow extension plugins to run on Chrome OS. We still
|
| - // parse the manifest entry so that error messages are consistently
|
| - // displayed across platforms.
|
| -#if !defined(OS_CHROMEOS)
|
| - plugins_.push_back(PluginInfo());
|
| - plugins_.back().path = path().Append(FilePath::FromUTF8Unsafe(path_str));
|
| - plugins_.back().is_public = is_public;
|
| -#endif
|
| + std::string default_icon;
|
| + // Read the file browser action |default_icon| (optional).
|
| + if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) {
|
| + if (!file_browser_handler->GetString(
|
| + keys::kPageActionDefaultIcon, &default_icon) ||
|
| + default_icon.empty()) {
|
| + *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath);
|
| + return NULL;
|
| }
|
| + result->set_icon_path(default_icon);
|
| }
|
|
|
| - if (manifest_->HasKey(keys::kNaClModules)) {
|
| - ListValue* list_value = NULL;
|
| - if (!manifest_->GetList(keys::kNaClModules, &list_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidNaClModules);
|
| + return result.release();
|
| +}
|
| +
|
| +bool Extension::LoadChromeURLOverrides(string16* error) {
|
| + if (!manifest_->HasKey(keys::kChromeURLOverrides))
|
| + return true;
|
| + DictionaryValue* overrides = NULL;
|
| + if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
|
| + return false;
|
| + }
|
| +
|
| + // Validate that the overrides are all strings
|
| + for (DictionaryValue::key_iterator iter = overrides->begin_keys();
|
| + iter != overrides->end_keys(); ++iter) {
|
| + std::string page = *iter;
|
| + std::string val;
|
| + // Restrict override pages to a list of supported URLs.
|
| + if ((page != chrome::kChromeUINewTabHost &&
|
| +#if defined(USE_VIRTUAL_KEYBOARD)
|
| + page != chrome::kChromeUIKeyboardHost &&
|
| +#endif
|
| +#if defined(OS_CHROMEOS)
|
| + page != chrome::kChromeUIActivationMessageHost &&
|
| +#endif
|
| + page != chrome::kChromeUIBookmarksHost &&
|
| + page != chrome::kChromeUIHistoryHost
|
| +#if defined(FILE_MANAGER_EXTENSION)
|
| + &&
|
| + !(location() == COMPONENT &&
|
| + page == chrome::kChromeUIFileManagerHost)
|
| +#endif
|
| + ) ||
|
| + !overrides->GetStringWithoutPathExpansion(*iter, &val)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
|
| return false;
|
| }
|
| + // Replace the entry with a fully qualified chrome-extension:// URL.
|
| + chrome_url_overrides_[page] = GetResourceURL(val);
|
| + }
|
|
|
| - for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| - DictionaryValue* module_value = NULL;
|
| - std::string path_str;
|
| - std::string mime_type;
|
| -
|
| - if (!list_value->GetDictionary(i, &module_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidNaClModules);
|
| - return false;
|
| - }
|
| + // An extension may override at most one page.
|
| + if (overrides->size() > 1) {
|
| + *error = ASCIIToUTF16(errors::kMultipleOverrides);
|
| + return false;
|
| + }
|
|
|
| - // Get nacl_modules[i].path.
|
| - if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidNaClModulesPath, base::IntToString(i));
|
| - return false;
|
| - }
|
| + return true;
|
| +}
|
|
|
| - // Get nacl_modules[i].mime_type.
|
| - if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidNaClModulesMIMEType, base::IntToString(i));
|
| - return false;
|
| - }
|
| +bool Extension::LoadOmnibox(string16* error) {
|
| + if (!manifest_->HasKey(keys::kOmnibox))
|
| + return true;
|
| + if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) ||
|
| + omnibox_keyword_.empty()) {
|
| + *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword);
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
|
|
| - nacl_modules_.push_back(NaClModuleInfo());
|
| - nacl_modules_.back().url = GetResourceURL(path_str);
|
| - nacl_modules_.back().mime_type = mime_type;
|
| - }
|
| +bool Extension::LoadTextToSpeechVoices(string16* error) {
|
| + if (!manifest_->HasKey(keys::kTtsEngine))
|
| + return true;
|
| + DictionaryValue* tts_dict = NULL;
|
| + if (!manifest_->GetDictionary(keys::kTtsEngine, &tts_dict)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTts);
|
| + return false;
|
| }
|
|
|
| - // Initialize content scripts (optional).
|
| - if (manifest_->HasKey(keys::kContentScripts)) {
|
| - ListValue* list_value;
|
| - if (!manifest_->GetList(keys::kContentScripts, &list_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidContentScriptsList);
|
| + if (tts_dict->HasKey(keys::kTtsVoices)) {
|
| + ListValue* tts_voices = NULL;
|
| + if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoices);
|
| return false;
|
| }
|
|
|
| - for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| - DictionaryValue* content_script = NULL;
|
| - if (!list_value->GetDictionary(i, &content_script)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidContentScript, base::IntToString(i));
|
| + for (size_t i = 0; i < tts_voices->GetSize(); i++) {
|
| + DictionaryValue* one_tts_voice = NULL;
|
| + if (!tts_voices->GetDictionary(i, &one_tts_voice)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoices);
|
| return false;
|
| }
|
|
|
| - UserScript script;
|
| - if (!LoadUserScriptHelper(content_script, i, flags, error, &script))
|
| - return false; // Failed to parse script context definition.
|
| - script.set_extension_id(id());
|
| - if (converted_from_user_script_) {
|
| - script.set_emulate_greasemonkey(true);
|
| - script.set_match_all_frames(true); // Greasemonkey matches all frames.
|
| + TtsVoice voice_data;
|
| + if (one_tts_voice->HasKey(keys::kTtsVoicesVoiceName)) {
|
| + if (!one_tts_voice->GetString(
|
| + keys::kTtsVoicesVoiceName, &voice_data.voice_name)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoicesVoiceName);
|
| + return false;
|
| + }
|
| }
|
| - content_scripts_.push_back(script);
|
| - }
|
| - }
|
| -
|
| - // Initialize web accessible resources (optional).
|
| - if (manifest_->HasKey(keys::kWebAccessibleResources)) {
|
| - ListValue* list_value;
|
| - if (!manifest_->GetList(keys::kWebAccessibleResources, &list_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList);
|
| - return false;
|
| - }
|
| - for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| - std::string relative_path;
|
| - if (!list_value->GetString(i, &relative_path)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidWebAccessibleResource, base::IntToString(i));
|
| - return false;
|
| + if (one_tts_voice->HasKey(keys::kTtsVoicesLang)) {
|
| + if (!one_tts_voice->GetString(
|
| + keys::kTtsVoicesLang, &voice_data.lang) ||
|
| + !l10n_util::IsValidLocaleSyntax(voice_data.lang)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoicesLang);
|
| + return false;
|
| + }
|
| + }
|
| + if (one_tts_voice->HasKey(keys::kTtsVoicesGender)) {
|
| + if (!one_tts_voice->GetString(
|
| + keys::kTtsVoicesGender, &voice_data.gender) ||
|
| + (voice_data.gender != keys::kTtsGenderMale &&
|
| + voice_data.gender != keys::kTtsGenderFemale)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoicesGender);
|
| + return false;
|
| + }
|
| + }
|
| + if (one_tts_voice->HasKey(keys::kTtsVoicesEventTypes)) {
|
| + ListValue* event_types_list;
|
| + if (!one_tts_voice->GetList(
|
| + keys::kTtsVoicesEventTypes, &event_types_list)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
|
| + return false;
|
| + }
|
| + for (size_t i = 0; i < event_types_list->GetSize(); i++) {
|
| + std::string event_type;
|
| + if (!event_types_list->GetString(i, &event_type)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
|
| + return false;
|
| + }
|
| + if (event_type != keys::kTtsVoicesEventTypeEnd &&
|
| + event_type != keys::kTtsVoicesEventTypeError &&
|
| + event_type != keys::kTtsVoicesEventTypeMarker &&
|
| + event_type != keys::kTtsVoicesEventTypeSentence &&
|
| + event_type != keys::kTtsVoicesEventTypeStart &&
|
| + event_type != keys::kTtsVoicesEventTypeWord) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
|
| + return false;
|
| + }
|
| + if (voice_data.event_types.find(event_type) !=
|
| + voice_data.event_types.end()) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
|
| + return false;
|
| + }
|
| + voice_data.event_types.insert(event_type);
|
| + }
|
| }
|
| - if (relative_path[0] != '/')
|
| - relative_path = '/' + relative_path;
|
| - web_accessible_resources_.insert(relative_path);
|
| +
|
| + tts_voices_.push_back(voice_data);
|
| }
|
| }
|
| + return true;
|
| +}
|
|
|
| - // Initialize page action (optional).
|
| - DictionaryValue* page_action_value = NULL;
|
| +bool Extension::LoadIncognitoMode(string16* error) {
|
| + // Apps default to split mode, extensions default to spanning.
|
| + incognito_split_mode_ = is_app();
|
| + if (!manifest_->HasKey(keys::kIncognito))
|
| + return true;
|
| + std::string value;
|
| + if (!manifest_->GetString(keys::kIncognito, &value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior);
|
| + return false;
|
| + }
|
| + if (value == values::kIncognitoSpanning) {
|
| + incognito_split_mode_ = false;
|
| + } else if (value == values::kIncognitoSplit) {
|
| + incognito_split_mode_ = true;
|
| + } else {
|
| + *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior);
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
|
|
| - if (manifest_->HasKey(keys::kPageActions)) {
|
| - ListValue* list_value = NULL;
|
| - if (!manifest_->GetList(keys::kPageActions, &list_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidPageActionsList);
|
| +bool Extension::LoadContentSecurityPolicy(string16* error) {
|
| + if (manifest_->HasKey(keys::kContentSecurityPolicy)) {
|
| + std::string content_security_policy;
|
| + if (!manifest_->GetString(keys::kContentSecurityPolicy,
|
| + &content_security_policy)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy);
|
| return false;
|
| }
|
| -
|
| - size_t list_value_length = list_value->GetSize();
|
| -
|
| - if (list_value_length == 0u) {
|
| - // A list with zero items is allowed, and is equivalent to not having
|
| - // a page_actions key in the manifest. Don't set |page_action_value|.
|
| - } else if (list_value_length == 1u) {
|
| - if (!list_value->GetDictionary(0, &page_action_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidPageAction);
|
| - return false;
|
| - }
|
| - } else { // list_value_length > 1u.
|
| - *error = ASCIIToUTF16(errors::kInvalidPageActionsListSize);
|
| + if (!ContentSecurityPolicyIsLegal(content_security_policy)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy);
|
| return false;
|
| }
|
| - } else if (manifest_->HasKey(keys::kPageAction)) {
|
| - if (!manifest_->GetDictionary(keys::kPageAction, &page_action_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidPageAction);
|
| + if (manifest_version_ >= 2 &&
|
| + !ContentSecurityPolicyIsSecure(content_security_policy)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy);
|
| return false;
|
| }
|
| - }
|
|
|
| - // If page_action_value is not NULL, then there was a valid page action.
|
| - if (page_action_value) {
|
| - page_action_.reset(
|
| - LoadExtensionActionHelper(page_action_value, error));
|
| - if (!page_action_.get())
|
| - return false; // Failed to parse page action definition.
|
| + content_security_policy_ = content_security_policy;
|
| + } else if (manifest_version_ >= 2) {
|
| + // Manifest version 2 introduced a default Content-Security-Policy.
|
| + // TODO(abarth): Should we continue to let extensions override the
|
| + // default Content-Security-Policy?
|
| + content_security_policy_ = kDefaultContentSecurityPolicy;
|
| + CHECK(ContentSecurityPolicyIsSecure(content_security_policy_));
|
| }
|
| + return true;
|
| +}
|
|
|
| - // Initialize browser action (optional).
|
| - if (manifest_->HasKey(keys::kBrowserAction)) {
|
| - DictionaryValue* browser_action_value = NULL;
|
| - if (!manifest_->GetDictionary(keys::kBrowserAction,
|
| - &browser_action_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidBrowserAction);
|
| - return false;
|
| - }
|
| +bool Extension::LoadAppIsolation(string16* error) {
|
| + Value* temp = NULL;
|
| + if (!manifest_->Get(keys::kIsolation, &temp))
|
| + return true;
|
|
|
| - browser_action_.reset(
|
| - LoadExtensionActionHelper(browser_action_value, error));
|
| - if (!browser_action_.get())
|
| - return false; // Failed to parse browser action definition.
|
| + if (temp->GetType() != Value::TYPE_LIST) {
|
| + *error = ASCIIToUTF16(errors::kInvalidIsolation);
|
| + return false;
|
| }
|
|
|
| - // Initialize file browser actions (optional).
|
| - if (manifest_->HasKey(keys::kFileBrowserHandlers)) {
|
| - ListValue* file_browser_handlers_value = NULL;
|
| - if (!manifest_->GetList(keys::kFileBrowserHandlers,
|
| - &file_browser_handlers_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
|
| + ListValue* isolation_list = static_cast<ListValue*>(temp);
|
| + for (size_t i = 0; i < isolation_list->GetSize(); ++i) {
|
| + std::string isolation_string;
|
| + if (!isolation_list->GetString(i, &isolation_string)) {
|
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| + errors::kInvalidIsolationValue,
|
| + base::UintToString(i));
|
| return false;
|
| }
|
|
|
| - file_browser_handlers_.reset(
|
| - LoadFileBrowserHandlers(file_browser_handlers_value, error));
|
| - if (!file_browser_handlers_.get())
|
| - return false; // Failed to parse file browser actions definition.
|
| + // Check for isolated storage.
|
| + if (isolation_string == values::kIsolatedStorage) {
|
| + is_storage_isolated_ = true;
|
| + } else {
|
| + DLOG(WARNING) << "Did not recognize isolation type: "
|
| + << isolation_string;
|
| + }
|
| }
|
| + return true;
|
| +}
|
|
|
| - // App isolation.
|
| - if (api_permissions.count(ExtensionAPIPermission::kExperimental)) {
|
| - if (is_app() && !LoadAppIsolation(error))
|
| - return false;
|
| +bool Extension::LoadThemeFeatures(string16* error) {
|
| + if (!manifest_->HasKey(keys::kTheme))
|
| + return true;
|
| + DictionaryValue* theme_value = NULL;
|
| + if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidTheme);
|
| + return false;
|
| }
|
| + if (!LoadThemeImages(theme_value, error))
|
| + return false;
|
| + if (!LoadThemeColors(theme_value, error))
|
| + return false;
|
| + if (!LoadThemeTints(theme_value, error))
|
| + return false;
|
| + if (!LoadThemeDisplayProperties(theme_value, error))
|
| + return false;
|
|
|
| - // Initialize options page url (optional).
|
| - if (manifest_->HasKey(keys::kOptionsPage)) {
|
| - std::string options_str;
|
| - if (!manifest_->GetString(keys::kOptionsPage, &options_str)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidOptionsPage);
|
| - return false;
|
| - }
|
| + return true;
|
| +}
|
|
|
| - if (is_hosted_app()) {
|
| - // hosted apps require an absolute URL.
|
| - GURL options_url(options_str);
|
| - if (!options_url.is_valid() ||
|
| - !(options_url.SchemeIs("http") || options_url.SchemeIs("https"))) {
|
| - *error = ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp);
|
| +bool Extension::LoadThemeImages(const DictionaryValue* theme_value,
|
| + string16* error) {
|
| + DictionaryValue* images_value = NULL;
|
| + if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) {
|
| + // Validate that the images are all strings
|
| + for (DictionaryValue::key_iterator iter = images_value->begin_keys();
|
| + iter != images_value->end_keys(); ++iter) {
|
| + std::string val;
|
| + if (!images_value->GetString(*iter, &val)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidThemeImages);
|
| return false;
|
| }
|
| - options_url_ = options_url;
|
| - } else {
|
| - GURL absolute(options_str);
|
| - if (absolute.is_valid()) {
|
| - *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage);
|
| + }
|
| + theme_images_.reset(images_value->DeepCopy());
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadThemeColors(const DictionaryValue* theme_value,
|
| + string16* error) {
|
| + DictionaryValue* colors_value = NULL;
|
| + if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) {
|
| + // Validate that the colors are RGB or RGBA lists
|
| + for (DictionaryValue::key_iterator iter = colors_value->begin_keys();
|
| + iter != colors_value->end_keys(); ++iter) {
|
| + ListValue* color_list = NULL;
|
| + double alpha = 0.0;
|
| + int color = 0;
|
| + // The color must be a list
|
| + if (!colors_value->GetListWithoutPathExpansion(*iter, &color_list) ||
|
| + // And either 3 items (RGB) or 4 (RGBA)
|
| + ((color_list->GetSize() != 3) &&
|
| + ((color_list->GetSize() != 4) ||
|
| + // For RGBA, the fourth item must be a real or int alpha value.
|
| + // Note that GetDouble() can get an integer value.
|
| + !color_list->GetDouble(3, &alpha))) ||
|
| + // For both RGB and RGBA, the first three items must be ints (R,G,B)
|
| + !color_list->GetInteger(0, &color) ||
|
| + !color_list->GetInteger(1, &color) ||
|
| + !color_list->GetInteger(2, &color)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidThemeColors);
|
| return false;
|
| }
|
| - options_url_ = GetResourceURL(options_str);
|
| - if (!options_url_.is_valid()) {
|
| - *error = ASCIIToUTF16(errors::kInvalidOptionsPage);
|
| + }
|
| + theme_colors_.reset(colors_value->DeepCopy());
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool Extension::LoadThemeTints(const DictionaryValue* theme_value,
|
| + string16* error) {
|
| + DictionaryValue* tints_value = NULL;
|
| + if (theme_value->GetDictionary(keys::kThemeTints, &tints_value)) {
|
| + // Validate that the tints are all reals.
|
| + for (DictionaryValue::key_iterator iter = tints_value->begin_keys();
|
| + iter != tints_value->end_keys(); ++iter) {
|
| + ListValue* tint_list = NULL;
|
| + double v = 0.0;
|
| + if (!tints_value->GetListWithoutPathExpansion(*iter, &tint_list) ||
|
| + tint_list->GetSize() != 3 ||
|
| + !tint_list->GetDouble(0, &v) ||
|
| + !tint_list->GetDouble(1, &v) ||
|
| + !tint_list->GetDouble(2, &v)) {
|
| + *error = ASCIIToUTF16(errors::kInvalidThemeTints);
|
| return false;
|
| }
|
| }
|
| + theme_tints_.reset(tints_value->DeepCopy());
|
| }
|
| + return true;
|
| +}
|
|
|
| - if (!LoadBackgroundScripts(error))
|
| - return false;
|
| +bool Extension::LoadThemeDisplayProperties(const DictionaryValue* theme_value,
|
| + string16* error) {
|
| + DictionaryValue* display_properties_value = NULL;
|
| + if (theme_value->GetDictionary(keys::kThemeDisplayProperties,
|
| + &display_properties_value)) {
|
| + theme_display_properties_.reset(
|
| + display_properties_value->DeepCopy());
|
| + }
|
| + return true;
|
| +}
|
|
|
| - if (!LoadBackgroundPage(api_permissions, error))
|
| - return false;
|
| +// static
|
| +bool Extension::IsTrustedId(const std::string& id) {
|
| + // See http://b/4946060 for more details.
|
| + return id == std::string("nckgahadagoaajjgafhacjanaoiihapd");
|
| +}
|
|
|
| - if (!LoadBackgroundPersistent(api_permissions, error))
|
| - return false;
|
| +Extension::Extension(const FilePath& path,
|
| + scoped_ptr<extensions::Manifest> manifest)
|
| + : manifest_version_(0),
|
| + incognito_split_mode_(false),
|
| + offline_enabled_(false),
|
| + converted_from_user_script_(false),
|
| + background_page_persists_(true),
|
| + allow_background_js_access_(true),
|
| + manifest_(manifest.release()),
|
| + is_storage_isolated_(false),
|
| + launch_container_(extension_misc::LAUNCH_TAB),
|
| + launch_width_(0),
|
| + launch_height_(0),
|
| + launch_min_width_(0),
|
| + launch_min_height_(0),
|
| + launch_max_width_(0),
|
| + launch_max_height_(0),
|
| + wants_file_access_(false),
|
| + creation_flags_(0) {
|
| + DCHECK(path.empty() || path.IsAbsolute());
|
| + path_ = MaybeNormalizePath(path);
|
| +}
|
| +
|
| +Extension::~Extension() {
|
| + if (manifest_)
|
| + delete manifest_;
|
| +}
|
| +
|
| +ExtensionResource Extension::GetResource(
|
| + const std::string& relative_path) const {
|
| +#if defined(OS_POSIX)
|
| + FilePath relative_file_path(relative_path);
|
| +#elif defined(OS_WIN)
|
| + FilePath relative_file_path(UTF8ToWide(relative_path));
|
| +#endif
|
| + return ExtensionResource(id(), path(), relative_file_path);
|
| +}
|
| +
|
| +ExtensionResource Extension::GetResource(
|
| + const FilePath& relative_file_path) const {
|
| + return ExtensionResource(id(), path(), relative_file_path);
|
| +}
|
|
|
| - if (!LoadBackgroundAllowJsAccess(api_permissions, error))
|
| +// TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a
|
| +// util class in base:
|
| +// http://code.google.com/p/chromium/issues/detail?id=13572
|
| +bool Extension::ParsePEMKeyBytes(const std::string& input,
|
| + std::string* output) {
|
| + DCHECK(output);
|
| + if (!output)
|
| + return false;
|
| + if (input.length() == 0)
|
| return false;
|
|
|
| - if (manifest_->HasKey(keys::kDefaultLocale)) {
|
| - if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) ||
|
| - !l10n_util::IsValidLocaleSyntax(default_locale_)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidDefaultLocale);
|
| + std::string working = input;
|
| + if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) {
|
| + working = CollapseWhitespaceASCII(working, true);
|
| + size_t header_pos = working.find(kKeyInfoEndMarker,
|
| + sizeof(kKeyBeginHeaderMarker) - 1);
|
| + if (header_pos == std::string::npos)
|
| return false;
|
| - }
|
| - }
|
| -
|
| - // Chrome URL overrides (optional)
|
| - if (manifest_->HasKey(keys::kChromeURLOverrides)) {
|
| - DictionaryValue* overrides = NULL;
|
| - if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
|
| + size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1;
|
| + size_t end_pos = working.rfind(kKeyBeginFooterMarker);
|
| + if (end_pos == std::string::npos)
|
| + return false;
|
| + if (start_pos >= end_pos)
|
| return false;
|
| - }
|
| -
|
| - // Validate that the overrides are all strings
|
| - for (DictionaryValue::key_iterator iter = overrides->begin_keys();
|
| - iter != overrides->end_keys(); ++iter) {
|
| - std::string page = *iter;
|
| - std::string val;
|
| - // Restrict override pages to a list of supported URLs.
|
| - if ((page != chrome::kChromeUINewTabHost &&
|
| -#if defined(USE_VIRTUAL_KEYBOARD)
|
| - page != chrome::kChromeUIKeyboardHost &&
|
| -#endif
|
| -#if defined(OS_CHROMEOS)
|
| - page != chrome::kChromeUIActivationMessageHost &&
|
| -#endif
|
| - page != chrome::kChromeUIBookmarksHost &&
|
| - page != chrome::kChromeUIHistoryHost
|
| -#if defined(FILE_MANAGER_EXTENSION)
|
| - &&
|
| - !(location() == COMPONENT &&
|
| - page == chrome::kChromeUIFileManagerHost)
|
| -#endif
|
| - ) ||
|
| - !overrides->GetStringWithoutPathExpansion(*iter, &val)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
|
| - return false;
|
| - }
|
| - // Replace the entry with a fully qualified chrome-extension:// URL.
|
| - chrome_url_overrides_[page] = GetResourceURL(val);
|
| - }
|
|
|
| - // An extension may override at most one page.
|
| - if (overrides->size() > 1) {
|
| - *error = ASCIIToUTF16(errors::kMultipleOverrides);
|
| + working = working.substr(start_pos, end_pos - start_pos);
|
| + if (working.length() == 0)
|
| return false;
|
| - }
|
| }
|
|
|
| - if (manifest_->HasKey(keys::kInputComponents)) {
|
| - ListValue* list_value = NULL;
|
| - if (!manifest_->GetList(keys::kInputComponents, &list_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidInputComponents);
|
| - return false;
|
| - }
|
| + return base::Base64Decode(working, output);
|
| +}
|
|
|
| - for (size_t i = 0; i < list_value->GetSize(); ++i) {
|
| - DictionaryValue* module_value = NULL;
|
| - std::string name_str;
|
| - InputComponentType type;
|
| - std::string id_str;
|
| - std::string description_str;
|
| - std::string language_str;
|
| - std::set<std::string> layouts;
|
| - std::string shortcut_keycode_str;
|
| - bool shortcut_alt = false;
|
| - bool shortcut_ctrl = false;
|
| - bool shortcut_shift = false;
|
| +bool Extension::ProducePEM(const std::string& input, std::string* output) {
|
| + DCHECK(output);
|
| + if (input.length() == 0)
|
| + return false;
|
|
|
| - if (!list_value->GetDictionary(i, &module_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidInputComponents);
|
| - return false;
|
| - }
|
| + return base::Base64Encode(input, output);
|
| +}
|
|
|
| - // Get input_components[i].name.
|
| - if (!module_value->GetString(keys::kName, &name_str)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidInputComponentName, base::IntToString(i));
|
| - return false;
|
| - }
|
| +bool Extension::FormatPEMForFileOutput(const std::string& input,
|
| + std::string* output,
|
| + bool is_public) {
|
| + DCHECK(output);
|
| + if (input.length() == 0)
|
| + return false;
|
| + *output = "";
|
| + output->append(kKeyBeginHeaderMarker);
|
| + output->append(" ");
|
| + output->append(is_public ? kPublic : kPrivate);
|
| + output->append(" ");
|
| + output->append(kKeyInfoEndMarker);
|
| + output->append("\n");
|
| + for (size_t i = 0; i < input.length(); ) {
|
| + int slice = std::min<int>(input.length() - i, kPEMOutputColumns);
|
| + output->append(input.substr(i, slice));
|
| + output->append("\n");
|
| + i += slice;
|
| + }
|
| + output->append(kKeyBeginFooterMarker);
|
| + output->append(" ");
|
| + output->append(is_public ? kPublic : kPrivate);
|
| + output->append(" ");
|
| + output->append(kKeyInfoEndMarker);
|
| + output->append("\n");
|
|
|
| - // Get input_components[i].type.
|
| - std::string type_str;
|
| - if (module_value->GetString(keys::kType, &type_str)) {
|
| - if (type_str == "ime") {
|
| - type = INPUT_COMPONENT_TYPE_IME;
|
| - } else if (type_str == "virtual_keyboard") {
|
| - if (!api_permissions.count(ExtensionAPIPermission::kExperimental)) {
|
| - // Virtual Keyboards require the experimental flag.
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidInputComponentType, base::IntToString(i));
|
| - return false;
|
| - }
|
| - type = INPUT_COMPONENT_TYPE_VIRTUAL_KEYBOARD;
|
| - } else {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidInputComponentType, base::IntToString(i));
|
| - return false;
|
| - }
|
| - } else {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidInputComponentType, base::IntToString(i));
|
| - return false;
|
| - }
|
| + return true;
|
| +}
|
|
|
| - // Get input_components[i].id.
|
| - if (!module_value->GetString(keys::kId, &id_str)) {
|
| - id_str = "";
|
| - }
|
| +// static
|
| +void Extension::DecodeIcon(const Extension* extension,
|
| + ExtensionIconSet::Icons preferred_icon_size,
|
| + ExtensionIconSet::MatchType match_type,
|
| + scoped_ptr<SkBitmap>* result) {
|
| + std::string path = extension->icons().Get(preferred_icon_size, match_type);
|
| + ExtensionIconSet::Icons size = extension->icons().GetIconSizeFromPath(path);
|
| + ExtensionResource icon_resource = extension->GetResource(path);
|
| + DecodeIconFromPath(icon_resource.GetFilePath(), size, result);
|
| +}
|
|
|
| - // Get input_components[i].description.
|
| - if (!module_value->GetString(keys::kDescription, &description_str)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidInputComponentDescription, base::IntToString(i));
|
| - return false;
|
| - }
|
| +// static
|
| +void Extension::DecodeIcon(const Extension* extension,
|
| + ExtensionIconSet::Icons icon_size,
|
| + scoped_ptr<SkBitmap>* result) {
|
| + DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result);
|
| +}
|
|
|
| - // Get input_components[i].language.
|
| - if (!module_value->GetString(keys::kLanguage, &language_str)) {
|
| - language_str = "";
|
| - }
|
| +// static
|
| +void Extension::DecodeIconFromPath(const FilePath& icon_path,
|
| + ExtensionIconSet::Icons icon_size,
|
| + scoped_ptr<SkBitmap>* result) {
|
| + if (icon_path.empty())
|
| + return;
|
|
|
| - // Get input_components[i].layouts.
|
| - ListValue* layouts_value = NULL;
|
| - if (!module_value->GetList(keys::kLayouts, &layouts_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidInputComponentLayouts);
|
| - return false;
|
| - }
|
| + std::string file_contents;
|
| + if (!file_util::ReadFileToString(icon_path, &file_contents)) {
|
| + DLOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName();
|
| + return;
|
| + }
|
|
|
| - for (size_t j = 0; j < layouts_value->GetSize(); ++j) {
|
| - std::string layout_name_str;
|
| - if (!layouts_value->GetString(j, &layout_name_str)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidInputComponentLayoutName, base::IntToString(i),
|
| - base::IntToString(j));
|
| - return false;
|
| - }
|
| - layouts.insert(layout_name_str);
|
| - }
|
| + // Decode the image using WebKit's image decoder.
|
| + const unsigned char* data =
|
| + reinterpret_cast<const unsigned char*>(file_contents.data());
|
| + webkit_glue::ImageDecoder decoder;
|
| + scoped_ptr<SkBitmap> decoded(new SkBitmap());
|
| + *decoded = decoder.Decode(data, file_contents.length());
|
| + if (decoded->empty()) {
|
| + DLOG(ERROR) << "Could not decode icon file: "
|
| + << icon_path.LossyDisplayName();
|
| + return;
|
| + }
|
|
|
| - if (module_value->HasKey(keys::kShortcutKey)) {
|
| - DictionaryValue* shortcut_value = NULL;
|
| - if (!module_value->GetDictionary(keys::kShortcutKey, &shortcut_value)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidInputComponentShortcutKey, base::IntToString(i));
|
| - return false;
|
| - }
|
| + if (decoded->width() != icon_size || decoded->height() != icon_size) {
|
| + DLOG(ERROR) << "Icon file has unexpected size: "
|
| + << base::IntToString(decoded->width()) << "x"
|
| + << base::IntToString(decoded->height());
|
| + return;
|
| + }
|
|
|
| - // Get input_components[i].shortcut_keycode.
|
| - if (!shortcut_value->GetString(keys::kKeycode, &shortcut_keycode_str)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidInputComponentShortcutKeycode,
|
| - base::IntToString(i));
|
| - return false;
|
| - }
|
| + result->swap(decoded);
|
| +}
|
|
|
| - // Get input_components[i].shortcut_alt.
|
| - if (!shortcut_value->GetBoolean(keys::kAltKey, &shortcut_alt)) {
|
| - shortcut_alt = false;
|
| - }
|
| +// static
|
| +const SkBitmap& Extension::GetDefaultIcon(bool is_app) {
|
| + if (is_app) {
|
| + return *ResourceBundle::GetSharedInstance().GetBitmapNamed(
|
| + IDR_APP_DEFAULT_ICON);
|
| + } else {
|
| + return *ResourceBundle::GetSharedInstance().GetBitmapNamed(
|
| + IDR_EXTENSION_DEFAULT_ICON);
|
| + }
|
| +}
|
|
|
| - // Get input_components[i].shortcut_ctrl.
|
| - if (!shortcut_value->GetBoolean(keys::kCtrlKey, &shortcut_ctrl)) {
|
| - shortcut_ctrl = false;
|
| - }
|
| +// static
|
| +GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
|
| + return GURL(std::string(chrome::kExtensionScheme) +
|
| + chrome::kStandardSchemeSeparator + extension_id + "/");
|
| +}
|
|
|
| - // Get input_components[i].shortcut_shift.
|
| - if (!shortcut_value->GetBoolean(keys::kShiftKey, &shortcut_shift)) {
|
| - shortcut_shift = false;
|
| - }
|
| - }
|
| +bool Extension::InitFromValue(int flags, string16* error) {
|
| + DCHECK(error);
|
|
|
| - input_components_.push_back(InputComponentInfo());
|
| - input_components_.back().name = name_str;
|
| - input_components_.back().type = type;
|
| - input_components_.back().id = id_str;
|
| - input_components_.back().description = description_str;
|
| - input_components_.back().language = language_str;
|
| - input_components_.back().layouts.insert(layouts.begin(), layouts.end());
|
| - input_components_.back().shortcut_keycode = shortcut_keycode_str;
|
| - input_components_.back().shortcut_alt = shortcut_alt;
|
| - input_components_.back().shortcut_ctrl = shortcut_ctrl;
|
| - input_components_.back().shortcut_shift = shortcut_shift;
|
| - }
|
| - }
|
| + base::AutoLock auto_lock(runtime_data_lock_);
|
|
|
| - if (manifest_->HasKey(keys::kOmnibox)) {
|
| - if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) ||
|
| - omnibox_keyword_.empty()) {
|
| - *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword);
|
| - return false;
|
| - }
|
| - }
|
| + // Initialize permissions with an empty, default permission set.
|
| + runtime_data_.SetActivePermissions(new ExtensionPermissionSet());
|
| + optional_permission_set_ = new ExtensionPermissionSet();
|
| + required_permission_set_ = new ExtensionPermissionSet();
|
|
|
| - if (manifest_->HasKey(keys::kContentSecurityPolicy)) {
|
| - std::string content_security_policy;
|
| - if (!manifest_->GetString(keys::kContentSecurityPolicy,
|
| - &content_security_policy)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy);
|
| - return false;
|
| - }
|
| - if (!ContentSecurityPolicyIsLegal(content_security_policy)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy);
|
| - return false;
|
| - }
|
| - if (manifest_version_ >= 2 &&
|
| - !ContentSecurityPolicyIsSecure(content_security_policy)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy);
|
| - return false;
|
| - }
|
| + creation_flags_ = flags;
|
|
|
| - content_security_policy_ = content_security_policy;
|
| - } else if (manifest_version_ >= 2) {
|
| - // Manifest version 2 introduced a default Content-Security-Policy.
|
| - // TODO(abarth): Should we continue to let extensions override the
|
| - // default Content-Security-Policy?
|
| - content_security_policy_ = kDefaultContentSecurityPolicy;
|
| - CHECK(ContentSecurityPolicyIsSecure(content_security_policy_));
|
| - }
|
| + // Validate minimum Chrome version. We don't need to store this, since the
|
| + // extension is not valid if it is incorrect
|
| + if (!CheckMinimumChromeVersion(error))
|
| + return false;
|
|
|
| - // Initialize devtools page url (optional).
|
| - if (manifest_->HasKey(keys::kDevToolsPage)) {
|
| - std::string devtools_str;
|
| - if (!manifest_->GetString(keys::kDevToolsPage, &devtools_str)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidDevToolsPage);
|
| - return false;
|
| - }
|
| - devtools_url_ = GetResourceURL(devtools_str);
|
| - }
|
| + if (!LoadRequiredFeatures(error))
|
| + return false;
|
|
|
| - // Initialize text-to-speech voices (optional).
|
| - if (manifest_->HasKey(keys::kTtsEngine)) {
|
| - DictionaryValue* tts_dict = NULL;
|
| - if (!manifest_->GetDictionary(keys::kTtsEngine, &tts_dict)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTts);
|
| - return false;
|
| - }
|
| + // We don't ned to validate because InitExtensionID already did that.
|
| + manifest_->GetString(keys::kPublicKey, &public_key_);
|
|
|
| - if (tts_dict->HasKey(keys::kTtsVoices)) {
|
| - ListValue* tts_voices = NULL;
|
| - if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoices);
|
| - return false;
|
| - }
|
| + // Initialize permissions with an empty, default permission set.
|
| + runtime_data_.SetActivePermissions(new ExtensionPermissionSet());
|
| + optional_permission_set_ = new ExtensionPermissionSet();
|
| + required_permission_set_ = new ExtensionPermissionSet();
|
|
|
| - for (size_t i = 0; i < tts_voices->GetSize(); i++) {
|
| - DictionaryValue* one_tts_voice = NULL;
|
| - if (!tts_voices->GetDictionary(i, &one_tts_voice)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoices);
|
| - return false;
|
| - }
|
| + extension_url_ = Extension::GetBaseURLFromExtensionId(id());
|
|
|
| - TtsVoice voice_data;
|
| - if (one_tts_voice->HasKey(keys::kTtsVoicesVoiceName)) {
|
| - if (!one_tts_voice->GetString(
|
| - keys::kTtsVoicesVoiceName, &voice_data.voice_name)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoicesVoiceName);
|
| - return false;
|
| - }
|
| - }
|
| - if (one_tts_voice->HasKey(keys::kTtsVoicesLang)) {
|
| - if (!one_tts_voice->GetString(
|
| - keys::kTtsVoicesLang, &voice_data.lang) ||
|
| - !l10n_util::IsValidLocaleSyntax(voice_data.lang)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoicesLang);
|
| - return false;
|
| - }
|
| - }
|
| - if (one_tts_voice->HasKey(keys::kTtsVoicesGender)) {
|
| - if (!one_tts_voice->GetString(
|
| - keys::kTtsVoicesGender, &voice_data.gender) ||
|
| - (voice_data.gender != keys::kTtsGenderMale &&
|
| - voice_data.gender != keys::kTtsGenderFemale)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoicesGender);
|
| - return false;
|
| - }
|
| - }
|
| - if (one_tts_voice->HasKey(keys::kTtsVoicesEventTypes)) {
|
| - ListValue* event_types_list;
|
| - if (!one_tts_voice->GetList(
|
| - keys::kTtsVoicesEventTypes, &event_types_list)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
|
| - return false;
|
| - }
|
| - for (size_t i = 0; i < event_types_list->GetSize(); i++) {
|
| - std::string event_type;
|
| - if (!event_types_list->GetString(i, &event_type)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
|
| - return false;
|
| - }
|
| - if (event_type != keys::kTtsVoicesEventTypeEnd &&
|
| - event_type != keys::kTtsVoicesEventTypeError &&
|
| - event_type != keys::kTtsVoicesEventTypeMarker &&
|
| - event_type != keys::kTtsVoicesEventTypeSentence &&
|
| - event_type != keys::kTtsVoicesEventTypeStart &&
|
| - event_type != keys::kTtsVoicesEventTypeWord) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
|
| - return false;
|
| - }
|
| - if (voice_data.event_types.find(event_type) !=
|
| - voice_data.event_types.end()) {
|
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
|
| - return false;
|
| - }
|
| - voice_data.event_types.insert(event_type);
|
| - }
|
| - }
|
| + // 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 (is_app() && !LoadAppFeatures(error))
|
| + return false;
|
|
|
| - tts_voices_.push_back(voice_data);
|
| - }
|
| - }
|
| + ExtensionAPIPermissionSet api_permissions;
|
| + URLPatternSet host_permissions;
|
| + if (!ParsePermissions(keys::kPermissions,
|
| + error,
|
| + &api_permissions,
|
| + &host_permissions)) {
|
| + return false;
|
| }
|
|
|
| - // Initialize web intents (optional).
|
| - if (!LoadWebIntentServices(error))
|
| + ExtensionAPIPermissionSet optional_api_permissions;
|
| + URLPatternSet optional_host_permissions;
|
| + if (!ParsePermissions(keys::kOptionalPermissions,
|
| + error,
|
| + &optional_api_permissions,
|
| + &optional_host_permissions)) {
|
| return false;
|
| -
|
| - // Initialize incognito behavior. Apps default to split mode, extensions
|
| - // default to spanning.
|
| - incognito_split_mode_ = is_app();
|
| - if (manifest_->HasKey(keys::kIncognito)) {
|
| - std::string value;
|
| - if (!manifest_->GetString(keys::kIncognito, &value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior);
|
| - return false;
|
| - }
|
| - if (value == values::kIncognitoSpanning) {
|
| - incognito_split_mode_ = false;
|
| - } else if (value == values::kIncognitoSplit) {
|
| - incognito_split_mode_ = true;
|
| - } else {
|
| - *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior);
|
| - return false;
|
| - }
|
| }
|
|
|
| - // Initialize offline-enabled status. Defaults to false.
|
| - if (manifest_->HasKey(keys::kOfflineEnabled)) {
|
| - if (!manifest_->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidOfflineEnabled);
|
| - return false;
|
| - }
|
| - }
|
| + // App isolation.
|
| + if (api_permissions.count(ExtensionAPIPermission::kExperimental) &&
|
| + is_app() && !LoadAppIsolation(error))
|
| + return false;
|
|
|
| - // Initialize requirements (optional). Not actually persisted (they're only
|
| - // used by the store), but still validated.
|
| - if (manifest_->HasKey(keys::kRequirements)) {
|
| - DictionaryValue* requirements_value = NULL;
|
| - if (!manifest_->GetDictionary(keys::kRequirements, &requirements_value)) {
|
| - *error = ASCIIToUTF16(errors::kInvalidRequirements);
|
| - return false;
|
| - }
|
| + if (!LoadSharedFeatures(api_permissions, error))
|
| + return false;
|
|
|
| - for (DictionaryValue::key_iterator it = requirements_value->begin_keys();
|
| - it != requirements_value->end_keys(); ++it) {
|
| - DictionaryValue* requirement_value;
|
| - if (!requirements_value->GetDictionaryWithoutPathExpansion(
|
| - *it, &requirement_value)) {
|
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
|
| - errors::kInvalidRequirement, *it);
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| +
|
| + if (!LoadExtensionFeatures(api_permissions, error))
|
| + return false;
|
| +
|
| + if (!LoadThemeFeatures(error))
|
| + return false;
|
|
|
| if (HasMultipleUISurfaces()) {
|
| *error = ASCIIToUTF16(errors::kOneUISurfaceOnly);
|
| @@ -2928,7 +3065,6 @@ GURL Extension::GetIconURL(int size,
|
| }
|
|
|
| bool Extension::ParsePermissions(const char* key,
|
| - int flags,
|
| string16* error,
|
| ExtensionAPIPermissionSet* api_permissions,
|
| URLPatternSet* host_permissions) {
|
| @@ -2979,7 +3115,7 @@ bool Extension::ParsePermissions(const char* key,
|
| if (pattern.MatchesScheme(chrome::kFileScheme) &&
|
| !CanExecuteScriptEverywhere()) {
|
| wants_file_access_ = true;
|
| - if (!(flags & ALLOW_FILE_ACCESS))
|
| + if (!(creation_flags_ & ALLOW_FILE_ACCESS))
|
| pattern.SetValidSchemes(
|
| pattern.valid_schemes() & ~URLPattern::SCHEME_FILE);
|
| }
|
| @@ -3168,7 +3304,6 @@ bool Extension::CanSpecifyAPIPermission(
|
| string16* error) const {
|
| if (location() == Extension::COMPONENT)
|
| return true;
|
| -
|
| bool access_denied = false;
|
| if (permission->HasWhitelist()) {
|
| if (permission->IsWhitelisted(id()))
|
| @@ -3346,7 +3481,7 @@ Extension::SyncType Extension::GetSyncType() const {
|
| bool Extension::IsSyncable() const {
|
| // TODO(akalin): Figure out if we need to allow some other types.
|
|
|
| - // We want to sync any extensions that are shown in the luancher because
|
| + // We want to sync any extensions that are shown in the launcher because
|
| // their positions should sync.
|
| return location() == Extension::INTERNAL ||
|
| ShouldDisplayInLauncher();
|
|
|