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