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
39namespace extensions {
40
41namespace keys = manifest_keys;
42namespace values = manifest_values;
43namespace errors = manifest_errors;
44
45namespace {
46
47const int kModernManifestVersion = 2;
48const int kPEMOutputColumns = 64;
49
50// KEY MARKERS
51const char kKeyBeginHeaderMarker[] = "-----BEGIN";
52const char kKeyBeginFooterMarker[] = "-----END";
53const char kKeyInfoEndMarker[] = "KEY-----";
54const char kPublic[] = "PUBLIC";
55const char kPrivate[] = "PRIVATE";
56
57bool 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
70const int Extension::kInitFromValueFlagBits = 13;
71
72const char Extension::kMimeType[] = "application/x-chrome-extension";
73
74const int Extension::kValidWebExtentSchemes =
75    URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
76
77const 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
88scoped_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.
103scoped_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
136Manifest::Type Extension::GetType() const {
137  return converted_from_user_script() ?
138      Manifest::TYPE_USER_SCRIPT : manifest_->type();
139}
140
141// static
142GURL 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
161bool Extension::ResourceMatches(const URLPatternSet& pattern_set,
162                                const std::string& resource) const {
163  return pattern_set.MatchesURL(extension_url_.Resolve(resource));
164}
165
166ExtensionResource 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
183ExtensionResource 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
198bool 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
229bool 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
238bool 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
268GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) {
269  return GURL(std::string(extensions::kExtensionScheme) +
270              url::kStandardSchemeSeparator + extension_id + "/");
271}
272
273bool 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
282bool 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
302bool Extension::RequiresSortOrdinal() const {
303  return is_app() && (display_in_launcher_ || display_in_new_tab_page_);
304}
305
306bool Extension::ShouldDisplayInAppLauncher() const {
307  // Only apps should be displayed in the launcher.
308  return is_app() && display_in_launcher_;
309}
310
311bool Extension::ShouldDisplayInNewTabPage() const {
312  // Only apps should be displayed on the NTP.
313  return is_app() && display_in_new_tab_page_;
314}
315
316bool 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
340bool 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
360Extension::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
369void 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
375Manifest::Location Extension::location() const {
376  return manifest_->location();
377}
378
379const std::string& Extension::id() const {
380  return manifest_->extension_id();
381}
382
383const std::string Extension::VersionString() const {
384  return version()->GetString();
385}
386
387void Extension::AddInstallWarning(const InstallWarning& new_warning) {
388  install_warnings_.push_back(new_warning);
389}
390
391void 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
397bool Extension::is_app() const {
398  return manifest()->is_app();
399}
400
401bool Extension::is_platform_app() const {
402  return manifest()->is_platform_app();
403}
404
405bool Extension::is_hosted_app() const {
406  return manifest()->is_hosted_app();
407}
408
409bool Extension::is_legacy_packaged_app() const {
410  return manifest()->is_legacy_packaged_app();
411}
412
413bool Extension::is_extension() const {
414  return manifest()->is_extension();
415}
416
417bool Extension::is_shared_module() const {
418  return manifest()->is_shared_module();
419}
420
421bool Extension::is_theme() const {
422  return manifest()->is_theme();
423}
424
425bool 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
430void 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
439bool 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
479Extension::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
493Extension::~Extension() {
494}
495
496bool 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
542bool Extension::LoadRequiredFeatures(base::string16* error) {
543  if (!LoadName(error) ||
544      !LoadVersion(error))
545    return false;
546  return true;
547}
548
549bool 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
561bool 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
575bool 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
598bool 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
672bool 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
681bool 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
690bool 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
718bool 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
735ExtensionInfo::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
746ExtensionInfo::~ExtensionInfo() {}
747
748InstalledExtensionInfo::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
758UnloadedExtensionInfo::UnloadedExtensionInfo(
759    const Extension* extension,
760    UnloadedExtensionInfo::Reason reason)
761    : reason(reason),
762      extension(extension) {}
763
764UpdatedExtensionPermissionsInfo::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