Home | History | Annotate | Download | only in common
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "extensions/common/extension.h"
      6 
      7 #include "base/base64.h"
      8 #include "base/basictypes.h"
      9 #include "base/command_line.h"
     10 #include "base/files/file_path.h"
     11 #include "base/i18n/rtl.h"
     12 #include "base/logging.h"
     13 #include "base/memory/singleton.h"
     14 #include "base/stl_util.h"
     15 #include "base/strings/string16.h"
     16 #include "base/strings/string_number_conversions.h"
     17 #include "base/strings/string_piece.h"
     18 #include "base/strings/string_util.h"
     19 #include "base/strings/stringprintf.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "base/values.h"
     22 #include "base/version.h"
     23 #include "content/public/common/url_constants.h"
     24 #include "extensions/common/constants.h"
     25 #include "extensions/common/error_utils.h"
     26 #include "extensions/common/id_util.h"
     27 #include "extensions/common/manifest.h"
     28 #include "extensions/common/manifest_constants.h"
     29 #include "extensions/common/manifest_handler.h"
     30 #include "extensions/common/manifest_handlers/permissions_parser.h"
     31 #include "extensions/common/permissions/permission_set.h"
     32 #include "extensions/common/permissions/permissions_data.h"
     33 #include "extensions/common/permissions/permissions_info.h"
     34 #include "extensions/common/switches.h"
     35 #include "extensions/common/url_pattern.h"
     36 #include "net/base/filename_util.h"
     37 #include "url/url_util.h"
     38 
     39 namespace extensions {
     40 
     41 namespace keys = manifest_keys;
     42 namespace values = manifest_values;
     43 namespace errors = manifest_errors;
     44 
     45 namespace {
     46 
     47 const int kModernManifestVersion = 2;
     48 const int kPEMOutputColumns = 64;
     49 
     50 // KEY MARKERS
     51 const char kKeyBeginHeaderMarker[] = "-----BEGIN";
     52 const char kKeyBeginFooterMarker[] = "-----END";
     53 const char kKeyInfoEndMarker[] = "KEY-----";
     54 const char kPublic[] = "PUBLIC";
     55 const char kPrivate[] = "PRIVATE";
     56 
     57 bool ContainsReservedCharacters(const base::FilePath& path) {
     58   // We should disallow backslash '\\' as file path separator even on Windows,
     59   // because the backslash is not regarded as file path separator on Linux/Mac.
     60   // Extensions are cross-platform.
     61   // Since FilePath uses backslash '\\' as file path separator on Windows, so we
     62   // need to check manually.
     63   if (path.value().find('\\') != path.value().npos)
     64     return true;
     65   return !net::IsSafePortableRelativePath(path);
     66 }
     67 
     68 }  // namespace
     69 
     70 const int Extension::kInitFromValueFlagBits = 11;
     71 
     72 const char Extension::kMimeType[] = "application/x-chrome-extension";
     73 
     74 const int Extension::kValidWebExtentSchemes =
     75     URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
     76 
     77 const int Extension::kValidHostPermissionSchemes = URLPattern::SCHEME_CHROMEUI |
     78                                                    URLPattern::SCHEME_HTTP |
     79                                                    URLPattern::SCHEME_HTTPS |
     80                                                    URLPattern::SCHEME_FILE |
     81                                                    URLPattern::SCHEME_FTP;
     82 
     83 //
     84 // Extension
     85 //
     86 
     87 // static
     88 scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
     89                                            Manifest::Location location,
     90                                            const base::DictionaryValue& value,
     91                                            int flags,
     92                                            std::string* utf8_error) {
     93   return Extension::Create(path,
     94                            location,
     95                            value,
     96                            flags,
     97                            std::string(),  // ID is ignored if empty.
     98                            utf8_error);
     99 }
    100 
    101 // TODO(sungguk): Continue removing std::string errors and replacing
    102 // with base::string16. See http://crbug.com/71980.
    103 scoped_refptr<Extension> Extension::Create(const base::FilePath& path,
    104                                            Manifest::Location location,
    105                                            const base::DictionaryValue& value,
    106                                            int flags,
    107                                            const std::string& explicit_id,
    108                                            std::string* utf8_error) {
    109   DCHECK(utf8_error);
    110   base::string16 error;
    111   scoped_ptr<extensions::Manifest> manifest(
    112       new extensions::Manifest(
    113           location, scoped_ptr<base::DictionaryValue>(value.DeepCopy())));
    114 
    115   if (!InitExtensionID(manifest.get(), path, explicit_id, flags, &error)) {
    116     *utf8_error = base::UTF16ToUTF8(error);
    117     return NULL;
    118   }
    119 
    120   std::vector<InstallWarning> install_warnings;
    121   if (!manifest->ValidateManifest(utf8_error, &install_warnings)) {
    122     return NULL;
    123   }
    124 
    125   scoped_refptr<Extension> extension = new Extension(path, manifest.Pass());
    126   extension->install_warnings_.swap(install_warnings);
    127 
    128   if (!extension->InitFromValue(flags, &error)) {
    129     *utf8_error = base::UTF16ToUTF8(error);
    130     return NULL;
    131   }
    132 
    133   return extension;
    134 }
    135 
    136 // static
    137 bool Extension::IdIsValid(const std::string& id) {
    138   // Verify that the id is legal.
    139   if (id.size() != (id_util::kIdSize * 2))
    140     return false;
    141 
    142   // We only support lowercase IDs, because IDs can be used as URL components
    143   // (where GURL will lowercase it).
    144   std::string temp = StringToLowerASCII(id);
    145   for (size_t i = 0; i < temp.size(); i++)
    146     if (temp[i] < 'a' || temp[i] > 'p')
    147       return false;
    148 
    149   return true;
    150 }
    151 
    152 Manifest::Type Extension::GetType() const {
    153   return converted_from_user_script() ?
    154       Manifest::TYPE_USER_SCRIPT : manifest_->type();
    155 }
    156 
    157 // static
    158 GURL Extension::GetResourceURL(const GURL& extension_url,
    159                                const std::string& relative_path) {
    160   DCHECK(extension_url.SchemeIs(extensions::kExtensionScheme));
    161   DCHECK_EQ("/", extension_url.path());
    162 
    163   std::string path = relative_path;
    164 
    165   // If the relative path starts with "/", it is "absolute" relative to the
    166   // extension base directory, but extension_url is already specified to refer
    167   // to that base directory, so strip the leading "/" if present.
    168   if (relative_path.size() > 0 && relative_path[0] == '/')
    169     path = relative_path.substr(1);
    170 
    171   GURL ret_val = GURL(extension_url.spec() + path);
    172   DCHECK(StartsWithASCII(ret_val.spec(), extension_url.spec(), false));
    173 
    174   return ret_val;
    175 }
    176 
    177 bool Extension::ResourceMatches(const URLPatternSet& pattern_set,
    178                                 const std::string& resource) const {
    179   return pattern_set.MatchesURL(extension_url_.Resolve(resource));
    180 }
    181 
    182 ExtensionResource Extension::GetResource(
    183     const std::string& relative_path) const {
    184   std::string new_path = relative_path;
    185   // We have some legacy data where resources have leading slashes.
    186   // See: http://crbug.com/121164
    187   if (!new_path.empty() && new_path.at(0) == '/')
    188     new_path.erase(0, 1);
    189   base::FilePath relative_file_path = base::FilePath::FromUTF8Unsafe(new_path);
    190   if (ContainsReservedCharacters(relative_file_path))
    191     return ExtensionResource();
    192   ExtensionResource r(id(), path(), relative_file_path);
    193   if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
    194     r.set_follow_symlinks_anywhere();
    195   }
    196   return r;
    197 }
    198 
    199 ExtensionResource Extension::GetResource(
    200     const base::FilePath& relative_file_path) const {
    201   if (ContainsReservedCharacters(relative_file_path))
    202     return ExtensionResource();
    203   ExtensionResource r(id(), path(), relative_file_path);
    204   if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
    205     r.set_follow_symlinks_anywhere();
    206   }
    207   return r;
    208 }
    209 
    210 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a
    211 // util class in base:
    212 // http://code.google.com/p/chromium/issues/detail?id=13572
    213 // static
    214 bool Extension::ParsePEMKeyBytes(const std::string& input,
    215                                  std::string* output) {
    216   DCHECK(output);
    217   if (!output)
    218     return false;
    219   if (input.length() == 0)
    220     return false;
    221 
    222   std::string working = input;
    223   if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) {
    224     working = base::CollapseWhitespaceASCII(working, true);
    225     size_t header_pos = working.find(kKeyInfoEndMarker,
    226       sizeof(kKeyBeginHeaderMarker) - 1);
    227     if (header_pos == std::string::npos)
    228       return false;
    229     size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1;
    230     size_t end_pos = working.rfind(kKeyBeginFooterMarker);
    231     if (end_pos == std::string::npos)
    232       return false;
    233     if (start_pos >= end_pos)
    234       return false;
    235 
    236     working = working.substr(start_pos, end_pos - start_pos);
    237     if (working.length() == 0)
    238       return false;
    239   }
    240 
    241   return base::Base64Decode(working, output);
    242 }
    243 
    244 // static
    245 bool Extension::ProducePEM(const std::string& input, std::string* output) {
    246   DCHECK(output);
    247   if (input.empty())
    248     return false;
    249   base::Base64Encode(input, output);
    250   return true;
    251 }
    252 
    253 // static
    254 bool Extension::FormatPEMForFileOutput(const std::string& input,
    255                                        std::string* output,
    256                                        bool is_public) {
    257   DCHECK(output);
    258   if (input.length() == 0)
    259     return false;
    260   *output = "";
    261   output->append(kKeyBeginHeaderMarker);
    262   output->append(" ");
    263   output->append(is_public ? kPublic : kPrivate);
    264   output->append(" ");
    265   output->append(kKeyInfoEndMarker);
    266   output->append("\n");
    267   for (size_t i = 0; i < input.length(); ) {
    268     int slice = std::min<int>(input.length() - i, kPEMOutputColumns);
    269     output->append(input.substr(i, slice));
    270     output->append("\n");
    271     i += slice;
    272   }
    273   output->append(kKeyBeginFooterMarker);
    274   output->append(" ");
    275   output->append(is_public ? kPublic : kPrivate);
    276   output->append(" ");
    277   output->append(kKeyInfoEndMarker);
    278   output->append("\n");
    279 
    280   return true;
    281 }
    282 
    283 // static
    284 GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
    285   return GURL(std::string(extensions::kExtensionScheme) +
    286               url::kStandardSchemeSeparator + extension_id + "/");
    287 }
    288 
    289 bool Extension::ShowConfigureContextMenus() const {
    290   // Don't show context menu for component extensions. We might want to show
    291   // options for component extension button but now there is no component
    292   // extension with options. All other menu items like uninstall have
    293   // no sense for component extensions.
    294   return location() != Manifest::COMPONENT &&
    295          location() != Manifest::EXTERNAL_COMPONENT;
    296 }
    297 
    298 bool Extension::OverlapsWithOrigin(const GURL& origin) const {
    299   if (url() == origin)
    300     return true;
    301 
    302   if (web_extent().is_empty())
    303     return false;
    304 
    305   // Note: patterns and extents ignore port numbers.
    306   URLPattern origin_only_pattern(kValidWebExtentSchemes);
    307   if (!origin_only_pattern.SetScheme(origin.scheme()))
    308     return false;
    309   origin_only_pattern.SetHost(origin.host());
    310   origin_only_pattern.SetPath("/*");
    311 
    312   URLPatternSet origin_only_pattern_list;
    313   origin_only_pattern_list.AddPattern(origin_only_pattern);
    314 
    315   return web_extent().OverlapsWith(origin_only_pattern_list);
    316 }
    317 
    318 bool Extension::RequiresSortOrdinal() const {
    319   return is_app() && (display_in_launcher_ || display_in_new_tab_page_);
    320 }
    321 
    322 bool Extension::ShouldDisplayInAppLauncher() const {
    323   // Only apps should be displayed in the launcher.
    324   return is_app() && display_in_launcher_;
    325 }
    326 
    327 bool Extension::ShouldDisplayInNewTabPage() const {
    328   // Only apps should be displayed on the NTP.
    329   return is_app() && display_in_new_tab_page_;
    330 }
    331 
    332 bool Extension::ShouldDisplayInExtensionSettings() const {
    333   // Don't show for themes since the settings UI isn't really useful for them.
    334   if (is_theme())
    335     return false;
    336 
    337   // Don't show component extensions and invisible apps.
    338   if (ShouldNotBeVisible())
    339     return false;
    340 
    341   // Always show unpacked extensions and apps.
    342   if (Manifest::IsUnpackedLocation(location()))
    343     return true;
    344 
    345   // Unless they are unpacked, never show hosted apps. Note: We intentionally
    346   // show packaged apps and platform apps because there are some pieces of
    347   // functionality that are only available in chrome://extensions/ but which
    348   // are needed for packaged and platform apps. For example, inspecting
    349   // background pages. See http://crbug.com/116134.
    350   if (is_hosted_app())
    351     return false;
    352 
    353   return true;
    354 }
    355 
    356 bool Extension::ShouldNotBeVisible() const {
    357   // Don't show component extensions because they are only extensions as an
    358   // implementation detail of Chrome.
    359   if (extensions::Manifest::IsComponentLocation(location()) &&
    360       !CommandLine::ForCurrentProcess()->HasSwitch(
    361           switches::kShowComponentExtensionOptions)) {
    362     return true;
    363   }
    364 
    365   // Always show unpacked extensions and apps.
    366   if (Manifest::IsUnpackedLocation(location()))
    367     return false;
    368 
    369   // Don't show apps that aren't visible in either launcher or ntp.
    370   if (is_app() && !ShouldDisplayInAppLauncher() && !ShouldDisplayInNewTabPage())
    371     return true;
    372 
    373   return false;
    374 }
    375 
    376 Extension::ManifestData* Extension::GetManifestData(const std::string& key)
    377     const {
    378   DCHECK(finished_parsing_manifest_ || thread_checker_.CalledOnValidThread());
    379   ManifestDataMap::const_iterator iter = manifest_data_.find(key);
    380   if (iter != manifest_data_.end())
    381     return iter->second.get();
    382   return NULL;
    383 }
    384 
    385 void Extension::SetManifestData(const std::string& key,
    386                                 Extension::ManifestData* data) {
    387   DCHECK(!finished_parsing_manifest_ && thread_checker_.CalledOnValidThread());
    388   manifest_data_[key] = linked_ptr<ManifestData>(data);
    389 }
    390 
    391 Manifest::Location Extension::location() const {
    392   return manifest_->location();
    393 }
    394 
    395 const std::string& Extension::id() const {
    396   return manifest_->extension_id();
    397 }
    398 
    399 const std::string Extension::VersionString() const {
    400   return version()->GetString();
    401 }
    402 
    403 void Extension::AddInstallWarning(const InstallWarning& new_warning) {
    404   install_warnings_.push_back(new_warning);
    405 }
    406 
    407 void Extension::AddInstallWarnings(
    408     const std::vector<InstallWarning>& new_warnings) {
    409   install_warnings_.insert(install_warnings_.end(),
    410                            new_warnings.begin(), new_warnings.end());
    411 }
    412 
    413 bool Extension::is_app() const {
    414   return manifest_->is_app();
    415 }
    416 
    417 bool Extension::is_platform_app() const {
    418   return manifest_->is_platform_app();
    419 }
    420 
    421 bool Extension::is_hosted_app() const {
    422   return manifest()->is_hosted_app();
    423 }
    424 
    425 bool Extension::is_legacy_packaged_app() const {
    426   return manifest()->is_legacy_packaged_app();
    427 }
    428 
    429 bool Extension::is_extension() const {
    430   return manifest()->is_extension();
    431 }
    432 
    433 bool Extension::can_be_incognito_enabled() const {
    434   // Only component platform apps are supported in incognito.
    435   return !is_platform_app() || location() == Manifest::COMPONENT;
    436 }
    437 
    438 void Extension::AddWebExtentPattern(const URLPattern& pattern) {
    439   // Bookmark apps are permissionless.
    440   if (from_bookmark())
    441     return;
    442 
    443   extent_.AddPattern(pattern);
    444 }
    445 
    446 bool Extension::is_theme() const {
    447   return manifest()->is_theme();
    448 }
    449 
    450 // static
    451 bool Extension::InitExtensionID(extensions::Manifest* manifest,
    452                                 const base::FilePath& path,
    453                                 const std::string& explicit_id,
    454                                 int creation_flags,
    455                                 base::string16* error) {
    456   if (!explicit_id.empty()) {
    457     manifest->set_extension_id(explicit_id);
    458     return true;
    459   }
    460 
    461   if (manifest->HasKey(keys::kPublicKey)) {
    462     std::string public_key;
    463     std::string public_key_bytes;
    464     if (!manifest->GetString(keys::kPublicKey, &public_key) ||
    465         !ParsePEMKeyBytes(public_key, &public_key_bytes)) {
    466       *error = base::ASCIIToUTF16(errors::kInvalidKey);
    467       return false;
    468     }
    469     std::string extension_id = id_util::GenerateId(public_key_bytes);
    470     manifest->set_extension_id(extension_id);
    471     return true;
    472   }
    473 
    474   if (creation_flags & REQUIRE_KEY) {
    475     *error = base::ASCIIToUTF16(errors::kInvalidKey);
    476     return false;
    477   } else {
    478     // If there is a path, we generate the ID from it. This is useful for
    479     // development mode, because it keeps the ID stable across restarts and
    480     // reloading the extension.
    481     std::string extension_id = id_util::GenerateIdForPath(path);
    482     if (extension_id.empty()) {
    483       NOTREACHED() << "Could not create ID from path.";
    484       return false;
    485     }
    486     manifest->set_extension_id(extension_id);
    487     return true;
    488   }
    489 }
    490 
    491 Extension::Extension(const base::FilePath& path,
    492                      scoped_ptr<extensions::Manifest> manifest)
    493     : manifest_version_(0),
    494       converted_from_user_script_(false),
    495       manifest_(manifest.release()),
    496       finished_parsing_manifest_(false),
    497       display_in_launcher_(true),
    498       display_in_new_tab_page_(true),
    499       wants_file_access_(false),
    500       creation_flags_(0) {
    501   DCHECK(path.empty() || path.IsAbsolute());
    502   path_ = id_util::MaybeNormalizePath(path);
    503 }
    504 
    505 Extension::~Extension() {
    506 }
    507 
    508 bool Extension::InitFromValue(int flags, base::string16* error) {
    509   DCHECK(error);
    510 
    511   creation_flags_ = flags;
    512 
    513   // Important to load manifest version first because many other features
    514   // depend on its value.
    515   if (!LoadManifestVersion(error))
    516     return false;
    517 
    518   if (!LoadRequiredFeatures(error))
    519     return false;
    520 
    521   // We don't need to validate because InitExtensionID already did that.
    522   manifest_->GetString(keys::kPublicKey, &public_key_);
    523 
    524   extension_url_ = Extension::GetBaseURLFromExtensionId(id());
    525 
    526   // Load App settings. LoadExtent at least has to be done before
    527   // ParsePermissions(), because the valid permissions depend on what type of
    528   // package this is.
    529   if (is_app() && !LoadAppFeatures(error))
    530     return false;
    531 
    532   permissions_parser_.reset(new PermissionsParser());
    533   if (!permissions_parser_->Parse(this, error))
    534     return false;
    535 
    536   if (manifest_->HasKey(keys::kConvertedFromUserScript)) {
    537     manifest_->GetBoolean(keys::kConvertedFromUserScript,
    538                           &converted_from_user_script_);
    539   }
    540 
    541   if (!LoadSharedFeatures(error))
    542     return false;
    543 
    544   permissions_parser_->Finalize(this);
    545   permissions_parser_.reset();
    546 
    547   finished_parsing_manifest_ = true;
    548 
    549   permissions_data_.reset(new PermissionsData(this));
    550 
    551   return true;
    552 }
    553 
    554 bool Extension::LoadRequiredFeatures(base::string16* error) {
    555   if (!LoadName(error) ||
    556       !LoadVersion(error))
    557     return false;
    558   return true;
    559 }
    560 
    561 bool Extension::LoadName(base::string16* error) {
    562   base::string16 localized_name;
    563   if (!manifest_->GetString(keys::kName, &localized_name)) {
    564     *error = base::ASCIIToUTF16(errors::kInvalidName);
    565     return false;
    566   }
    567   non_localized_name_ = base::UTF16ToUTF8(localized_name);
    568   base::i18n::AdjustStringForLocaleDirection(&localized_name);
    569   name_ = base::UTF16ToUTF8(localized_name);
    570   return true;
    571 }
    572 
    573 bool Extension::LoadVersion(base::string16* error) {
    574   std::string version_str;
    575   if (!manifest_->GetString(keys::kVersion, &version_str)) {
    576     *error = base::ASCIIToUTF16(errors::kInvalidVersion);
    577     return false;
    578   }
    579   version_.reset(new Version(version_str));
    580   if (!version_->IsValid() || version_->components().size() > 4) {
    581     *error = base::ASCIIToUTF16(errors::kInvalidVersion);
    582     return false;
    583   }
    584   return true;
    585 }
    586 
    587 bool Extension::LoadAppFeatures(base::string16* error) {
    588   if (!LoadExtent(keys::kWebURLs, &extent_,
    589                   errors::kInvalidWebURLs, errors::kInvalidWebURL, error)) {
    590     return false;
    591   }
    592   if (manifest_->HasKey(keys::kDisplayInLauncher) &&
    593       !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) {
    594     *error = base::ASCIIToUTF16(errors::kInvalidDisplayInLauncher);
    595     return false;
    596   }
    597   if (manifest_->HasKey(keys::kDisplayInNewTabPage)) {
    598     if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage,
    599                                &display_in_new_tab_page_)) {
    600       *error = base::ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage);
    601       return false;
    602     }
    603   } else {
    604     // Inherit default from display_in_launcher property.
    605     display_in_new_tab_page_ = display_in_launcher_;
    606   }
    607   return true;
    608 }
    609 
    610 bool Extension::LoadExtent(const char* key,
    611                            URLPatternSet* extent,
    612                            const char* list_error,
    613                            const char* value_error,
    614                            base::string16* error) {
    615   const base::Value* temp_pattern_value = NULL;
    616   if (!manifest_->Get(key, &temp_pattern_value))
    617     return true;
    618 
    619   const base::ListValue* pattern_list = NULL;
    620   if (!temp_pattern_value->GetAsList(&pattern_list)) {
    621     *error = base::ASCIIToUTF16(list_error);
    622     return false;
    623   }
    624 
    625   for (size_t i = 0; i < pattern_list->GetSize(); ++i) {
    626     std::string pattern_string;
    627     if (!pattern_list->GetString(i, &pattern_string)) {
    628       *error = ErrorUtils::FormatErrorMessageUTF16(value_error,
    629                                                    base::UintToString(i),
    630                                                    errors::kExpectString);
    631       return false;
    632     }
    633 
    634     URLPattern pattern(kValidWebExtentSchemes);
    635     URLPattern::ParseResult parse_result = pattern.Parse(pattern_string);
    636     if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) {
    637       pattern_string += "/";
    638       parse_result = pattern.Parse(pattern_string);
    639     }
    640 
    641     if (parse_result != URLPattern::PARSE_SUCCESS) {
    642       *error = ErrorUtils::FormatErrorMessageUTF16(
    643           value_error,
    644           base::UintToString(i),
    645           URLPattern::GetParseResultString(parse_result));
    646       return false;
    647     }
    648 
    649     // Do not allow authors to claim "<all_urls>".
    650     if (pattern.match_all_urls()) {
    651       *error = ErrorUtils::FormatErrorMessageUTF16(
    652           value_error,
    653           base::UintToString(i),
    654           errors::kCannotClaimAllURLsInExtent);
    655       return false;
    656     }
    657 
    658     // Do not allow authors to claim "*" for host.
    659     if (pattern.host().empty()) {
    660       *error = ErrorUtils::FormatErrorMessageUTF16(
    661           value_error,
    662           base::UintToString(i),
    663           errors::kCannotClaimAllHostsInExtent);
    664       return false;
    665     }
    666 
    667     // We do not allow authors to put wildcards in their paths. Instead, we
    668     // imply one at the end.
    669     if (pattern.path().find('*') != std::string::npos) {
    670       *error = ErrorUtils::FormatErrorMessageUTF16(
    671           value_error,
    672           base::UintToString(i),
    673           errors::kNoWildCardsInPaths);
    674       return false;
    675     }
    676     pattern.SetPath(pattern.path() + '*');
    677 
    678     extent->AddPattern(pattern);
    679   }
    680 
    681   return true;
    682 }
    683 
    684 bool Extension::LoadSharedFeatures(base::string16* error) {
    685   if (!LoadDescription(error) ||
    686       !ManifestHandler::ParseExtension(this, error) ||
    687       !LoadShortName(error))
    688     return false;
    689 
    690   return true;
    691 }
    692 
    693 bool Extension::LoadDescription(base::string16* error) {
    694   if (manifest_->HasKey(keys::kDescription) &&
    695       !manifest_->GetString(keys::kDescription, &description_)) {
    696     *error = base::ASCIIToUTF16(errors::kInvalidDescription);
    697     return false;
    698   }
    699   return true;
    700 }
    701 
    702 bool Extension::LoadManifestVersion(base::string16* error) {
    703   // Get the original value out of the dictionary so that we can validate it
    704   // more strictly.
    705   if (manifest_->value()->HasKey(keys::kManifestVersion)) {
    706     int manifest_version = 1;
    707     if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) ||
    708         manifest_version < 1) {
    709       *error = base::ASCIIToUTF16(errors::kInvalidManifestVersion);
    710       return false;
    711     }
    712   }
    713 
    714   manifest_version_ = manifest_->GetManifestVersion();
    715   if (manifest_version_ < kModernManifestVersion &&
    716       ((creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION &&
    717         !CommandLine::ForCurrentProcess()->HasSwitch(
    718             switches::kAllowLegacyExtensionManifests)) ||
    719        GetType() == Manifest::TYPE_PLATFORM_APP)) {
    720     *error = ErrorUtils::FormatErrorMessageUTF16(
    721         errors::kInvalidManifestVersionOld,
    722         base::IntToString(kModernManifestVersion),
    723         is_platform_app() ? "apps" : "extensions");
    724     return false;
    725   }
    726 
    727   return true;
    728 }
    729 
    730 bool Extension::LoadShortName(base::string16* error) {
    731   if (manifest_->HasKey(keys::kShortName)) {
    732     base::string16 localized_short_name;
    733     if (!manifest_->GetString(keys::kShortName, &localized_short_name) ||
    734         localized_short_name.empty()) {
    735       *error = base::ASCIIToUTF16(errors::kInvalidShortName);
    736       return false;
    737     }
    738 
    739     base::i18n::AdjustStringForLocaleDirection(&localized_short_name);
    740     short_name_ = base::UTF16ToUTF8(localized_short_name);
    741   } else {
    742     short_name_ = name_;
    743   }
    744   return true;
    745 }
    746 
    747 ExtensionInfo::ExtensionInfo(const base::DictionaryValue* manifest,
    748                              const std::string& id,
    749                              const base::FilePath& path,
    750                              Manifest::Location location)
    751     : extension_id(id),
    752       extension_path(path),
    753       extension_location(location) {
    754   if (manifest)
    755     extension_manifest.reset(manifest->DeepCopy());
    756 }
    757 
    758 ExtensionInfo::~ExtensionInfo() {}
    759 
    760 InstalledExtensionInfo::InstalledExtensionInfo(
    761     const Extension* extension,
    762     bool is_update,
    763     bool from_ephemeral,
    764     const std::string& old_name)
    765     : extension(extension),
    766       is_update(is_update),
    767       from_ephemeral(from_ephemeral),
    768       old_name(old_name) {}
    769 
    770 UnloadedExtensionInfo::UnloadedExtensionInfo(
    771     const Extension* extension,
    772     UnloadedExtensionInfo::Reason reason)
    773     : reason(reason),
    774       extension(extension) {}
    775 
    776 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
    777     const Extension* extension,
    778     const PermissionSet* permissions,
    779     Reason reason)
    780     : reason(reason),
    781       extension(extension),
    782       permissions(permissions) {}
    783 
    784 }   // namespace extensions
    785