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