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
45namespace keys = extension_manifest_keys;
46namespace values = extension_manifest_values;
47namespace errors = extension_manifest_errors;
48
49namespace extensions {
50
51namespace {
52
53const int kModernManifestVersion = 2;
54const int kPEMOutputColumns = 65;
55
56// KEY MARKERS
57const char kKeyBeginHeaderMarker[] = "-----BEGIN";
58const char kKeyBeginFooterMarker[] = "-----END";
59const char kKeyInfoEndMarker[] = "KEY-----";
60const char kPublic[] = "PUBLIC";
61const char kPrivate[] = "PRIVATE";
62
63const int kRSAKeySize = 1024;
64
65// A singleton object containing global data needed by the extension objects.
66class 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)
103const char Extension::kExtensionRegistryPath[] =
104    "Software\\Google\\Chrome\\Extensions";
105#endif
106
107const char Extension::kMimeType[] = "application/x-chrome-extension";
108
109const int Extension::kValidWebExtentSchemes =
110    URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
111
112const 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
123scoped_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.
138scoped_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
172bool 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
187Manifest::Type Extension::GetType() const {
188  return converted_from_user_script() ?
189      Manifest::TYPE_USER_SCRIPT : manifest_->type();
190}
191
192// static
193GURL 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
212bool Extension::ResourceMatches(const URLPatternSet& pattern_set,
213                                const std::string& resource) const {
214  return pattern_set.MatchesURL(extension_url_.Resolve(resource));
215}
216
217ExtensionResource 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
232ExtensionResource 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
245bool 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
276bool 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
282bool 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
312GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
313  return GURL(std::string(extensions::kExtensionScheme) +
314              content::kStandardSchemeSeparator + extension_id + "/");
315}
316
317// static
318void 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
330const Extension::ScriptingWhitelist* Extension::GetScriptingWhitelist() {
331  return ExtensionConfig::GetInstance()->whitelist();
332}
333
334bool Extension::HasAPIPermission(APIPermission::ID permission) const {
335  return PermissionsData::HasAPIPermission(this, permission);
336}
337
338bool Extension::HasAPIPermission(const std::string& permission_name) const {
339  return PermissionsData::HasAPIPermission(this, permission_name);
340}
341
342scoped_refptr<const PermissionSet> Extension::GetActivePermissions() const {
343  return PermissionsData::GetActivePermissions(this);
344}
345
346bool 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
354bool 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
374bool Extension::RequiresSortOrdinal() const {
375  return is_app() && (display_in_launcher_ || display_in_new_tab_page_);
376}
377
378bool Extension::ShouldDisplayInAppLauncher() const {
379  // Only apps should be displayed in the launcher.
380  return is_app() && display_in_launcher_;
381}
382
383bool Extension::ShouldDisplayInNewTabPage() const {
384  // Only apps should be displayed on the NTP.
385  return is_app() && display_in_new_tab_page_;
386}
387
388bool 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
412bool 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
432Extension::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
441void 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
447Manifest::Location Extension::location() const {
448  return manifest_->location();
449}
450
451const std::string& Extension::id() const {
452  return manifest_->extension_id();
453}
454
455const std::string Extension::VersionString() const {
456  return version()->GetString();
457}
458
459void Extension::AddInstallWarning(const InstallWarning& new_warning) {
460  install_warnings_.push_back(new_warning);
461}
462
463void 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
469bool Extension::is_app() const {
470  return manifest_->is_app();
471}
472
473bool Extension::is_platform_app() const {
474  return manifest_->is_platform_app();
475}
476
477bool Extension::is_hosted_app() const {
478  return manifest()->is_hosted_app();
479}
480
481bool Extension::is_legacy_packaged_app() const {
482  return manifest()->is_legacy_packaged_app();
483}
484
485bool Extension::is_extension() const {
486  return manifest()->is_extension();
487}
488
489bool 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
494bool Extension::force_incognito_enabled() const {
495  return PermissionsData::HasAPIPermission(this, APIPermission::kProxy);
496}
497
498void Extension::AddWebExtentPattern(const URLPattern& pattern) {
499  extent_.AddPattern(pattern);
500}
501
502bool Extension::is_theme() const {
503  return manifest()->is_theme();
504}
505
506// static
507bool 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
547Extension::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
561Extension::~Extension() {
562}
563
564bool 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
607bool Extension::LoadRequiredFeatures(string16* error) {
608  if (!LoadName(error) ||
609      !LoadVersion(error))
610    return false;
611  return true;
612}
613
614bool 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
626bool 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
640bool 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
663bool 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
737bool Extension::LoadSharedFeatures(string16* error) {
738  if (!LoadDescription(error) ||
739      !ManifestHandler::ParseExtension(this, error))
740    return false;
741
742  return true;
743}
744
745bool 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
754bool 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
780ExtensionInfo::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
791ExtensionInfo::~ExtensionInfo() {}
792
793InstalledExtensionInfo::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
801UnloadedExtensionInfo::UnloadedExtensionInfo(
802    const Extension* extension,
803    extension_misc::UnloadedExtensionReason reason)
804    : reason(reason),
805      extension(extension) {}
806
807UpdatedExtensionPermissionsInfo::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