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