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