Chromium Code Reviews| Index: chrome/common/extensions/extension.cc |
| diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc |
| index 04eb7dff7d9a63295feafc8e0c9fbf0fd9ba60f0..859d3ee4ed384433c1498937474e08860f8fdbd7 100644 |
| --- a/chrome/common/extensions/extension.cc |
| +++ b/chrome/common/extensions/extension.cc |
| @@ -333,7 +333,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 +479,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 { |
| @@ -892,909 +891,1267 @@ 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; |
| - } |
| - 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(); |
| -} |
| +bool Extension::LoadSharedFeatures(const int& flags, |
| + string16* error) { |
| + if (!LoadName(error)) |
| + return false; |
| -FileBrowserHandler* Extension::LoadFileBrowserHandler( |
| - const DictionaryValue* file_browser_handler, string16* error) { |
| - scoped_ptr<FileBrowserHandler> result( |
| - new FileBrowserHandler()); |
| - result->set_extension_id(id()); |
| + if (manifest_->HasKey(keys::kDescription) && !LoadDescription(error)) |
|
Aaron Boodman
2012/02/29 20:38:25
If you put the check for the presence of the key i
Devlin
2012/03/02 17:31:04
Done.
|
| + 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; |
| + if (!LoadVersion(error)) |
| + return false; |
| + |
| + // Validate minimum Chrome version. We don't need to store this, since the |
| + // extension is not valid if it is incorrect |
| + if (manifest_->HasKey(keys::kMinimumChromeVersion) && |
| + !LoadMinimumChromeVersion(error)) |
|
Yoyo Zhou
2012/03/01 01:17:25
Maybe call this CheckMinimumChromeVersion instead,
Devlin
2012/03/02 17:31:04
Done.
|
| + return false; |
| + |
| + extension_url_ = Extension::GetBaseURLFromExtensionId(id()); |
| + |
| + if (manifest_->HasKey(keys::kHomepageURL) && !LoadHomepageURL(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kUpdateURL) && !LoadUpdateURL(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; |
| } |
| - result->set_id(id); |
| + base::i18n::AdjustStringForLocaleDirection(&localized_name); |
| + name_ = UTF16ToUTF8(localized_name); |
| + return true; |
| +} |
| - // 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; |
| +bool Extension::LoadDescription(string16* error) { |
| + if (!manifest_->GetString(keys::kDescription, |
| + &description_)) { |
| + *error = ASCIIToUTF16(errors::kInvalidDescription); |
| + return false; |
| } |
| - result->set_title(title); |
| + return true; |
| +} |
| - // 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; |
| +bool Extension::LoadVersion(string16* error) { |
| + std::string version_str; |
| + if (!manifest_->GetString(keys::kVersion, &version_str)) { |
| + *error = ASCIIToUTF16(errors::kInvalidVersion); |
| + 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); |
| + version_.reset(Version::GetVersionFromString(version_str)); |
| + if (!version_.get() || |
| + version_->components().size() > 4) { |
| + *error = ASCIIToUTF16(errors::kInvalidVersion); |
| + 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::LoadMinimumChromeVersion(string16* error) { |
| + std::string minimum_version_string; |
| + if (!manifest_->GetString(keys::kMinimumChromeVersion, |
| + &minimum_version_string)) { |
|
Aaron Boodman
2012/02/29 20:38:25
+1 indent
Devlin
2012/03/02 17:31:04
Done.
|
| + *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); |
| + return false; |
| } |
| - return result.release(); |
| -} |
| + scoped_ptr<Version> minimum_version( |
| + Version::GetVersionFromString(minimum_version_string)); |
| + if (!minimum_version.get()) { |
| + *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); |
| + return false; |
| + } |
| -bool Extension::LoadExtent(const char* key, |
| - URLPatternSet* extent, |
| - const char* list_error, |
| - const char* value_error, |
| - string16* error) { |
| - Value* temp = NULL; |
| - if (!manifest_->Get(key, &temp)) |
| - return true; |
| + chrome::VersionInfo current_version_info; |
| + if (!current_version_info.is_valid()) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| - if (temp->GetType() != Value::TYPE_LIST) { |
| - *error = ASCIIToUTF16(list_error); |
| + scoped_ptr<Version> current_version( |
| + Version::GetVersionFromString(current_version_info.Version())); |
| + if (!current_version.get()) { |
| + DCHECK(false); |
| return false; |
| } |
| - ListValue* pattern_list = static_cast<ListValue*>(temp); |
| - for (size_t i = 0; i < pattern_list->GetSize(); ++i) { |
| - std::string pattern_string; |
| - if (!pattern_list->GetString(i, &pattern_string)) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16(value_error, |
| - base::UintToString(i), |
| - errors::kExpectString); |
| + 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; |
| +} |
| + |
| +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; |
| } |
| + } |
| - URLPattern pattern(kValidWebExtentSchemes); |
| - URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); |
| - if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { |
| - pattern_string += "/"; |
| - parse_result = pattern.Parse(pattern_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; |
| + } |
| - if (parse_result != URLPattern::PARSE_SUCCESS) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - value_error, |
| - base::UintToString(i), |
| - URLPattern::GetParseResultString(parse_result)); |
| - return false; |
| - } |
| + return true; |
| +} |
| - // Do not allow authors to claim "<all_urls>". |
| - if (pattern.match_all_urls()) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - value_error, |
| - base::UintToString(i), |
| - errors::kCannotClaimAllURLsInExtent); |
| - return false; |
| - } |
| +// 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; |
| + } |
| - // Do not allow authors to claim "*" for host. |
| - if (pattern.host().empty()) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - value_error, |
| - base::UintToString(i), |
| - errors::kCannotClaimAllHostsInExtent); |
| + 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; |
| } |
| + manifest->set_extension_id(extension_id); |
| + return true; |
| + } |
| - // We do not allow authors to put wildcards in their paths. Instead, we |
| - // imply one at the end. |
| - if (pattern.path().find('*') != std::string::npos) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - value_error, |
| - base::UintToString(i), |
| - errors::kNoWildCardsInPaths); |
| + 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; |
| } |
| - pattern.SetPath(pattern.path() + '*'); |
| + manifest->set_extension_id(extension_id); |
| + return true; |
| + } |
| +} |
| - extent->AddPattern(pattern); |
| +bool Extension::LoadHomepageURL(string16* error) { |
| + 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; |
| +} |
| +bool Extension::LoadUpdateURL(string16* error) { |
| + 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()) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidUpdateURL, tmp); |
| + return false; |
| + } |
| return true; |
| } |
| -bool Extension::LoadLaunchURL(string16* error) { |
| - Value* temp = NULL; |
| +bool Extension::LoadExtensionFeatures( |
| + const int& flags, |
| + const ExtensionAPIPermissionSet& api_permissions, |
| + string16* error) { |
| + if (manifest_->HasKey(keys::kOptionsPage) && |
|
Yoyo Zhou
2012/03/01 01:17:25
As Aaron wrote, you can check for the presence of
Devlin
2012/03/02 17:31:04
Done.
|
| + !LoadOptionsPage(error)) |
|
Yoyo Zhou
2012/03/01 01:17:25
unfortunately these aren't all exclusively extensi
Devlin
2012/03/02 17:31:04
Thanks! A while back, Aaron listed the ones he th
|
| + return false; |
| - // launch URL can be either local (to chrome-extension:// root) or an absolute |
| - // web URL. |
| - if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { |
| - if (manifest_->Get(keys::kLaunchWebURL, NULL)) { |
| - *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); |
| + if (manifest_->HasKey(keys::kDevToolsPage) && |
| + !LoadDevToolsPage(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kInputComponents) && |
| + !LoadInputComponents(api_permissions, error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kConvertedFromUserScript)) |
| + manifest_->GetBoolean(keys::kConvertedFromUserScript, |
| + &converted_from_user_script_); |
|
Yoyo Zhou
2012/03/01 01:17:25
indent +1
Devlin
2012/03/02 17:31:04
Done.
|
| + |
| + if (manifest_->HasKey(keys::kIcons) && !LoadIcons(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kCommands) && !LoadCommands(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kPlugins) && !LoadPlugins(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kNaClModules) && !LoadNaClModules(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kContentScripts) && |
| + !LoadContentScripts(flags, error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kWebAccessibleResources) && |
| + !LoadWebAccessibleResources(error)) |
| + return false; |
| + |
| + if (!LoadPageAction(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kBrowserAction) && |
| + !LoadBrowserAction(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kFileBrowserHandlers) && |
| + !LoadFileBrowserHandlers(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kChromeURLOverrides) && |
| + !LoadChromeURLOverrides(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kOmnibox) && !LoadOmnibox(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kTtsEngine) && |
| + !LoadTextToSpeechVoices(error)) |
| + return false; |
| + |
| + if (!LoadWebIntentServices(error)) |
| + return false; |
| + |
| + // These are not actually persisted (they're only used by the store), but |
| + // still validated. |
| + if (manifest_->HasKey(keys::kRequirements) && |
| + !LoadRequirements(error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kDefaultLocale) && |
| + !LoadDefaultLocale(error)) |
| + return false; |
| + |
| + // Defaults to false. |
|
Yoyo Zhou
2012/03/01 01:17:25
Comments like this seem like they should be in the
Devlin
2012/03/02 17:31:04
Done.
|
| + if (manifest_->HasKey(keys::kOfflineEnabled) && |
| + !LoadOfflineEnabled(error)) |
| + return false; |
| + |
| + // Apps default to split mode, extensions default to spanning. |
| + incognito_split_mode_ = is_app(); |
|
Yoyo Zhou
2012/03/01 01:17:25
Should be inside LoadIncognito, which I would rena
Devlin
2012/03/02 17:31:04
Done. I was debating making it LoadIncognitoMode
|
| + if (manifest_->HasKey(keys::kIncognito) && !LoadIncognito(error)) |
| + return false; |
| + |
| + if (!LoadBackgroundScripts(error)) |
| + return false; |
| + |
| + if (!LoadBackgroundPage(api_permissions, error)) |
| + return false; |
| + |
| + if (!LoadBackgroundPersistent(api_permissions, error)) |
| + return false; |
| + |
| + if (!LoadContentSecurityPolicy(error)) |
| + return false; |
| + |
| + return true; |
| +} |
| + |
| +bool Extension::LoadOptionsPage(string16* error) { |
| + 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; |
| + } |
| + } |
| - if (manifest_->Get(keys::kWebURLs, NULL)) { |
| - *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive); |
| + return true; |
| +} |
| + |
| +bool Extension::LoadDevToolsPage(string16* error) { |
| + std::string devtools_str; |
| + if (!manifest_->GetString(keys::kDevToolsPage, &devtools_str)) { |
| + *error = ASCIIToUTF16(errors::kInvalidDevToolsPage); |
| + return false; |
| + } |
| + devtools_url_ = GetResourceURL(devtools_str); |
| + return true; |
| +} |
| + |
| +bool Extension::LoadInputComponents( |
| + const ExtensionAPIPermissionSet& api_permissions, |
| + string16* error) { |
| + ListValue* list_value = NULL; |
| + if (!manifest_->GetList(keys::kInputComponents, &list_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidInputComponents); |
| + return false; |
| + } |
| + |
| + 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; |
| } |
| - std::string launch_path; |
| - if (!temp->GetAsString(&launch_path)) { |
| + // Get input_components[i].name. |
| + if (!module_value->GetString(keys::kName, &name_str)) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidLaunchValue, |
| - keys::kLaunchLocalPath); |
| + errors::kInvalidInputComponentName, base::IntToString(i)); |
| return false; |
| } |
| - // Ensure the launch path is a valid relative URL. |
| - GURL resolved = url().Resolve(launch_path); |
| - if (!resolved.is_valid() || resolved.GetOrigin() != url()) { |
| + // 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::kInvalidLaunchValue, |
| - keys::kLaunchLocalPath); |
| + errors::kInvalidInputComponentType, base::IntToString(i)); |
| return false; |
| } |
| - launch_local_path_ = launch_path; |
| - } else if (manifest_->Get(keys::kLaunchWebURL, &temp)) { |
| - std::string launch_url; |
| - if (!temp->GetAsString(&launch_url)) { |
| + // Get input_components[i].id. |
| + if (!module_value->GetString(keys::kId, &id_str)) { |
| + id_str = ""; |
| + } |
| + |
| + // Get input_components[i].description. |
| + if (!module_value->GetString(keys::kDescription, &description_str)) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidLaunchValue, |
| - keys::kLaunchWebURL); |
| + errors::kInvalidInputComponentDescription, base::IntToString(i)); |
| return false; |
| } |
| + // Get input_components[i].language. |
| + if (!module_value->GetString(keys::kLanguage, &language_str)) { |
| + language_str = ""; |
| + } |
| - // Ensure the launch URL is a valid absolute URL and web extent scheme. |
| - GURL url(launch_url); |
| - URLPattern pattern(kValidWebExtentSchemes); |
| - if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidLaunchValue, |
| - keys::kLaunchWebURL); |
| + // Get input_components[i].layouts. |
| + ListValue* layouts_value = NULL; |
| + if (!module_value->GetList(keys::kLayouts, &layouts_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidInputComponentLayouts); |
| return false; |
| } |
| - launch_web_url_ = launch_url; |
| - } else if (is_app()) { |
| - *error = ASCIIToUTF16(errors::kLaunchURLRequired); |
| + 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); |
| + } |
| + |
| + 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; |
| + } |
| + |
| + // 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; |
| + } |
| + |
| + // Get input_components[i].shortcut_alt. |
| + if (!shortcut_value->GetBoolean(keys::kAltKey, &shortcut_alt)) { |
| + shortcut_alt = false; |
| + } |
| + |
| + // Get input_components[i].shortcut_ctrl. |
| + if (!shortcut_value->GetBoolean(keys::kCtrlKey, &shortcut_ctrl)) { |
| + shortcut_ctrl = false; |
| + } |
| + |
| + // Get input_components[i].shortcut_shift. |
| + if (!shortcut_value->GetBoolean(keys::kShiftKey, &shortcut_shift)) { |
| + shortcut_shift = 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; |
| + } |
| + |
| + return true; |
| +} |
| +bool Extension::LoadIcons(string16* error) { |
| + DictionaryValue* icons_value = NULL; |
| + if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidIcons); |
| return false; |
| } |
| - // If there is no extent, we default the extent based on the launch URL. |
| - if (web_extent().is_empty() && !launch_web_url().empty()) { |
| - GURL launch_url(launch_web_url()); |
| - URLPattern pattern(kValidWebExtentSchemes); |
| - if (!pattern.SetScheme("*")) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidLaunchValue, |
| - keys::kLaunchWebURL); |
| - 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); |
| } |
| - pattern.SetHost(launch_url.host()); |
| - pattern.SetPath("/*"); |
| - extent_.AddPattern(pattern); |
| } |
| + return true; |
| +} |
| - // In order for the --apps-gallery-url switch to work with the gallery |
| - // process isolation, we must insert any provided value into the component |
| - // app's launch url and web extent. |
| - if (id() == extension_misc::kWebStoreAppId) { |
| - std::string gallery_url_str = CommandLine::ForCurrentProcess()-> |
| - GetSwitchValueASCII(switches::kAppsGalleryURL); |
| - |
| - // Empty string means option was not used. |
| - if (!gallery_url_str.empty()) { |
| - GURL gallery_url(gallery_url_str); |
| - OverrideLaunchUrl(gallery_url); |
| +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; |
| } |
| - } else if (id() == extension_misc::kCloudPrintAppId) { |
| - // In order for the --cloud-print-service switch to work, we must update |
| - // the launch URL and web extent. |
| - // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is |
| - // currently under chrome/browser. |
| - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| - GURL cloud_print_service_url = GURL(command_line.GetSwitchValueASCII( |
| - switches::kCloudPrintServiceURL)); |
| - if (!cloud_print_service_url.is_empty()) { |
| - std::string path( |
| - cloud_print_service_url.path() + "/enable_chrome_connector"); |
| - GURL::Replacements replacements; |
| - replacements.SetPathStr(path); |
| - GURL cloud_print_enable_connector_url = |
| - cloud_print_service_url.ReplaceComponents(replacements); |
| - OverrideLaunchUrl(cloud_print_enable_connector_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::kInvalidKeyBindingDictionary, |
| + base::IntToString(command_index)); |
| + return false; |
| + } |
| + |
| + ExtensionKeybinding binding; |
| + if (!binding.Parse(command, *iter, command_index, error)) |
| + return false; // |error| already set. |
| + |
| + commands_.push_back(binding); |
| } |
| } |
| 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); |
| +bool Extension::LoadPlugins(string16* error) { |
| + 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; |
| } |
| - if (!temp->GetAsInteger(target) || *target < 0) { |
| - *target = 0; |
| + // Get plugins[i].path. |
| + if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidLaunchValue, |
| - key); |
| + 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; |
| + } |
| + } |
| + |
| + // 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; |
| } |
| -bool Extension::LoadLaunchContainer(string16* error) { |
| - Value* temp = NULL; |
| - if (!manifest_->Get(keys::kLaunchContainer, &temp)) |
| - return true; |
| - |
| - std::string launch_container_string; |
| - if (!temp->GetAsString(&launch_container_string)) { |
| - *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
| - return false; |
| - } |
| - |
| - if (launch_container_string == values::kLaunchContainerShell) { |
| - launch_container_ = extension_misc::LAUNCH_SHELL; |
| - } else if (launch_container_string == values::kLaunchContainerPanel) { |
| - launch_container_ = extension_misc::LAUNCH_PANEL; |
| - } else if (launch_container_string == values::kLaunchContainerTab) { |
| - launch_container_ = extension_misc::LAUNCH_TAB; |
| - } else { |
| - *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
| +bool Extension::LoadNaClModules(string16* error) { |
| + ListValue* list_value = NULL; |
| + if (!manifest_->GetList(keys::kNaClModules, &list_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidNaClModules); |
| return false; |
| } |
| - bool can_specify_initial_size = |
| - 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_, |
| - keys::kLaunchWidth, |
| - &launch_width_, |
| - can_specify_initial_size, |
| - error)) |
| - return false; |
| - |
| - // Validate container height if present. |
| - if (!ReadLaunchDimension(manifest_, |
| - keys::kLaunchHeight, |
| - &launch_height_, |
| - can_specify_initial_size, |
| - error)) |
| - return false; |
| - |
| - bool can_specify_size_range = |
| - launch_container() == extension_misc::LAUNCH_SHELL; |
| + for (size_t i = 0; i < list_value->GetSize(); ++i) { |
| + DictionaryValue* module_value = NULL; |
| + std::string path_str; |
| + std::string mime_type; |
| - // Validate min size if present. |
| - if (!ReadLaunchDimension(manifest_, |
| - keys::kLaunchMinWidth, |
| - &launch_min_width_, |
| - can_specify_size_range, |
| - error)) |
| - return false; |
| - if (!ReadLaunchDimension(manifest_, |
| - keys::kLaunchMinHeight, |
| - &launch_min_height_, |
| - can_specify_size_range, |
| - error)) |
| + if (!list_value->GetDictionary(i, &module_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidNaClModules); |
| return false; |
| + } |
| - if (launch_container() == extension_misc::LAUNCH_SHELL) { |
| - if (!manifest_->Get(keys::kLaunchWidth, &temp)) { |
| + // Get nacl_modules[i].path. |
| + if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidLaunchValue, |
| - keys::kLaunchWidth); |
| + errors::kInvalidNaClModulesPath, base::IntToString(i)); |
| return false; |
| } |
| - if (!manifest_->Get(keys::kLaunchHeight, &temp)) { |
| + |
| + // Get nacl_modules[i].mime_type. |
| + if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidLaunchValue, |
| - keys::kLaunchHeight); |
| + 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; |
| } |
| return true; |
| } |
| -bool Extension::LoadAppIsolation(string16* error) { |
| - Value* temp = NULL; |
| - if (!manifest_->Get(keys::kIsolation, &temp)) |
| - return true; |
| - |
| - if (temp->GetType() != Value::TYPE_LIST) { |
| - *error = ASCIIToUTF16(errors::kInvalidIsolation); |
| +bool Extension::LoadContentScripts(const int& flags, |
| + string16* error) { |
| + ListValue* list_value; |
| + if (!manifest_->GetList(keys::kContentScripts, &list_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); |
| return false; |
| } |
| - 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)) { |
| + 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::kInvalidIsolationValue, |
| - base::UintToString(i)); |
| + errors::kInvalidContentScript, base::IntToString(i)); |
| 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; |
| + 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. |
| } |
| + content_scripts_.push_back(script); |
| } |
| return true; |
| } |
| -bool Extension::LoadWebIntentServices(string16* error) { |
| - DCHECK(error); |
| - |
| - if (!manifest_->HasKey(keys::kIntents)) |
| - return true; |
| - |
| - DictionaryValue* all_services = NULL; |
| - if (!manifest_->GetDictionary(keys::kIntents, &all_services)) { |
| - *error = ASCIIToUTF16(errors::kInvalidIntents); |
| +bool Extension::LoadWebAccessibleResources(string16* error) { |
| + ListValue* list_value; |
| + if (!manifest_->GetList(keys::kWebAccessibleResources, &list_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList); |
| return false; |
| } |
| - |
| - std::string value; |
| - for (DictionaryValue::key_iterator iter(all_services->begin_keys()); |
| - iter != all_services->end_keys(); ++iter) { |
| - webkit_glue::WebIntentServiceData service; |
| - |
| - DictionaryValue* one_service = NULL; |
| - if (!all_services->GetDictionaryWithoutPathExpansion(*iter, &one_service)) { |
| - *error = ASCIIToUTF16(errors::kInvalidIntent); |
| - return false; |
| - } |
| - service.action = UTF8ToUTF16(*iter); |
| - |
| - ListValue* mime_types = NULL; |
| - if (!one_service->HasKey(keys::kIntentType) || |
| - !one_service->GetList(keys::kIntentType, &mime_types) || |
| - mime_types->GetSize() == 0) { |
| + 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::kInvalidIntentType, *iter); |
| + errors::kInvalidWebAccessibleResource, base::IntToString(i)); |
| return false; |
| } |
| + if (relative_path[0] != '/') |
| + relative_path = '/' + relative_path; |
| + web_accessible_resources_.insert(relative_path); |
| + } |
| - if (one_service->HasKey(keys::kIntentPath)) { |
| - if (!one_service->GetString(keys::kIntentPath, &value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidIntentPath); |
| - return false; |
| - } |
| - if (is_hosted_app()) { |
| - // Hosted apps require an absolute URL for intents. |
| - GURL service_url(value); |
| - if (!service_url.is_valid() || |
| - !(web_extent().MatchesURL(service_url))) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidIntentPageInHostedApp, *iter); |
| - return false; |
| - } |
| - service.service_url = service_url; |
| - } else { |
| - // We do not allow absolute intent URLs in non-hosted apps. |
| - if (GURL(value).is_valid()) { |
| - *error =ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kCannotAccessPage, value.c_str()); |
| - return false; |
| - } |
| - service.service_url = GetResourceURL(value); |
| - } |
| - } |
| + return true; |
| +} |
| - if (one_service->HasKey(keys::kIntentTitle) && |
| - !one_service->GetString(keys::kIntentTitle, &service.title)) { |
| - *error = ASCIIToUTF16(errors::kInvalidIntentTitle); |
| +bool Extension::LoadPageAction(string16* error) { |
| + DictionaryValue* page_action_value = NULL; |
| + |
| + if (manifest_->HasKey(keys::kPageActions)) { |
| + ListValue* list_value = NULL; |
| + if (!manifest_->GetList(keys::kPageActions, &list_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidPageActionsList); |
| return false; |
| } |
| - if (one_service->HasKey(keys::kIntentDisposition)) { |
| - if (!one_service->GetString(keys::kIntentDisposition, &value) || |
| - (value != values::kIntentDispositionWindow && |
| - value != values::kIntentDispositionInline)) { |
| - *error = ASCIIToUTF16(errors::kInvalidIntentDisposition); |
| - return false; |
| - } |
| - if (value == values::kIntentDispositionInline) { |
| - service.disposition = |
| - webkit_glue::WebIntentServiceData::DISPOSITION_INLINE; |
| - } else { |
| - service.disposition = |
| - webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW; |
| - } |
| - } |
| + size_t list_value_length = list_value->GetSize(); |
| - for (size_t i = 0; i < mime_types->GetSize(); ++i) { |
| - if (!mime_types->GetString(i, &service.type)) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidIntentTypeElement, *iter, |
| - std::string(base::IntToString(i))); |
| + 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; |
| } |
| - intents_services_.push_back(service); |
| + } else { // list_value_length > 1u. |
| + *error = ASCIIToUTF16(errors::kInvalidPageActionsListSize); |
| + return false; |
| + } |
| + } 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::LoadBackgroundScripts(string16* error) { |
| - Value* background_scripts_value = NULL; |
| - if (!manifest_->Get(keys::kBackgroundScripts, &background_scripts_value)) |
| - return true; |
| - |
| - CHECK(background_scripts_value); |
| - if (background_scripts_value->GetType() != Value::TYPE_LIST) { |
| - *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts); |
| +bool Extension::LoadBrowserAction(string16* error) { |
| + DictionaryValue* browser_action_value = NULL; |
| + if (!manifest_->GetDictionary(keys::kBrowserAction, &browser_action_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidBrowserAction); |
| return false; |
| } |
| - ListValue* background_scripts = |
| - static_cast<ListValue*>(background_scripts_value); |
| - for (size_t i = 0; i < background_scripts->GetSize(); ++i) { |
| - std::string script; |
| - if (!background_scripts->GetString(i, &script)) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidBackgroundScript, base::IntToString(i)); |
| - return false; |
| - } |
| - background_scripts_.push_back(script); |
| - } |
| - |
| + browser_action_.reset( |
| + LoadExtensionActionHelper(browser_action_value, error)); |
| + if (!browser_action_.get()) |
| + return false; // Failed to parse browser action definition. |
| return true; |
| } |
| -bool Extension::LoadBackgroundPage( |
| - const ExtensionAPIPermissionSet& api_permissions, |
| - string16* error) { |
| - base::Value* background_page_value = NULL; |
| - if (!manifest_->Get(keys::kBackgroundPage, &background_page_value)) |
| - manifest_->Get(keys::kBackgroundPageLegacy, &background_page_value); |
| - |
| - if (!background_page_value) |
| - return true; |
| - |
| - std::string background_str; |
| - if (!background_page_value->GetAsString(&background_str)) { |
| - *error = ASCIIToUTF16(errors::kInvalidBackground); |
| +bool Extension::LoadFileBrowserHandlers(string16* error) { |
| + 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; |
| +} |
| - if (!background_scripts_.empty()) { |
| - *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination); |
| - return false; |
| +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(); |
| +} |
| - if (is_hosted_app()) { |
| - // Make sure "background" permission is set. |
| - if (!api_permissions.count(ExtensionAPIPermission::kBackground)) { |
| - *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded); |
| - return false; |
| +FileBrowserHandler* Extension::LoadFileBrowserHandler( |
| + const DictionaryValue* file_browser_handler, string16* error) { |
| + scoped_ptr<FileBrowserHandler> result( |
| + new FileBrowserHandler()); |
| + result->set_extension_id(id()); |
| + |
| + 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); |
| + |
| + // 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); |
| + |
| + // 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; |
| } |
| - // Hosted apps require an absolute URL. |
| - GURL bg_page(background_str); |
| - if (!bg_page.is_valid()) { |
| - *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); |
| - return false; |
| + 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 (!(bg_page.SchemeIs("https") || |
| - (CommandLine::ForCurrentProcess()->HasSwitch( |
| - switches::kAllowHTTPBackgroundPage) && |
| - bg_page.SchemeIs("http")))) { |
| - *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); |
| - return false; |
| + 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; |
| } |
| - background_url_ = bg_page; |
| - } else { |
| - background_url_ = GetResourceURL(background_str); |
| + result->set_icon_path(default_icon); |
| } |
| - return true; |
| + return result.release(); |
| } |
| -bool Extension::LoadBackgroundPersistent( |
| - const ExtensionAPIPermissionSet& api_permissions, |
| - string16* error) { |
| - Value* background_persistent = NULL; |
| - if (!api_permissions.count(ExtensionAPIPermission::kExperimental) || |
| - !manifest_->Get(keys::kBackgroundPersistent, &background_persistent)) |
| - return true; |
| - |
| - if (!background_persistent->IsType(Value::TYPE_BOOLEAN) || |
| - !background_persistent->GetAsBoolean(&background_page_persists_)) { |
| - *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent); |
| +bool Extension::LoadChromeURLOverrides(string16* error) { |
| + DictionaryValue* overrides = NULL; |
| + if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) { |
| + *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); |
| return false; |
| } |
| - if (!has_background_page()) { |
| - *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage); |
| + // 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); |
| return false; |
| } |
| 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), |
| - 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), |
| - 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); |
| +bool Extension::LoadOmnibox(string16* error) { |
| + if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) || |
| + omnibox_keyword_.empty()) { |
| + *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword); |
| + return false; |
| + } |
| + return true; |
| } |
| -// 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) |
| +bool Extension::LoadTextToSpeechVoices(string16* error) { |
| + DictionaryValue* tts_dict = NULL; |
| + if (!manifest_->GetDictionary(keys::kTtsEngine, &tts_dict)) { |
| + *error = ASCIIToUTF16(errors::kInvalidTts); |
| 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) |
| + if (tts_dict->HasKey(keys::kTtsVoices)) { |
| + ListValue* tts_voices = NULL; |
| + if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) { |
| + *error = ASCIIToUTF16(errors::kInvalidTtsVoices); |
| return false; |
| + } |
| - working = working.substr(start_pos, end_pos - start_pos); |
| - if (working.length() == 0) |
| - return false; |
| - } |
| + 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; |
| + } |
| - return base::Base64Decode(working, output); |
| + 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); |
| + } |
| + } |
| + |
| + tts_voices_.push_back(voice_data); |
| + } |
| + } |
| + return true; |
| } |
| -bool Extension::ProducePEM(const std::string& input, std::string* output) { |
| - DCHECK(output); |
| - if (input.length() == 0) |
| - return false; |
| +bool Extension::LoadWebIntentServices(string16* error) { |
| + DCHECK(error); |
| - return base::Base64Encode(input, output); |
| -} |
| + if (!manifest_->HasKey(keys::kIntents)) |
| + return true; |
| -bool Extension::FormatPEMForFileOutput(const std::string& input, |
| - std::string* output, |
| - bool is_public) { |
| - DCHECK(output); |
| - if (input.length() == 0) |
| + DictionaryValue* all_services = NULL; |
| + if (!manifest_->GetDictionary(keys::kIntents, &all_services)) { |
| + *error = ASCIIToUTF16(errors::kInvalidIntents); |
| 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; |
| -} |
| + std::string value; |
| + for (DictionaryValue::key_iterator iter(all_services->begin_keys()); |
| + iter != all_services->end_keys(); ++iter) { |
| + webkit_glue::WebIntentServiceData service; |
| -// 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); |
| -} |
| + DictionaryValue* one_service = NULL; |
| + if (!all_services->GetDictionaryWithoutPathExpansion(*iter, &one_service)) { |
| + *error = ASCIIToUTF16(errors::kInvalidIntent); |
| + return false; |
| + } |
| + service.action = UTF8ToUTF16(*iter); |
| -// static |
| -void Extension::DecodeIcon(const Extension* extension, |
| - ExtensionIconSet::Icons icon_size, |
| - scoped_ptr<SkBitmap>* result) { |
| - DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result); |
| -} |
| + ListValue* mime_types = NULL; |
| + if (!one_service->HasKey(keys::kIntentType) || |
| + !one_service->GetList(keys::kIntentType, &mime_types) || |
| + mime_types->GetSize() == 0) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidIntentType, *iter); |
| + return false; |
| + } |
| + |
| + if (one_service->HasKey(keys::kIntentPath)) { |
| + if (!one_service->GetString(keys::kIntentPath, &value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidIntentPath); |
| + return false; |
| + } |
| + if (is_hosted_app()) { |
| + // Hosted apps require an absolute URL for intents. |
| + GURL service_url(value); |
| + if (!service_url.is_valid() || |
| + !(web_extent().MatchesURL(service_url))) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidIntentPageInHostedApp, *iter); |
| + return false; |
| + } |
| + service.service_url = service_url; |
| + } else { |
| + // We do not allow absolute intent URLs in non-hosted apps. |
| + if (GURL(value).is_valid()) { |
| + *error =ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kCannotAccessPage, value.c_str()); |
| + return false; |
| + } |
| + service.service_url = GetResourceURL(value); |
| + } |
| + } |
| + |
| + if (one_service->HasKey(keys::kIntentTitle) && |
| + !one_service->GetString(keys::kIntentTitle, &service.title)) { |
| + *error = ASCIIToUTF16(errors::kInvalidIntentTitle); |
| + return false; |
| + } |
| -// static |
| -void Extension::DecodeIconFromPath(const FilePath& icon_path, |
| - ExtensionIconSet::Icons icon_size, |
| - scoped_ptr<SkBitmap>* result) { |
| - if (icon_path.empty()) |
| - return; |
| + if (one_service->HasKey(keys::kIntentDisposition)) { |
| + if (!one_service->GetString(keys::kIntentDisposition, &value) || |
| + (value != values::kIntentDispositionWindow && |
| + value != values::kIntentDispositionInline)) { |
| + *error = ASCIIToUTF16(errors::kInvalidIntentDisposition); |
| + return false; |
| + } |
| + if (value == values::kIntentDispositionInline) { |
| + service.disposition = |
| + webkit_glue::WebIntentServiceData::DISPOSITION_INLINE; |
| + } else { |
| + service.disposition = |
| + webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW; |
| + } |
| + } |
| - 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 i = 0; i < mime_types->GetSize(); ++i) { |
| + if (!mime_types->GetString(i, &service.type)) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidIntentTypeElement, *iter, |
| + std::string(base::IntToString(i))); |
| + return false; |
| + } |
| + intents_services_.push_back(service); |
| + } |
| } |
| + return true; |
| +} |
| - // 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; |
| +bool Extension::LoadRequirements(string16* error) { |
| + DictionaryValue* requirements_value = NULL; |
| + if (!manifest_->GetDictionary(keys::kRequirements, &requirements_value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidRequirements); |
| + 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; |
| + 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; |
| + } |
| } |
| - |
| - result->swap(decoded); |
| + return true; |
| } |
| -// 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); |
| +bool Extension::LoadDefaultLocale(string16* error) { |
| + if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) || |
| + !l10n_util::IsValidLocaleSyntax(default_locale_)) { |
| + *error = ASCIIToUTF16(errors::kInvalidDefaultLocale); |
| + return false; |
| } |
| + return true; |
| } |
| -GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { |
| - return GURL(std::string(chrome::kExtensionScheme) + |
| - chrome::kStandardSchemeSeparator + extension_id + "/"); |
| +bool Extension::LoadOfflineEnabled(string16* error) { |
| + if (!manifest_->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) { |
| + *error = ASCIIToUTF16(errors::kInvalidOfflineEnabled); |
| + return false; |
| + } |
| + return true; |
| } |
| -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; |
| - } |
| +bool Extension::LoadIncognito(string16* error) { |
| + std::string value; |
| + if (!manifest_->GetString(keys::kIncognito, &value)) { |
| + *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); |
| + 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); |
| + 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; |
| } |
| -// 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); |
| +bool Extension::LoadBackgroundScripts(string16* error) { |
| + Value* background_scripts_value = NULL; |
| + if (!manifest_->Get(keys::kBackgroundScripts, &background_scripts_value)) |
| 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; |
| - } |
| - manifest->set_extension_id(extension_id); |
| - return true; |
| + CHECK(background_scripts_value); |
| + if (background_scripts_value->GetType() != Value::TYPE_LIST) { |
| + *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts); |
| + return false; |
| } |
| - 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."; |
| + ListValue* background_scripts = |
| + static_cast<ListValue*>(background_scripts_value); |
| + for (size_t i = 0; i < background_scripts->GetSize(); ++i) { |
| + std::string script; |
| + if (!background_scripts->GetString(i, &script)) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidBackgroundScript, base::IntToString(i)); |
| return false; |
| } |
| - manifest->set_extension_id(extension_id); |
| - return true; |
| + background_scripts_.push_back(script); |
| } |
| -} |
| - |
| -bool Extension::InitFromValue(int flags, string16* error) { |
| - DCHECK(error); |
| - base::AutoLock auto_lock(runtime_data_lock_); |
| + return true; |
| +} |
| - // Initialize permissions with an empty, default permission set. |
| - runtime_data_.SetActivePermissions(new ExtensionPermissionSet()); |
| - optional_permission_set_ = new ExtensionPermissionSet(); |
| - required_permission_set_ = new ExtensionPermissionSet(); |
| +bool Extension::LoadBackgroundPage( |
| + const ExtensionAPIPermissionSet& api_permissions, |
| + string16* error) { |
| + base::Value* background_page_value = NULL; |
| + if (!manifest_->Get(keys::kBackgroundPage, &background_page_value)) |
| + manifest_->Get(keys::kBackgroundPageLegacy, &background_page_value); |
|
Yoyo Zhou
2012/03/01 01:17:25
leave the original indent
Devlin
2012/03/02 17:31:04
Whoops, didn't mean to do that. Done.
|
| - creation_flags_ = flags; |
| + if (!background_page_value) |
| + return true; |
| - if (!LoadManifestVersion(error)) |
| + std::string background_str; |
| + if (!background_page_value->GetAsString(&background_str)) { |
| + *error = ASCIIToUTF16(errors::kInvalidBackground); |
| return false; |
| + } |
| - // We don't ned to validate because InitExtensionID already did that. |
| - manifest_->GetString(keys::kPublicKey, &public_key_); |
| + if (!background_scripts_.empty()) { |
| + *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination); |
| + return false; |
| + } |
| - // Initialize the URL. |
| - extension_url_ = Extension::GetBaseURLFromExtensionId(id()); |
| + if (is_hosted_app()) { |
| + // Make sure "background" permission is set. |
| + if (!api_permissions.count(ExtensionAPIPermission::kBackground)) { |
| + *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded); |
| + return false; |
| + } |
| + // Hosted apps require an absolute URL. |
| + GURL bg_page(background_str); |
| + if (!bg_page.is_valid()) { |
| + *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); |
| + return false; |
| + } |
| - // Initialize version. |
| - std::string version_str; |
| - if (!manifest_->GetString(keys::kVersion, &version_str)) { |
| - *error = ASCIIToUTF16(errors::kInvalidVersion); |
| - return false; |
| + if (!(bg_page.SchemeIs("https") || |
| + (CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kAllowHTTPBackgroundPage) && |
| + bg_page.SchemeIs("http")))) { |
| + *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); |
| + return false; |
| + } |
| + background_url_ = bg_page; |
| + } else { |
| + background_url_ = GetResourceURL(background_str); |
| } |
| - version_.reset(Version::GetVersionFromString(version_str)); |
| - if (!version_.get() || |
| - version_->components().size() > 4) { |
| - *error = ASCIIToUTF16(errors::kInvalidVersion); |
| + |
| + return true; |
| +} |
| + |
| +bool Extension::LoadBackgroundPersistent( |
| + const ExtensionAPIPermissionSet& api_permissions, |
| + string16* error) { |
| + Value* background_persistent = NULL; |
| + if (!api_permissions.count(ExtensionAPIPermission::kExperimental) || |
| + !manifest_->Get(keys::kBackgroundPersistent, &background_persistent)) |
| + return true; |
| + |
| + if (!background_persistent->IsType(Value::TYPE_BOOLEAN) || |
| + !background_persistent->GetAsBoolean(&background_page_persists_)) { |
| + *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent); |
| return false; |
| } |
| - // Initialize name. |
| - string16 localized_name; |
| - if (!manifest_->GetString(keys::kName, &localized_name)) { |
| - *error = ASCIIToUTF16(errors::kInvalidName); |
| + if (!has_background_page()) { |
| + *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage); |
| return false; |
| } |
| - base::i18n::AdjustStringForLocaleDirection(&localized_name); |
| - name_ = UTF16ToUTF8(localized_name); |
| - // 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))) { |
| + return true; |
| +} |
| + |
| +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; |
| + } |
| + 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; |
| + } |
| + |
| + 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; |
| +} |
| + |
| +// Load App settings. LoadExtent at least has to be done before |
| +// ParsePermissions(), because the valid permissions depend on what type of |
| +// package this is. |
|
Yoyo Zhou
2012/03/01 01:17:25
This comment belongs in InitValue (at this functio
Devlin
2012/03/02 17:31:04
Done.
|
| +bool Extension::LoadAppFeatures(string16* error) { |
| + if (!LoadExtent(keys::kWebURLs, &extent_, |
| + errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || |
| + !LoadLaunchURL(error) || |
| + !LoadLaunchContainer(error)) |
| return false; |
| - } |
| if (is_platform_app()) { |
| if (launch_container() != extension_misc::LAUNCH_SHELL) { |
| @@ -1806,866 +2163,669 @@ bool Extension::InitFromValue(int flags, string16* error) { |
| return false; |
| } |
| - // Initialize the permissions (optional). |
| - ExtensionAPIPermissionSet api_permissions; |
| - URLPatternSet host_permissions; |
| - if (!ParsePermissions(keys::kPermissions, |
| - flags, |
| - error, |
| - &api_permissions, |
| - &host_permissions)) { |
| - return false; |
| - } |
| + return true; |
| +} |
| - // 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)) { |
| +bool Extension::LoadExtent(const char* key, |
| + URLPatternSet* extent, |
| + const char* list_error, |
| + const char* value_error, |
| + string16* error) { |
| + Value* temp = NULL; |
| + if (!manifest_->Get(key, &temp)) |
| + return true; |
| + |
| + if (temp->GetType() != Value::TYPE_LIST) { |
| + *error = ASCIIToUTF16(list_error); |
| return false; |
| } |
| - // Initialize description (if present). |
| - if (manifest_->HasKey(keys::kDescription)) { |
| - if (!manifest_->GetString(keys::kDescription, &description_)) { |
| - *error = ASCIIToUTF16(errors::kInvalidDescription); |
| + ListValue* pattern_list = static_cast<ListValue*>(temp); |
| + for (size_t i = 0; i < pattern_list->GetSize(); ++i) { |
| + std::string pattern_string; |
| + if (!pattern_list->GetString(i, &pattern_string)) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16(value_error, |
| + base::UintToString(i), |
| + errors::kExpectString); |
| return false; |
| } |
| - } |
| - // Initialize homepage url (if present). |
| - if (manifest_->HasKey(keys::kHomepageURL)) { |
| - std::string tmp; |
| - if (!manifest_->GetString(keys::kHomepageURL, &tmp)) { |
| + URLPattern pattern(kValidWebExtentSchemes); |
| + URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); |
| + if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { |
| + pattern_string += "/"; |
| + parse_result = pattern.Parse(pattern_string); |
| + } |
| + |
| + if (parse_result != URLPattern::PARSE_SUCCESS) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidHomepageURL, ""); |
| + value_error, |
| + base::UintToString(i), |
| + URLPattern::GetParseResultString(parse_result)); |
| return false; |
| } |
| - homepage_url_ = GURL(tmp); |
| - if (!homepage_url_.is_valid() || |
| - (!homepage_url_.SchemeIs("http") && |
| - !homepage_url_.SchemeIs("https"))) { |
| + |
| + // Do not allow authors to claim "<all_urls>". |
| + if (pattern.match_all_urls()) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidHomepageURL, tmp); |
| + value_error, |
| + base::UintToString(i), |
| + errors::kCannotClaimAllURLsInExtent); |
| return false; |
| } |
| - } |
| - // Initialize update url (if present). |
| - if (manifest_->HasKey(keys::kUpdateURL)) { |
| - std::string tmp; |
| - if (!manifest_->GetString(keys::kUpdateURL, &tmp)) { |
| + // Do not allow authors to claim "*" for host. |
| + if (pattern.host().empty()) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidUpdateURL, ""); |
| + value_error, |
| + base::UintToString(i), |
| + errors::kCannotClaimAllHostsInExtent); |
| return false; |
| } |
| - update_url_ = GURL(tmp); |
| - if (!update_url_.is_valid() || |
| - update_url_.has_ref()) { |
| + |
| + // We do not allow authors to put wildcards in their paths. Instead, we |
| + // imply one at the end. |
| + if (pattern.path().find('*') != std::string::npos) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidUpdateURL, tmp); |
| + value_error, |
| + base::UintToString(i), |
| + errors::kNoWildCardsInPaths); |
| return false; |
| } |
| + pattern.SetPath(pattern.path() + '*'); |
| + |
| + extent->AddPattern(pattern); |
| } |
| - // 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); |
| - return false; |
| - } |
| + return true; |
| +} |
| - scoped_ptr<Version> minimum_version( |
| - Version::GetVersionFromString(minimum_version_string)); |
| - if (!minimum_version.get()) { |
| - *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion); |
| - return false; |
| - } |
| +bool Extension::LoadLaunchURL(string16* error) { |
| + Value* temp = NULL; |
| - chrome::VersionInfo current_version_info; |
| - if (!current_version_info.is_valid()) { |
| - NOTREACHED(); |
| + // launch URL can be either local (to chrome-extension:// root) or an absolute |
| + // web URL. |
| + if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { |
| + if (manifest_->Get(keys::kLaunchWebURL, NULL)) { |
| + *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); |
| return false; |
| } |
| - scoped_ptr<Version> current_version( |
| - Version::GetVersionFromString(current_version_info.Version())); |
| - if (!current_version.get()) { |
| - DCHECK(false); |
| + if (manifest_->Get(keys::kWebURLs, NULL)) { |
| + *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive); |
| return false; |
| } |
| - if (current_version->CompareTo(*minimum_version) < 0) { |
| + std::string launch_path; |
| + if (!temp->GetAsString(&launch_path)) { |
| *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kChromeVersionTooLow, |
| - l10n_util::GetStringUTF8(IDS_PRODUCT_NAME), |
| - minimum_version_string); |
| + errors::kInvalidLaunchValue, |
| + keys::kLaunchLocalPath); |
| 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); |
| + // Ensure the launch path is a valid relative URL. |
| + GURL resolved = url().Resolve(launch_path); |
| + if (!resolved.is_valid() || resolved.GetOrigin() != url()) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidLaunchValue, |
| + keys::kLaunchLocalPath); |
| return false; |
| } |
| - 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::kInvalidKeyBindingDictionary, |
| - base::IntToString(command_index)); |
| - return false; |
| - } |
| - |
| - ExtensionKeybinding binding; |
| - if (!binding.Parse(command, *iter, command_index, error)) |
| - return false; // |error| already set. |
| - |
| - commands_.push_back(binding); |
| - } |
| - } |
| - |
| - // Initialize icons (if present). |
| - if (manifest_->HasKey(keys::kIcons)) { |
| - DictionaryValue* icons_value = NULL; |
| - if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidIcons); |
| + launch_local_path_ = launch_path; |
| + } else if (manifest_->Get(keys::kLaunchWebURL, &temp)) { |
| + std::string launch_url; |
| + if (!temp->GetAsString(&launch_url)) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidLaunchValue, |
| + keys::kLaunchWebURL); |
| 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); |
| - } |
| - } |
| - } |
| - |
| - // Initialize themes (if present). |
| - if (manifest_->HasKey(keys::kTheme)) { |
| - DictionaryValue* theme_value = NULL; |
| - if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidTheme); |
| + // Ensure the launch URL is a valid absolute URL and web extent scheme. |
| + GURL url(launch_url); |
| + URLPattern pattern(kValidWebExtentSchemes); |
| + if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidLaunchValue, |
| + keys::kLaunchWebURL); |
| 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; |
| - } |
| - } |
| - theme_tints_.reset(tints_value->DeepCopy()); |
| - } |
| - |
| - DictionaryValue* display_properties_value = NULL; |
| - if (theme_value->GetDictionary(keys::kThemeDisplayProperties, |
| - &display_properties_value)) { |
| - theme_display_properties_.reset( |
| - display_properties_value->DeepCopy()); |
| - } |
| - |
| - return true; |
| + launch_web_url_ = launch_url; |
| + } else if (is_app()) { |
| + *error = ASCIIToUTF16(errors::kLaunchURLRequired); |
| + 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); |
| + // If there is no extent, we default the extent based on the launch URL. |
| + if (web_extent().is_empty() && !launch_web_url().empty()) { |
| + GURL launch_url(launch_web_url()); |
| + URLPattern pattern(kValidWebExtentSchemes); |
| + if (!pattern.SetScheme("*")) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidLaunchValue, |
| + keys::kLaunchWebURL); |
| return false; |
| } |
| + pattern.SetHost(launch_url.host()); |
| + pattern.SetPath("/*"); |
| + extent_.AddPattern(pattern); |
| + } |
| - 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; |
| - } |
| - } |
| + // In order for the --apps-gallery-url switch to work with the gallery |
| + // process isolation, we must insert any provided value into the component |
| + // app's launch url and web extent. |
| + if (id() == extension_misc::kWebStoreAppId) { |
| + std::string gallery_url_str = CommandLine::ForCurrentProcess()-> |
| + GetSwitchValueASCII(switches::kAppsGalleryURL); |
| - // 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 |
| + // Empty string means option was not used. |
| + if (!gallery_url_str.empty()) { |
| + GURL gallery_url(gallery_url_str); |
| + OverrideLaunchUrl(gallery_url); |
| } |
| - } |
| - |
| - if (manifest_->HasKey(keys::kNaClModules)) { |
| - ListValue* list_value = NULL; |
| - if (!manifest_->GetList(keys::kNaClModules, &list_value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidNaClModules); |
| - return false; |
| + } else if (id() == extension_misc::kCloudPrintAppId) { |
| + // In order for the --cloud-print-service switch to work, we must update |
| + // the launch URL and web extent. |
| + // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is |
| + // currently under chrome/browser. |
| + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
| + GURL cloud_print_service_url = GURL(command_line.GetSwitchValueASCII( |
| + switches::kCloudPrintServiceURL)); |
| + if (!cloud_print_service_url.is_empty()) { |
| + std::string path( |
| + cloud_print_service_url.path() + "/enable_chrome_connector"); |
| + GURL::Replacements replacements; |
| + replacements.SetPathStr(path); |
| + GURL cloud_print_enable_connector_url = |
| + cloud_print_service_url.ReplaceComponents(replacements); |
| + OverrideLaunchUrl(cloud_print_enable_connector_url); |
| } |
| + } |
| + return true; |
| +} |
| - 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; |
| - } |
| - |
| - // 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; |
| +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; |
| } |
| - } |
| - |
| - // Initialize content scripts (optional). |
| - if (manifest_->HasKey(keys::kContentScripts)) { |
| - ListValue* list_value; |
| - if (!manifest_->GetList(keys::kContentScripts, &list_value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); |
| + if (!temp->GetAsInteger(target) || *target < 0) { |
| + *target = 0; |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidLaunchValue, |
| + key); |
| return false; |
| } |
| + } |
| + return true; |
| +} |
| - 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; |
| - } |
| +bool Extension::LoadLaunchContainer(string16* error) { |
| + Value* temp = NULL; |
| + if (!manifest_->Get(keys::kLaunchContainer, &temp)) |
| + return true; |
| - 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. |
| - } |
| - content_scripts_.push_back(script); |
| - } |
| + std::string launch_container_string; |
| + if (!temp->GetAsString(&launch_container_string)) { |
| + *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
| + return false; |
| } |
| - // 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 (relative_path[0] != '/') |
| - relative_path = '/' + relative_path; |
| - web_accessible_resources_.insert(relative_path); |
| - } |
| + if (launch_container_string == values::kLaunchContainerShell) { |
| + launch_container_ = extension_misc::LAUNCH_SHELL; |
| + } else if (launch_container_string == values::kLaunchContainerPanel) { |
| + launch_container_ = extension_misc::LAUNCH_PANEL; |
| + } else if (launch_container_string == values::kLaunchContainerTab) { |
| + launch_container_ = extension_misc::LAUNCH_TAB; |
| + } else { |
| + *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
| + return false; |
| } |
| - // Initialize page action (optional). |
| - DictionaryValue* page_action_value = NULL; |
| + bool can_specify_initial_size = |
| + launch_container() == extension_misc::LAUNCH_PANEL || |
| + launch_container() == extension_misc::LAUNCH_WINDOW || |
| + launch_container() == extension_misc::LAUNCH_SHELL; |
| - if (manifest_->HasKey(keys::kPageActions)) { |
| - ListValue* list_value = NULL; |
| - if (!manifest_->GetList(keys::kPageActions, &list_value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidPageActionsList); |
| + // Validate the container width if present. |
| + if (!ReadLaunchDimension(manifest_, |
| + keys::kLaunchWidth, |
| + &launch_width_, |
| + can_specify_initial_size, |
| + error)) |
| return false; |
| - } |
| - size_t list_value_length = list_value->GetSize(); |
| + // Validate container height if present. |
| + if (!ReadLaunchDimension(manifest_, |
| + keys::kLaunchHeight, |
| + &launch_height_, |
| + can_specify_initial_size, |
| + error)) |
| + return false; |
| - 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); |
| + bool can_specify_size_range = |
| + launch_container() == extension_misc::LAUNCH_SHELL; |
| + |
| + // Validate min size if present. |
| + if (!ReadLaunchDimension(manifest_, |
| + keys::kLaunchMinWidth, |
| + &launch_min_width_, |
| + can_specify_size_range, |
| + error)) |
| + return false; |
| + if (!ReadLaunchDimension(manifest_, |
| + keys::kLaunchMinHeight, |
| + &launch_min_height_, |
| + can_specify_size_range, |
| + error)) |
| + return false; |
| + |
| + if (launch_container() == extension_misc::LAUNCH_SHELL) { |
| + if (!manifest_->Get(keys::kLaunchWidth, &temp)) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidLaunchValue, |
| + keys::kLaunchWidth); |
| return false; |
| } |
| - } else if (manifest_->HasKey(keys::kPageAction)) { |
| - if (!manifest_->GetDictionary(keys::kPageAction, &page_action_value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidPageAction); |
| + if (!manifest_->Get(keys::kLaunchHeight, &temp)) { |
| + *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| + errors::kInvalidLaunchValue, |
| + keys::kLaunchHeight); |
| 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; |
| +} |
| - // 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; |
| - } |
| - browser_action_.reset( |
| - LoadExtensionActionHelper(browser_action_value, error)); |
| - if (!browser_action_.get()) |
| - return false; // Failed to parse browser action definition. |
| +bool Extension::LoadAppIsolation(string16* error) { |
| + Value* temp = NULL; |
| + if (!manifest_->Get(keys::kIsolation, &temp)) |
| + return true; |
| + |
| + 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) { |
| + 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; |
| - |
| - if (!LoadBackgroundPage(api_permissions, 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 (!LoadBackgroundPersistent(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 (manifest_->HasKey(keys::kDefaultLocale)) { |
| - if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) || |
| - !l10n_util::IsValidLocaleSyntax(default_locale_)) { |
| - *error = ASCIIToUTF16(errors::kInvalidDefaultLocale); |
| - 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), |
| + 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), |
| + wants_file_access_(false), |
| + creation_flags_(0) { |
| + DCHECK(path.empty() || path.IsAbsolute()); |
| + path_ = MaybeNormalizePath(path); |
| +} |
| - // Chrome URL overrides (optional) |
| - if (manifest_->HasKey(keys::kChromeURLOverrides)) { |
| - DictionaryValue* overrides = NULL; |
| - if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) { |
| - *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); |
| - return false; |
| - } |
| +Extension::~Extension() { |
| + if (manifest_) |
| + delete manifest_; |
| +} |
| - // 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) |
| +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 |
| - ) || |
| - !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); |
| - } |
| + 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; |
| - // An extension may override at most one page. |
| - if (overrides->size() > 1) { |
| - *error = ASCIIToUTF16(errors::kMultipleOverrides); |
| + 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 (manifest_->HasKey(keys::kInputComponents)) { |
| - ListValue* list_value = NULL; |
| - if (!manifest_->GetList(keys::kInputComponents, &list_value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidInputComponents); |
| + if (start_pos >= end_pos) |
| return false; |
| - } |
| - |
| - 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; |
| - } |
| + working = working.substr(start_pos, end_pos - start_pos); |
| + if (working.length() == 0) |
| + return false; |
| + } |
| - // Get input_components[i].name. |
| - if (!module_value->GetString(keys::kName, &name_str)) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidInputComponentName, base::IntToString(i)); |
| - return false; |
| - } |
| + return base::Base64Decode(working, output); |
| +} |
| - // 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; |
| - } |
| +bool Extension::ProducePEM(const std::string& input, std::string* output) { |
| + DCHECK(output); |
| + if (input.length() == 0) |
| + return false; |
| - // Get input_components[i].id. |
| - if (!module_value->GetString(keys::kId, &id_str)) { |
| - id_str = ""; |
| - } |
| + return base::Base64Encode(input, output); |
| +} |
| - // Get input_components[i].description. |
| - if (!module_value->GetString(keys::kDescription, &description_str)) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidInputComponentDescription, 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].language. |
| - if (!module_value->GetString(keys::kLanguage, &language_str)) { |
| - language_str = ""; |
| - } |
| + return true; |
| +} |
| - // Get input_components[i].layouts. |
| - ListValue* layouts_value = NULL; |
| - if (!module_value->GetList(keys::kLayouts, &layouts_value)) { |
| - *error = ASCIIToUTF16(errors::kInvalidInputComponentLayouts); |
| - return false; |
| - } |
| +// 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); |
| +} |
| - 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); |
| - } |
| +// static |
| +void Extension::DecodeIcon(const Extension* extension, |
| + ExtensionIconSet::Icons icon_size, |
| + scoped_ptr<SkBitmap>* result) { |
| + DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result); |
| +} |
| - 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; |
| - } |
| +// 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].shortcut_keycode. |
| - if (!shortcut_value->GetString(keys::kKeycode, &shortcut_keycode_str)) { |
| - *error = ExtensionErrorUtils::FormatErrorMessageUTF16( |
| - errors::kInvalidInputComponentShortcutKeycode, |
| - base::IntToString(i)); |
| - 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; |
| + } |
| - // Get input_components[i].shortcut_alt. |
| - if (!shortcut_value->GetBoolean(keys::kAltKey, &shortcut_alt)) { |
| - shortcut_alt = false; |
| - } |
| + // 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; |
| + } |
| - // Get input_components[i].shortcut_ctrl. |
| - if (!shortcut_value->GetBoolean(keys::kCtrlKey, &shortcut_ctrl)) { |
| - shortcut_ctrl = 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_shift. |
| - if (!shortcut_value->GetBoolean(keys::kShiftKey, &shortcut_shift)) { |
| - shortcut_shift = false; |
| - } |
| - } |
| + result->swap(decoded); |
| +} |
| - 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; |
| - } |
| +// 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); |
| } |
| +} |
| - if (manifest_->HasKey(keys::kOmnibox)) { |
| - if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) || |
| - omnibox_keyword_.empty()) { |
| - *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword); |
| - return false; |
| - } |
| - } |
| +GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { |
|
Yoyo Zhou
2012/03/01 01:17:25
Put a
// static
before this.
|
| + return GURL(std::string(chrome::kExtensionScheme) + |
| + chrome::kStandardSchemeSeparator + extension_id + "/"); |
| +} |
| - 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; |
| - } |
| +bool Extension::InitFromValue(int flags, string16* error) { |
| + DCHECK(error); |
| - 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_)); |
| - } |
| + base::AutoLock auto_lock(runtime_data_lock_); |
| - // 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); |
| - } |
| + // Initialize permissions with an empty, default permission set. |
| + runtime_data_.SetActivePermissions(new ExtensionPermissionSet()); |
| + optional_permission_set_ = new ExtensionPermissionSet(); |
| + required_permission_set_ = new ExtensionPermissionSet(); |
| - // 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; |
| - } |
| + creation_flags_ = flags; |
| - if (tts_dict->HasKey(keys::kTtsVoices)) { |
| - ListValue* tts_voices = NULL; |
| - if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) { |
| - *error = ASCIIToUTF16(errors::kInvalidTtsVoices); |
| - return false; |
| - } |
| + if (!LoadManifestVersion(error)) |
| + return false; |
| - 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; |
| - } |
| + // We don't ned to validate because InitExtensionID already did that. |
| + manifest_->GetString(keys::kPublicKey, &public_key_); |
| - 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); |
| - } |
| - } |
| + // Initialize permissions with an empty, default permission set. |
| + runtime_data_.SetActivePermissions(new ExtensionPermissionSet()); |
| + optional_permission_set_ = new ExtensionPermissionSet(); |
| + required_permission_set_ = new ExtensionPermissionSet(); |
| - tts_voices_.push_back(voice_data); |
| - } |
| - } |
| - } |
| + if (!LoadSharedFeatures(flags, error)) |
|
Yoyo Zhou
2012/03/01 01:17:25
In this and in LoadExtensionFeatures and whatever
|
| + return false; |
| - // Initialize web intents (optional). |
| - if (!LoadWebIntentServices(error)) |
| + if (is_app() && !LoadAppFeatures(error)) |
| 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; |
| - } |
| + ExtensionAPIPermissionSet api_permissions; |
| + URLPatternSet host_permissions; |
| + if (!ParsePermissions(keys::kPermissions, |
| + flags, |
| + error, |
| + &api_permissions, |
| + &host_permissions)) { |
| + 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; |
| - } |
| + ExtensionAPIPermissionSet optional_api_permissions; |
| + URLPatternSet optional_host_permissions; |
| + if (!ParsePermissions(keys::kOptionalPermissions, |
| + flags, |
| + error, |
| + &optional_api_permissions, |
| + &optional_host_permissions)) { |
| + 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; |
| - } |
| + // App isolation. |
| + if (api_permissions.count(ExtensionAPIPermission::kExperimental) && |
| + !LoadAppIsolation(error)) |
|
Yoyo Zhou
2012/03/01 01:17:25
This used to have an is_app check.
Ideally this w
Yoyo Zhou
2012/03/02 20:20:07
Can you put the is_app check back here?
|
| + 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(flags, api_permissions, error)) |
| + return false; |
| + |
| + if (manifest_->HasKey(keys::kTheme) && !LoadThemeFeatures(error)) |
| + return false; |
| if (HasMultipleUISurfaces()) { |
| *error = ASCIIToUTF16(errors::kOneUISurfaceOnly); |
| @@ -3075,7 +3235,6 @@ bool Extension::CanSpecifyAPIPermission( |
| string16* error) const { |
| if (location() == Extension::COMPONENT) |
| return true; |
| - |
| bool access_denied = false; |
| if (permission->HasWhitelist()) { |
| if (permission->IsWhitelisted(id())) |
| @@ -3253,7 +3412,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(); |