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