summaryrefslogtreecommitdiffstats
path: root/extensions/common/extension.cc
diff options
context:
space:
mode:
authoryoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-15 23:07:41 +0000
committeryoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-15 23:07:41 +0000
commite4452d31c6aadb6e9c86836e8b45c0c151223826 (patch)
tree465eb8100a1d86ab889cd9f3f709df1703597a81 /extensions/common/extension.cc
parent741c3917b61418986e76534c2bc68394f6f23d29 (diff)
downloadchromium_src-e4452d31c6aadb6e9c86836e8b45c0c151223826.zip
chromium_src-e4452d31c6aadb6e9c86836e8b45c0c151223826.tar.gz
chromium_src-e4452d31c6aadb6e9c86836e8b45c0c151223826.tar.bz2
Move Extension and PermissionsData to extensions/common.
BUG=298586 R=cdn@chromium.org, miket@chromium.org, sky@chromium.org TBR=cdn@chromium.org Review URL: https://codereview.chromium.org/15239002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235462 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/common/extension.cc')
-rw-r--r--extensions/common/extension.cc792
1 files changed, 792 insertions, 0 deletions
diff --git a/extensions/common/extension.cc b/extensions/common/extension.cc
new file mode 100644
index 0000000..f38d547
--- /dev/null
+++ b/extensions/common/extension.cc
@@ -0,0 +1,792 @@
+// 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/permissions/api_permission_set.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_set.h"
+#include "grit/chromium_strings.h"
+#include "grit/theme_resources.h"
+#include "net/base/net_util.h"
+#include "url/url_util.h"
+
+#if defined(OS_WIN)
+#include "grit/generated_resources.h"
+#endif
+
+namespace extensions {
+
+namespace keys = manifest_keys;
+namespace values = manifest_values;
+namespace errors = manifest_errors;
+
+namespace {
+
+const int kModernManifestVersion = 2;
+const int kPEMOutputColumns = 65;
+
+// 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 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 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);
+ 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 = 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 = 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 = 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);
+ return (input.length() == 0) ? false : base::Base64Encode(input, output);
+}
+
+// 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) +
+ content::kStandardSchemeSeparator + extension_id + "/");
+}
+
+bool Extension::HasAPIPermission(APIPermission::ID permission) const {
+ return PermissionsData::HasAPIPermission(this, permission);
+}
+
+bool Extension::HasAPIPermission(const std::string& permission_name) const {
+ return PermissionsData::HasAPIPermission(this, permission_name);
+}
+
+scoped_refptr<const PermissionSet> Extension::GetActivePermissions() const {
+ return PermissionsData::GetActivePermissions(this);
+}
+
+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;
+}
+
+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 (location() == Manifest::COMPONENT &&
+ !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::can_be_incognito_enabled() const {
+ // Only component platform apps are supported in incognito.
+ return !is_platform_app() || location() == Manifest::COMPONENT;
+}
+
+bool Extension::force_incognito_enabled() const {
+ return PermissionsData::HasAPIPermission(this, APIPermission::kProxy);
+}
+
+void Extension::AddWebExtentPattern(const URLPattern& pattern) {
+ extent_.AddPattern(pattern);
+}
+
+bool Extension::is_theme() const {
+ return manifest()->is_theme();
+}
+
+// static
+bool Extension::InitExtensionID(extensions::Manifest* manifest,
+ const base::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;
+ if (!manifest->GetString(keys::kPublicKey, &public_key) ||
+ !ParsePEMKeyBytes(public_key, &public_key_bytes)) {
+ *error = 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 = 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, 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_data_.reset(new PermissionsData);
+ if (!permissions_data_->ParsePermissions(this, error))
+ return false;
+
+ if (manifest_->HasKey(keys::kConvertedFromUserScript)) {
+ manifest_->GetBoolean(keys::kConvertedFromUserScript,
+ &converted_from_user_script_);
+ }
+
+ if (!LoadSharedFeatures(error))
+ return false;
+
+ finished_parsing_manifest_ = true;
+
+ permissions_data_->InitializeManifestPermissions(this);
+ permissions_data_->FinalizePermissions(this);
+
+ return true;
+}
+
+bool Extension::LoadRequiredFeatures(string16* error) {
+ if (!LoadName(error) ||
+ !LoadVersion(error))
+ return false;
+ return true;
+}
+
+bool Extension::LoadName(string16* error) {
+ string16 localized_name;
+ if (!manifest_->GetString(keys::kName, &localized_name)) {
+ *error = ASCIIToUTF16(errors::kInvalidName);
+ return false;
+ }
+ non_localized_name_ = UTF16ToUTF8(localized_name);
+ base::i18n::AdjustStringForLocaleDirection(&localized_name);
+ name_ = UTF16ToUTF8(localized_name);
+ 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(new Version(version_str));
+ if (!version_->IsValid() || version_->components().size() > 4) {
+ *error = ASCIIToUTF16(errors::kInvalidVersion);
+ return false;
+ }
+ return true;
+}
+
+bool Extension::LoadAppFeatures(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 = ASCIIToUTF16(errors::kInvalidDisplayInLauncher);
+ return false;
+ }
+ if (manifest_->HasKey(keys::kDisplayInNewTabPage)) {
+ if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage,
+ &display_in_new_tab_page_)) {
+ *error = 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,
+ 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 = 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(string16* error) {
+ if (!LoadDescription(error) ||
+ !ManifestHandler::ParseExtension(this, error) ||
+ !LoadShortName(error))
+ return false;
+
+ 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::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;
+ }
+ }
+
+ 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(string16* error) {
+ if (manifest_->HasKey(keys::kShortName)) {
+ string16 localized_short_name;
+ if (!manifest_->GetString(keys::kShortName, &localized_short_name) ||
+ localized_short_name.empty()) {
+ *error = ASCIIToUTF16(errors::kInvalidShortName);
+ return false;
+ }
+
+ base::i18n::AdjustStringForLocaleDirection(&localized_short_name);
+ short_name_ = 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,
+ const std::string& old_name)
+ : extension(extension),
+ is_update(is_update),
+ 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