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