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