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