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