| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "extensions/common/extension.h" |
| |
| #include "base/base64.h" |
| #include "base/basictypes.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/i18n/rtl.h" |
| #include "base/logging.h" |
| #include "base/memory/singleton.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "base/version.h" |
| #include "content/public/common/url_constants.h" |
| #include "extensions/common/constants.h" |
| #include "extensions/common/error_utils.h" |
| #include "extensions/common/id_util.h" |
| #include "extensions/common/manifest.h" |
| #include "extensions/common/manifest_constants.h" |
| #include "extensions/common/manifest_handler.h" |
| #include "extensions/common/manifest_handlers/permissions_parser.h" |
| #include "extensions/common/permissions/permission_set.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "extensions/common/permissions/permissions_info.h" |
| #include "extensions/common/switches.h" |
| #include "extensions/common/url_pattern.h" |
| #include "net/base/filename_util.h" |
| #include "url/url_util.h" |
| |
| namespace extensions { |
| |
| namespace keys = manifest_keys; |
| namespace values = manifest_values; |
| namespace errors = manifest_errors; |
| |
| namespace { |
| |
| const int kModernManifestVersion = 2; |
| const int kPEMOutputColumns = 64; |
| |
| // KEY MARKERS |
| const char kKeyBeginHeaderMarker[] = "-----BEGIN"; |
| const char kKeyBeginFooterMarker[] = "-----END"; |
| const char kKeyInfoEndMarker[] = "KEY-----"; |
| const char kPublic[] = "PUBLIC"; |
| const char kPrivate[] = "PRIVATE"; |
| |
| bool ContainsReservedCharacters(const base::FilePath& path) { |
| // We should disallow backslash '\\' as file path separator even on Windows, |
| // because the backslash is not regarded as file path separator on Linux/Mac. |
| // Extensions are cross-platform. |
| // Since FilePath uses backslash '\\' as file path separator on Windows, so we |
| // need to check manually. |
| if (path.value().find('\\') != path.value().npos) |
| return true; |
| return !net::IsSafePortableRelativePath(path); |
| } |
| |
| } // namespace |
| |
| const int Extension::kInitFromValueFlagBits = 12; |
| |
| const char Extension::kMimeType[] = "application/x-chrome-extension"; |
| |
| const int Extension::kValidWebExtentSchemes = |
| URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; |
| |
| const int Extension::kValidHostPermissionSchemes = URLPattern::SCHEME_CHROMEUI | |
| URLPattern::SCHEME_HTTP | |
| URLPattern::SCHEME_HTTPS | |
| URLPattern::SCHEME_FILE | |
| URLPattern::SCHEME_FTP; |
| |
| // |
| // Extension |
| // |
| |
| // static |
| scoped_refptr<Extension> Extension::Create(const base::FilePath& path, |
| Manifest::Location location, |
| const base::DictionaryValue& value, |
| int flags, |
| std::string* utf8_error) { |
| return Extension::Create(path, |
| location, |
| value, |
| flags, |
| std::string(), // ID is ignored if empty. |
| utf8_error); |
| } |
| |
| // TODO(sungguk): Continue removing std::string errors and replacing |
| // with base::string16. See http://crbug.com/71980. |
| scoped_refptr<Extension> Extension::Create(const base::FilePath& path, |
| Manifest::Location location, |
| const base::DictionaryValue& value, |
| int flags, |
| const std::string& explicit_id, |
| std::string* utf8_error) { |
| DCHECK(utf8_error); |
| base::string16 error; |
| scoped_ptr<extensions::Manifest> manifest( |
| new extensions::Manifest( |
| location, scoped_ptr<base::DictionaryValue>(value.DeepCopy()))); |
| |
| if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error)) { |
| *utf8_error = base::UTF16ToUTF8(error); |
| return NULL; |
| } |
| |
| std::vector<InstallWarning> install_warnings; |
| if (!manifest->ValidateManifest(utf8_error, &install_warnings)) { |
| return NULL; |
| } |
| |
| scoped_refptr<Extension> extension = new Extension(path, manifest.Pass()); |
| extension->install_warnings_.swap(install_warnings); |
| |
| if (!extension->InitFromValue(flags, &error)) { |
| *utf8_error = base::UTF16ToUTF8(error); |
| return NULL; |
| } |
| |
| return extension; |
| } |
| |
| // static |
| bool Extension::IdIsValid(const std::string& id) { |
| // Verify that the id is legal. |
| if (id.size() != (id_util::kIdSize * 2)) |
| return false; |
| |
| // We only support lowercase IDs, because IDs can be used as URL components |
| // (where GURL will lowercase it). |
| std::string temp = StringToLowerASCII(id); |
| for (size_t i = 0; i < temp.size(); i++) |
| if (temp[i] < 'a' || temp[i] > 'p') |
| return false; |
| |
| return true; |
| } |
| |
| Manifest::Type Extension::GetType() const { |
| return converted_from_user_script() ? |
| Manifest::TYPE_USER_SCRIPT : manifest_->type(); |
| } |
| |
| // static |
| GURL Extension::GetResourceURL(const GURL& extension_url, |
| const std::string& relative_path) { |
| DCHECK(extension_url.SchemeIs(extensions::kExtensionScheme)); |
| DCHECK_EQ("/", extension_url.path()); |
| |
| std::string path = relative_path; |
| |
| // If the relative path starts with "/", it is "absolute" relative to the |
| // extension base directory, but extension_url is already specified to refer |
| // to that base directory, so strip the leading "/" if present. |
| if (relative_path.size() > 0 && relative_path[0] == '/') |
| path = relative_path.substr(1); |
| |
| GURL ret_val = GURL(extension_url.spec() + path); |
| DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false)); |
| |
| return ret_val; |
| } |
| |
| bool Extension::ResourceMatches(const URLPatternSet& pattern_set, |
| const std::string& resource) const { |
| return pattern_set.MatchesURL(extension_url_.Resolve(resource)); |
| } |
| |
| ExtensionResource Extension::GetResource( |
| const std::string& relative_path) const { |
| std::string new_path = relative_path; |
| // We have some legacy data where resources have leading slashes. |
| // See: http://crbug.com/121164 |
| if (!new_path.empty() && new_path.at(0) == '/') |
| new_path.erase(0, 1); |
| base::FilePath relative_file_path = base::FilePath::FromUTF8Unsafe(new_path); |
| if (ContainsReservedCharacters(relative_file_path)) |
| return ExtensionResource(); |
| ExtensionResource r(id(), path(), relative_file_path); |
| if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { |
| r.set_follow_symlinks_anywhere(); |
| } |
| return r; |
| } |
| |
| ExtensionResource Extension::GetResource( |
| const base::FilePath& relative_file_path) const { |
| if (ContainsReservedCharacters(relative_file_path)) |
| return ExtensionResource(); |
| ExtensionResource r(id(), path(), relative_file_path); |
| if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { |
| r.set_follow_symlinks_anywhere(); |
| } |
| return r; |
| } |
| |
| // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a |
| // util class in base: |
| // http://code.google.com/p/chromium/issues/detail?id=13572 |
| // static |
| bool Extension::ParsePEMKeyBytes(const std::string& input, |
| std::string* output) { |
| DCHECK(output); |
| if (!output) |
| return false; |
| if (input.length() == 0) |
| return false; |
| |
| std::string working = input; |
| if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { |
| working = base::CollapseWhitespaceASCII(working, true); |
| size_t header_pos = working.find(kKeyInfoEndMarker, |
| sizeof(kKeyBeginHeaderMarker) - 1); |
| if (header_pos == std::string::npos) |
| return false; |
| size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; |
| size_t end_pos = working.rfind(kKeyBeginFooterMarker); |
| if (end_pos == std::string::npos) |
| return false; |
| if (start_pos >= end_pos) |
| return false; |
| |
| working = working.substr(start_pos, end_pos - start_pos); |
| if (working.length() == 0) |
| return false; |
| } |
| |
| return base::Base64Decode(working, output); |
| } |
| |
| // static |
| bool Extension::ProducePEM(const std::string& input, std::string* output) { |
| DCHECK(output); |
| if (input.empty()) |
| return false; |
| base::Base64Encode(input, output); |
| return true; |
| } |
| |
| // static |
| 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 |
| GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { |
| return GURL(std::string(extensions::kExtensionScheme) + |
| url::kStandardSchemeSeparator + extension_id + "/"); |
| } |
| |
| bool Extension::ShowConfigureContextMenus() const { |
| // Don't show context menu for component extensions. We might want to show |
| // options for component extension button but now there is no component |
| // extension with options. All other menu items like uninstall have |
| // no sense for component extensions. |
| return location() != Manifest::COMPONENT && |
| location() != Manifest::EXTERNAL_COMPONENT; |
| } |
| |
| bool Extension::OverlapsWithOrigin(const GURL& origin) const { |
| if (url() == origin) |
| return true; |
| |
| if (web_extent().is_empty()) |
| return false; |
| |
| // Note: patterns and extents ignore port numbers. |
| URLPattern origin_only_pattern(kValidWebExtentSchemes); |
| if (!origin_only_pattern.SetScheme(origin.scheme())) |
| return false; |
| origin_only_pattern.SetHost(origin.host()); |
| origin_only_pattern.SetPath("/*"); |
| |
| URLPatternSet origin_only_pattern_list; |
| origin_only_pattern_list.AddPattern(origin_only_pattern); |
| |
| return web_extent().OverlapsWith(origin_only_pattern_list); |
| } |
| |
| bool Extension::RequiresSortOrdinal() const { |
| return is_app() && (display_in_launcher_ || display_in_new_tab_page_); |
| } |
| |
| bool Extension::ShouldDisplayInAppLauncher() const { |
| // Only apps should be displayed in the launcher. |
| return is_app() && display_in_launcher_; |
| } |
| |
| bool Extension::ShouldDisplayInNewTabPage() const { |
| // Only apps should be displayed on the NTP. |
| return is_app() && display_in_new_tab_page_; |
| } |
| |
| bool Extension::ShouldDisplayInExtensionSettings() const { |
| // Don't show for themes since the settings UI isn't really useful for them. |
| if (is_theme()) |
| return false; |
| |
| // Don't show component extensions and invisible apps. |
| if (ShouldNotBeVisible()) |
| return false; |
| |
| // Always show unpacked extensions and apps. |
| if (Manifest::IsUnpackedLocation(location())) |
| return true; |
| |
| // Unless they are unpacked, never show hosted apps. Note: We intentionally |
| // show packaged apps and platform apps because there are some pieces of |
| // functionality that are only available in chrome://extensions/ but which |
| // are needed for packaged and platform apps. For example, inspecting |
| // background pages. See http://crbug.com/116134. |
| if (is_hosted_app()) |
| return false; |
| |
| return true; |
| } |
| |
| bool Extension::ShouldNotBeVisible() const { |
| // Don't show component extensions because they are only extensions as an |
| // implementation detail of Chrome. |
| if (extensions::Manifest::IsComponentLocation(location()) && |
| !CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kShowComponentExtensionOptions)) { |
| return true; |
| } |
| |
| // Always show unpacked extensions and apps. |
| if (Manifest::IsUnpackedLocation(location())) |
| return false; |
| |
| // Don't show apps that aren't visible in either launcher or ntp. |
| if (is_app() && !ShouldDisplayInAppLauncher() && !ShouldDisplayInNewTabPage()) |
| return true; |
| |
| return false; |
| } |
| |
| Extension::ManifestData* Extension::GetManifestData(const std::string& key) |
| const { |
| DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread()); |
| ManifestDataMap::const_iterator iter = manifest_data_.find(key); |
| if (iter != manifest_data_.end()) |
| return iter->second.get(); |
| return NULL; |
| } |
| |
| void Extension::SetManifestData(const std::string& key, |
| Extension::ManifestData* data) { |
| DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread()); |
| manifest_data_[key] = linked_ptr<ManifestData>(data); |
| } |
| |
| Manifest::Location Extension::location() const { |
| return manifest_->location(); |
| } |
| |
| const std::string& Extension::id() const { |
| return manifest_->extension_id(); |
| } |
| |
| const std::string Extension::VersionString() const { |
| return version()->GetString(); |
| } |
| |
| void Extension::AddInstallWarning(const InstallWarning& new_warning) { |
| install_warnings_.push_back(new_warning); |
| } |
| |
| void Extension::AddInstallWarnings( |
| const std::vector<InstallWarning>& new_warnings) { |
| install_warnings_.insert(install_warnings_.end(), |
| new_warnings.begin(), new_warnings.end()); |
| } |
| |
| bool Extension::is_app() const { |
| return manifest()->is_app(); |
| } |
| |
| bool Extension::is_platform_app() const { |
| return manifest()->is_platform_app(); |
| } |
| |
| bool Extension::is_hosted_app() const { |
| return manifest()->is_hosted_app(); |
| } |
| |
| bool Extension::is_legacy_packaged_app() const { |
| return manifest()->is_legacy_packaged_app(); |
| } |
| |
| bool Extension::is_extension() const { |
| return manifest()->is_extension(); |
| } |
| |
| bool Extension::is_shared_module() const { |
| return manifest()->is_shared_module(); |
| } |
| |
| bool Extension::is_theme() const { |
| return manifest()->is_theme(); |
| } |
| |
| bool Extension::can_be_incognito_enabled() const { |
| // Only component platform apps are supported in incognito. |
| return !is_platform_app() || location() == Manifest::COMPONENT; |
| } |
| |
| void Extension::AddWebExtentPattern(const URLPattern& pattern) { |
| // Bookmark apps are permissionless. |
| if (from_bookmark()) |
| return; |
| |
| extent_.AddPattern(pattern); |
| } |
| |
| // static |
| bool Extension::InitExtensionID(extensions::Manifest* manifest, |
| const base::FilePath& path, |
| const std::string& explicit_id, |
| int creation_flags, |
| base::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; |
| if (!manifest->GetString(keys::kPublicKey, &public_key) || |
| !ParsePEMKeyBytes(public_key, &public_key_bytes)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidKey); |
| return false; |
| } |
| std::string extension_id = id_util::GenerateId(public_key_bytes); |
| manifest->set_extension_id(extension_id); |
| return true; |
| } |
| |
| if (creation_flags & REQUIRE_KEY) { |
| *error = base::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 = id_util::GenerateIdForPath(path); |
| if (extension_id.empty()) { |
| NOTREACHED() << "Could not create ID from path."; |
| return false; |
| } |
| manifest->set_extension_id(extension_id); |
| return true; |
| } |
| } |
| |
| Extension::Extension(const base::FilePath& path, |
| scoped_ptr<extensions::Manifest> manifest) |
| : manifest_version_(0), |
| converted_from_user_script_(false), |
| manifest_(manifest.release()), |
| finished_parsing_manifest_(false), |
| display_in_launcher_(true), |
| display_in_new_tab_page_(true), |
| wants_file_access_(false), |
| creation_flags_(0) { |
| DCHECK(path.empty() || path.IsAbsolute()); |
| path_ = id_util::MaybeNormalizePath(path); |
| } |
| |
| Extension::~Extension() { |
| } |
| |
| bool Extension::InitFromValue(int flags, base::string16* error) { |
| DCHECK(error); |
| |
| creation_flags_ = flags; |
| |
| // Important to load manifest version first because many other features |
| // depend on its value. |
| if (!LoadManifestVersion(error)) |
| return false; |
| |
| if (!LoadRequiredFeatures(error)) |
| return false; |
| |
| // We don't need to validate because InitExtensionID already did that. |
| manifest_->GetString(keys::kPublicKey, &public_key_); |
| |
| extension_url_ = Extension::GetBaseURLFromExtensionId(id()); |
| |
| // 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; |
| |
| permissions_parser_.reset(new PermissionsParser()); |
| if (!permissions_parser_->Parse(this, error)) |
| return false; |
| |
| if (manifest_->HasKey(keys::kConvertedFromUserScript)) { |
| manifest_->GetBoolean(keys::kConvertedFromUserScript, |
| &converted_from_user_script_); |
| } |
| |
| if (!LoadSharedFeatures(error)) |
| return false; |
| |
| permissions_parser_->Finalize(this); |
| permissions_parser_.reset(); |
| |
| finished_parsing_manifest_ = true; |
| |
| permissions_data_.reset(new PermissionsData(this)); |
| |
| return true; |
| } |
| |
| bool Extension::LoadRequiredFeatures(base::string16* error) { |
| if (!LoadName(error) || |
| !LoadVersion(error)) |
| return false; |
| return true; |
| } |
| |
| bool Extension::LoadName(base::string16* error) { |
| base::string16 localized_name; |
| if (!manifest_->GetString(keys::kName, &localized_name)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidName); |
| return false; |
| } |
| non_localized_name_ = base::UTF16ToUTF8(localized_name); |
| base::i18n::AdjustStringForLocaleDirection(&localized_name); |
| name_ = base::UTF16ToUTF8(localized_name); |
| return true; |
| } |
| |
| bool Extension::LoadVersion(base::string16* error) { |
| std::string version_str; |
| if (!manifest_->GetString(keys::kVersion, &version_str)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidVersion); |
| return false; |
| } |
| version_.reset(new Version(version_str)); |
| if (!version_->IsValid() || version_->components().size() > 4) { |
| *error = base::ASCIIToUTF16(errors::kInvalidVersion); |
| return false; |
| } |
| return true; |
| } |
| |
| bool Extension::LoadAppFeatures(base::string16* error) { |
| if (!LoadExtent(keys::kWebURLs, &extent_, |
| errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) { |
| return false; |
| } |
| if (manifest_->HasKey(keys::kDisplayInLauncher) && |
| !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidDisplayInLauncher); |
| return false; |
| } |
| if (manifest_->HasKey(keys::kDisplayInNewTabPage)) { |
| if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage, |
| &display_in_new_tab_page_)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); |
| return false; |
| } |
| } else { |
| // Inherit default from display_in_launcher property. |
| display_in_new_tab_page_ = display_in_launcher_; |
| } |
| return true; |
| } |
| |
| bool Extension::LoadExtent(const char* key, |
| URLPatternSet* extent, |
| const char* list_error, |
| const char* value_error, |
| base::string16* error) { |
| const base::Value* temp_pattern_value = NULL; |
| if (!manifest_->Get(key, &temp_pattern_value)) |
| return true; |
| |
| const base::ListValue* pattern_list = NULL; |
| if (!temp_pattern_value->GetAsList(&pattern_list)) { |
| *error = base::ASCIIToUTF16(list_error); |
| return false; |
| } |
| |
| for (size_t i = 0; i < pattern_list->GetSize(); ++i) { |
| std::string pattern_string; |
| if (!pattern_list->GetString(i, &pattern_string)) { |
| *error = ErrorUtils::FormatErrorMessageUTF16(value_error, |
| base::UintToString(i), |
| errors::kExpectString); |
| return false; |
| } |
| |
| URLPattern pattern(kValidWebExtentSchemes); |
| URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); |
| if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { |
| pattern_string += "/"; |
| parse_result = pattern.Parse(pattern_string); |
| } |
| |
| if (parse_result != URLPattern::PARSE_SUCCESS) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| value_error, |
| base::UintToString(i), |
| URLPattern::GetParseResultString(parse_result)); |
| return false; |
| } |
| |
| // Do not allow authors to claim "<all_urls>". |
| if (pattern.match_all_urls()) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| value_error, |
| base::UintToString(i), |
| errors::kCannotClaimAllURLsInExtent); |
| return false; |
| } |
| |
| // Do not allow authors to claim "*" for host. |
| if (pattern.host().empty()) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| value_error, |
| base::UintToString(i), |
| errors::kCannotClaimAllHostsInExtent); |
| return false; |
| } |
| |
| // We do not allow authors to put wildcards in their paths. Instead, we |
| // imply one at the end. |
| if (pattern.path().find('*') != std::string::npos) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| value_error, |
| base::UintToString(i), |
| errors::kNoWildCardsInPaths); |
| return false; |
| } |
| pattern.SetPath(pattern.path() + '*'); |
| |
| extent->AddPattern(pattern); |
| } |
| |
| return true; |
| } |
| |
| bool Extension::LoadSharedFeatures(base::string16* error) { |
| if (!LoadDescription(error) || |
| !ManifestHandler::ParseExtension(this, error) || |
| !LoadShortName(error)) |
| return false; |
| |
| return true; |
| } |
| |
| bool Extension::LoadDescription(base::string16* error) { |
| if (manifest_->HasKey(keys::kDescription) && |
| !manifest_->GetString(keys::kDescription, &description_)) { |
| *error = base::ASCIIToUTF16(errors::kInvalidDescription); |
| return false; |
| } |
| return true; |
| } |
| |
| bool Extension::LoadManifestVersion(base::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 = base::ASCIIToUTF16(errors::kInvalidManifestVersion); |
| return false; |
| } |
| } |
| |
| manifest_version_ = manifest_->GetManifestVersion(); |
| if (manifest_version_ < kModernManifestVersion && |
| ((creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION && |
| !CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kAllowLegacyExtensionManifests)) || |
| GetType() == Manifest::TYPE_PLATFORM_APP)) { |
| *error = ErrorUtils::FormatErrorMessageUTF16( |
| errors::kInvalidManifestVersionOld, |
| base::IntToString(kModernManifestVersion), |
| is_platform_app() ? "apps" : "extensions"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool Extension::LoadShortName(base::string16* error) { |
| if (manifest_->HasKey(keys::kShortName)) { |
| base::string16 localized_short_name; |
| if (!manifest_->GetString(keys::kShortName, &localized_short_name) || |
| localized_short_name.empty()) { |
| *error = base::ASCIIToUTF16(errors::kInvalidShortName); |
| return false; |
| } |
| |
| base::i18n::AdjustStringForLocaleDirection(&localized_short_name); |
| short_name_ = base::UTF16ToUTF8(localized_short_name); |
| } else { |
| short_name_ = name_; |
| } |
| return true; |
| } |
| |
| ExtensionInfo::ExtensionInfo(const base::DictionaryValue* manifest, |
| const std::string& id, |
| const base::FilePath& path, |
| Manifest::Location location) |
| : extension_id(id), |
| extension_path(path), |
| extension_location(location) { |
| if (manifest) |
| extension_manifest.reset(manifest->DeepCopy()); |
| } |
| |
| ExtensionInfo::~ExtensionInfo() {} |
| |
| InstalledExtensionInfo::InstalledExtensionInfo( |
| const Extension* extension, |
| bool is_update, |
| bool from_ephemeral, |
| const std::string& old_name) |
| : extension(extension), |
| is_update(is_update), |
| from_ephemeral(from_ephemeral), |
| old_name(old_name) {} |
| |
| UnloadedExtensionInfo::UnloadedExtensionInfo( |
| const Extension* extension, |
| UnloadedExtensionInfo::Reason reason) |
| : reason(reason), |
| extension(extension) {} |
| |
| UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo( |
| const Extension* extension, |
| const PermissionSet* permissions, |
| Reason reason) |
| : reason(reason), |
| extension(extension), |
| permissions(permissions) {} |
| |
| } // namespace extensions |