blob: 19dfa2ac889f0f46d8daf1508cbbaee9aa787524 [file] [log] [blame]
// Copyright 2013 The Chromium Authors
// 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 <stddef.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <string>
#include <string_view>
#include <utility>
#include "base/base64.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/files/file_path.h"
#include "base/i18n/rtl.h"
#include "base/json/json_writer.h"
#include "base/memory/singleton.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/timer/elapsed_timer.h"
#include "base/version.h"
#include "components/crx_file/id_util.h"
#include "content/public/common/url_constants.h"
#include "extensions/common/constants.h"
#include "extensions/common/error_utils.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/manifest.h"
#include "extensions/common/manifest_constants.h"
#include "extensions/common/manifest_handler.h"
#include "extensions/common/manifest_handlers/incognito_info.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"
using extensions::mojom::ManifestLocation;
namespace extensions {
namespace keys = manifest_keys;
namespace values = manifest_values;
namespace errors = manifest_errors;
namespace {
constexpr int kMinimumSupportedManifestVersion = 2;
constexpr int kMaximumSupportedManifestVersion = 3;
constexpr int kPEMOutputColumns = 64;
static_assert(kMaximumSupportedManifestVersion >=
kMinimumSupportedManifestVersion,
"The modern manifest version must be supported.");
bool g_silence_deprecated_manifest_version_warnings = false;
// KEY MARKERS
constexpr char kKeyBeginHeaderMarker[] = "-----BEGIN";
constexpr char kKeyBeginFooterMarker[] = "-----END";
constexpr char kKeyInfoEndMarker[] = "KEY-----";
constexpr char kPublic[] = "PUBLIC";
constexpr 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 (base::Contains(path.value(), '\\')) {
return true;
}
return !net::IsSafePortableRelativePath(path);
}
// Returns true if the given |manifest_version| is supported for the specified
// |type| of extension. Optionally populates |warning| if an InstallWarning
// should be added.
bool IsManifestSupported(int manifest_version,
Manifest::Type type,
ManifestLocation location,
int creation_flags,
std::string* warning) {
// Supported versions are always safe.
if (manifest_version >= kMinimumSupportedManifestVersion &&
manifest_version <= kMaximumSupportedManifestVersion) {
// Emit a warning for unpacked extensions on Manifest V2 warning that
// MV2 is deprecated.
if (type == Manifest::TYPE_EXTENSION && manifest_version == 2 &&
Manifest::IsUnpackedLocation(location) &&
!g_silence_deprecated_manifest_version_warnings) {
*warning = errors::kManifestV2IsDeprecatedWarning;
}
return true;
}
if (manifest_version > kMaximumSupportedManifestVersion) {
// Silence future manifest error with flag.
bool allow_future_manifest_version =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAllowFutureManifestVersion);
if (!allow_future_manifest_version) {
*warning = ErrorUtils::FormatErrorMessage(
manifest_errors::kManifestVersionTooHighWarning,
base::NumberToString(kMaximumSupportedManifestVersion),
base::NumberToString(manifest_version));
}
return true;
}
// Allow an exception for extensions if a special commandline flag is present.
// Note: This allows the extension to load, but it may effectively be treated
// as a higher manifest version. For instance, all extension v1-specific
// handling has been removed, which means they will effectively be treated as
// v2s.
bool allow_legacy_extensions =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kAllowLegacyExtensionManifests);
if (type == Manifest::TYPE_EXTENSION && allow_legacy_extensions)
return true;
if ((creation_flags & Extension::REQUIRE_MODERN_MANIFEST_VERSION) != 0)
return false;
static constexpr int kMinimumExtensionManifestVersion = 2;
if (type == Manifest::TYPE_EXTENSION)
return manifest_version >= kMinimumExtensionManifestVersion;
static constexpr int kMinimumPlatformAppManifestVersion = 2;
if (type == Manifest::TYPE_PLATFORM_APP)
return manifest_version >= kMinimumPlatformAppManifestVersion;
return true;
}
// Computes the |extension_id| from the given parameters. On success, returns
// true. On failure, populates |error| and returns false.
bool ComputeExtensionID(const base::Value::Dict& manifest,
const base::FilePath& path,
int creation_flags,
std::u16string* error,
ExtensionId* extension_id) {
if (const base::Value* public_key = manifest.Find(keys::kPublicKey)) {
std::string public_key_bytes;
if (!public_key->is_string() ||
!Extension::ParsePEMKeyBytes(public_key->GetString(),
&public_key_bytes)) {
*error = errors::kInvalidKey;
return false;
}
*extension_id = crx_file::id_util::GenerateId(public_key_bytes);
return true;
}
if (creation_flags & Extension::REQUIRE_KEY) {
*error = errors::kInvalidKey;
return false;
}
// 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.
*extension_id = crx_file::id_util::GenerateIdForPath(path);
if (extension_id->empty()) {
NOTREACHED() << "Could not create ID from path.";
return false;
}
return true;
}
std::u16string InvalidManifestVersionError(const char* manifest_version_error,
bool is_platform_app) {
std::string valid_version;
if (kMinimumSupportedManifestVersion == kMaximumSupportedManifestVersion) {
valid_version = base::NumberToString(kMinimumSupportedManifestVersion);
} else if (kMaximumSupportedManifestVersion -
kMinimumSupportedManifestVersion ==
1) {
valid_version = base::StrCat(
{"either ", base::NumberToString(kMinimumSupportedManifestVersion),
" or ", base::NumberToString(kMaximumSupportedManifestVersion)});
} else {
valid_version = base::StrCat(
{"between ", base::NumberToString(kMinimumSupportedManifestVersion),
" and ", base::NumberToString(kMaximumSupportedManifestVersion)});
}
return ErrorUtils::FormatErrorMessageUTF16(
manifest_version_error, valid_version,
is_platform_app ? "apps" : "extensions");
}
} // namespace
const int Extension::kInitFromValueFlagBits = 15;
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 | URLPattern::SCHEME_WS | URLPattern::SCHEME_WSS |
URLPattern::SCHEME_UUID_IN_PACKAGE;
//
// Extension
//
// static
void Extension::set_silence_deprecated_manifest_version_warnings_for_testing(
bool silence) {
g_silence_deprecated_manifest_version_warnings = silence;
}
// static
scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
ManifestLocation location,
const base::Value::Dict& 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 std::u16string. See http://crbug.com/71980.
scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
ManifestLocation location,
const base::Value::Dict& value,
int flags,
const ExtensionId& explicit_id,
std::string* utf8_error) {
base::ElapsedTimer timer;
DCHECK(utf8_error);
std::u16string error;
ExtensionId extension_id;
if (!explicit_id.empty()) {
extension_id = explicit_id;
} else if (!ComputeExtensionID(value, path, flags, &error, &extension_id)) {
*utf8_error = base::UTF16ToUTF8(error);
return nullptr;
}
std::unique_ptr<extensions::Manifest> manifest;
if (flags & FOR_LOGIN_SCREEN) {
manifest = Manifest::CreateManifestForLoginScreen(location, value.Clone(),
std::move(extension_id));
} else {
manifest = std::make_unique<Manifest>(location, value.Clone(),
std::move(extension_id));
}
std::vector<InstallWarning> install_warnings;
manifest->ValidateManifest(&install_warnings);
scoped_refptr<Extension> extension = new Extension(path, std::move(manifest));
extension->install_warnings_.swap(install_warnings);
if (!extension->InitFromValue(flags, &error)) {
*utf8_error = base::UTF16ToUTF8(error);
return nullptr;
}
extension->guid_ = base::Uuid::GenerateRandomV4();
extension->dynamic_url_ = Extension::GetBaseURLFromExtensionId(
extension->guid_.AsLowercaseString());
return extension;
}
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(kExtensionScheme));
return extension_url.Resolve(relative_path);
}
bool Extension::ResourceMatches(const URLPatternSet& pattern_set,
const std::string& resource) const {
return pattern_set.MatchesURL(extension_url_.Resolve(resource));
}
ExtensionResource Extension::GetResource(std::string_view relative_path) const {
// We have some legacy data where resources have leading slashes.
// See: http://crbug.com/121164
if (!relative_path.empty() && relative_path[0] == '/')
relative_path.remove_prefix(1);
base::FilePath relative_file_path =
base::FilePath::FromUTF8Unsafe(relative_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 (base::StartsWith(working, kKeyBeginHeaderMarker,
base::CompareCase::SENSITIVE)) {
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;
*output = base::Base64Encode(input);
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 ExtensionId& extension_id) {
return GURL(base::StrCat({extensions::kExtensionScheme,
url::kStandardSchemeSeparator, extension_id}));
}
// static
url::Origin Extension::CreateOriginFromExtensionId(
const ExtensionId& extension_id) {
// TODO(lukasza): Avoid url::Origin::Create calls in general. Sadly the call
// below cannot be replaced with CreateFromNormalizedTuple, because it DCHECKs
// that the `extension_id` is not a properly canonicalized hostname.
return url::Origin::Create(GetBaseURLFromExtensionId(extension_id));
}
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);
}
Extension::ManifestData* Extension::GetManifestData(const std::string& key)
const {
DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread());
auto iter = manifest_data_.find(key);
if (iter != manifest_data_.end())
return iter->second.get();
return nullptr;
}
void Extension::SetManifestData(const std::string& key,
std::unique_ptr<Extension::ManifestData> data) {
DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread());
manifest_data_[key] = std::move(data);
}
void Extension::SetGUID(const ExtensionGuid& guid) {
guid_ = base::Uuid::ParseLowercase(guid);
DCHECK(guid_.is_valid());
dynamic_url_ =
Extension::GetBaseURLFromExtensionId(guid_.AsLowercaseString());
}
const ExtensionGuid& Extension::guid() const {
DCHECK(guid_.is_valid());
return guid_.AsLowercaseString();
}
ManifestLocation Extension::location() const {
return manifest_->location();
}
const ExtensionId& Extension::id() const {
return manifest_->extension_id();
}
const HashedExtensionId& Extension::hashed_id() const {
return manifest_->hashed_id();
}
std::string Extension::VersionString() const {
return version_.GetString();
}
std::string Extension::DifferentialFingerprint() const {
// We currently support two sources of differential fingerprints:
// server-provided and synthesized. Fingerprints are of the format V.FP, where
// V indicates the fingerprint type (1 for SHA256 hash, 2 for app version) and
// FP indicates the value. The hash-based FP from the server is more precise
// (a hash of the extension CRX), so use that when available, otherwise
// synthesize a 2.VERSION fingerprint for use. For more information, see
// https://github.com/google/omaha/blob/master/doc/ServerProtocolV3.md#packages--fingerprints
if (const std::string* fingerprint =
manifest_->FindStringPath(keys::kDifferentialFingerprint))
return *fingerprint;
return "2." + VersionString();
}
std::string Extension::GetVersionForDisplay() const {
if (version_name_.size() > 0)
return version_name_;
return VersionString();
}
void Extension::AddInstallWarning(InstallWarning new_warning) {
install_warnings_.push_back(std::move(new_warning));
}
void Extension::AddInstallWarnings(std::vector<InstallWarning> new_warnings) {
install_warnings_.insert(install_warnings_.end(),
std::make_move_iterator(new_warnings.begin()),
std::make_move_iterator(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::is_login_screen_extension() const {
return manifest()->is_login_screen_extension();
}
bool Extension::is_chromeos_system_extension() const {
return manifest()->is_chromeos_system_extension();
}
void Extension::AddWebExtentPattern(const URLPattern& pattern) {
extent_.AddPattern(pattern);
}
Extension::Extension(const base::FilePath& path,
std::unique_ptr<extensions::Manifest> manifest)
: manifest_version_(0),
converted_from_user_script_(false),
manifest_(manifest.release()),
finished_parsing_manifest_(false),
wants_file_access_(false),
creation_flags_(0) {
DCHECK(path.empty() || path.IsAbsolute());
path_ = crx_file::id_util::MaybeNormalizePath(path);
}
Extension::~Extension() {
}
bool Extension::InitFromValue(int flags, std::u16string* error) {
DCHECK(error);
creation_flags_ = flags;
// Check for |converted_from_user_script| first, since it affects the type
// returned by GetType(). This is needed to determine if the manifest version
// is valid.
converted_from_user_script_ =
manifest_->FindBoolPath(keys::kConvertedFromUserScript).value_or(false);
// Important to load manifest version first because many other features
// depend on its value.
if (!LoadManifestVersion(error))
return false;
if (!LoadRequiredFeatures(error))
return false;
if (const std::string* temp = manifest()->FindStringPath(keys::kPublicKey)) {
// We don't need to validate because ComputeExtensionId() already did that.
public_key_ = *temp;
}
extension_origin_ = Extension::CreateOriginFromExtensionId(id());
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_ = std::make_unique<PermissionsParser>();
if (!permissions_parser_->Parse(this, error))
return false;
if (!LoadSharedFeatures(error))
return false;
permissions_parser_->Finalize(this);
permissions_parser_.reset();
finished_parsing_manifest_ = true;
permissions_data_ = std::make_unique<PermissionsData>(
id(), GetType(), location(),
PermissionsParser::GetRequiredPermissions(this).Clone());
return true;
}
bool Extension::LoadRequiredFeatures(std::u16string* error) {
if (!LoadName(error) ||
!LoadVersion(error))
return false;
return true;
}
bool Extension::LoadName(std::u16string* error) {
const std::string* non_localized_name_ptr =
manifest_->FindStringPath(keys::kName);
if (non_localized_name_ptr == nullptr || *non_localized_name_ptr == "") {
*error = errors::kInvalidName16;
return false;
}
non_localized_name_ = *non_localized_name_ptr;
std::u16string sanitized_name =
base::CollapseWhitespace(base::UTF8ToUTF16(non_localized_name_), true);
base::i18n::SanitizeUserSuppliedString(&sanitized_name);
display_name_ = base::UTF16ToUTF8(sanitized_name);
return true;
}
bool Extension::LoadVersion(std::u16string* error) {
const std::string* version_str = manifest_->FindStringPath(keys::kVersion);
if (version_str == nullptr) {
*error = errors::kInvalidVersion;
return false;
}
version_ = base::Version(*version_str);
if (!version_.IsValid() || version_.components().size() > 4) {
*error = errors::kInvalidVersion;
return false;
}
if (const base::Value* temp = manifest_->FindKey(keys::kVersionName)) {
if (!temp->is_string()) {
*error = errors::kInvalidVersionName;
return false;
}
version_name_ = temp->GetString();
}
return true;
}
bool Extension::LoadAppFeatures(std::u16string* error) {
if (!LoadExtent(keys::kWebURLs, &extent_,
errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) {
return false;
}
return true;
}
bool Extension::LoadExtent(const char* key,
URLPatternSet* extent,
const char* list_error,
const char* value_error,
std::u16string* error) {
const base::Value* temp_pattern_value = manifest_->FindPath(key);
if (temp_pattern_value == nullptr)
return true;
if (!temp_pattern_value->is_list()) {
*error = base::ASCIIToUTF16(list_error);
return false;
}
const base::Value::List& pattern_list = temp_pattern_value->GetList();
for (size_t i = 0; i < pattern_list.size(); ++i) {
std::string pattern_string;
if (pattern_list[i].is_string()) {
pattern_string = pattern_list[i].GetString();
} else {
*error = ErrorUtils::FormatErrorMessageUTF16(
value_error, base::NumberToString(i), errors::kExpectString);
return false;
}
URLPattern pattern(kValidWebExtentSchemes);
URLPattern::ParseResult parse_result = pattern.Parse(pattern_string);
if (parse_result == URLPattern::ParseResult::kEmptyPath) {
pattern_string += "/";
parse_result = pattern.Parse(pattern_string);
}
if (parse_result != URLPattern::ParseResult::kSuccess) {
*error = ErrorUtils::FormatErrorMessageUTF16(
value_error, base::NumberToString(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::NumberToString(i),
errors::kCannotClaimAllURLsInExtent);
return false;
}
// Do not allow authors to claim "*" for host.
if (pattern.host().empty()) {
*error = ErrorUtils::FormatErrorMessageUTF16(
value_error, base::NumberToString(i),
errors::kCannotClaimAllHostsInExtent);
return false;
}
// We do not allow authors to put wildcards in their paths. Instead, we
// imply one at the end.
if (base::Contains(pattern.path(), '*')) {
*error = ErrorUtils::FormatErrorMessageUTF16(
value_error, base::NumberToString(i), errors::kNoWildCardsInPaths);
return false;
}
pattern.SetPath(pattern.path() + '*');
extent->AddPattern(pattern);
}
return true;
}
bool Extension::LoadSharedFeatures(std::u16string* error) {
if (!LoadDescription(error) ||
!ManifestHandler::ParseExtension(this, error) ||
!LoadShortName(error))
return false;
return true;
}
bool Extension::LoadDescription(std::u16string* error) {
if (const base::Value* temp = manifest_->FindKey(keys::kDescription)) {
if (!temp->is_string()) {
*error = errors::kInvalidDescription;
return false;
}
description_ = temp->GetString();
}
return true;
}
bool Extension::LoadManifestVersion(std::u16string* error) {
// Get the original value out of the dictionary so that we can validate it
// more strictly.
bool key_exists = false;
if (const base::Value* version_value =
manifest_->available_values().Find(keys::kManifestVersion)) {
if (!version_value->is_int()) {
*error = InvalidManifestVersionError(
errors::kInvalidManifestVersionUnsupported, is_platform_app());
return false;
}
key_exists = true;
}
manifest_version_ = manifest_->manifest_version();
std::string warning;
if (!IsManifestSupported(manifest_version_, GetType(), location(),
creation_flags_, &warning)) {
std::string json;
base::JSONWriter::Write(*manifest_->value(), &json);
LOG(WARNING) << "Failed to load extension. Manifest JSON: " << json;
*error = InvalidManifestVersionError(
key_exists ? errors::kInvalidManifestVersionUnsupported
: errors::kInvalidManifestVersionMissingKey,
is_platform_app());
return false;
}
if (!warning.empty())
AddInstallWarning(InstallWarning(warning, keys::kManifestVersion));
return true;
}
bool Extension::LoadShortName(std::u16string* error) {
if (const base::Value* temp = manifest_->FindKey(keys::kShortName)) {
const std::string* localized_short_name_utf8 = temp->GetIfString();
if (!localized_short_name_utf8 || localized_short_name_utf8->empty()) {
*error = errors::kInvalidShortName;
return false;
}
std::u16string localized_short_name =
base::UTF8ToUTF16(*localized_short_name_utf8);
base::i18n::AdjustStringForLocaleDirection(&localized_short_name);
short_name_ = base::UTF16ToUTF8(localized_short_name);
} else {
short_name_ = display_name_;
}
return true;
}
ExtensionInfo::ExtensionInfo(const base::Value::Dict* manifest,
const ExtensionId& id,
const base::FilePath& path,
ManifestLocation location)
: extension_id(id), extension_path(path), extension_location(location) {
if (manifest) {
extension_manifest = std::make_unique<base::Value::Dict>(manifest->Clone());
}
}
ExtensionInfo::ExtensionInfo(ExtensionInfo&&) noexcept = default;
ExtensionInfo& ExtensionInfo::operator=(ExtensionInfo&&) = default;
ExtensionInfo::~ExtensionInfo() = default;
} // namespace extensions