1 // Copyright (c) 2012 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.
5 #include "chrome/common/extensions/extension.h"
9 #include "base/base64.h"
10 #include "base/basictypes.h"
11 #include "base/command_line.h"
12 #include "base/file_path.h"
13 #include "base/file_util.h"
14 #include "base/i18n/rtl.h"
15 #include "base/logging.h"
16 #include "base/memory/singleton.h"
17 #include "base/stl_util.h"
18 #include "base/string16.h"
19 #include "base/string_number_conversions.h"
20 #include "base/string_piece.h"
21 #include "base/string_util.h"
22 #include "base/stringprintf.h"
23 #include "base/utf_string_conversions.h"
24 #include "base/values.h"
25 #include "base/version.h"
26 #include "chrome/common/chrome_constants.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "chrome/common/chrome_version_info.h"
29 #include "chrome/common/extensions/csp_validator.h"
30 #include "chrome/common/extensions/extension_error_utils.h"
31 #include "chrome/common/extensions/extension_manifest_constants.h"
32 #include "chrome/common/extensions/extension_resource.h"
33 #include "chrome/common/extensions/feature_switch.h"
34 #include "chrome/common/extensions/features/feature.h"
35 #include "chrome/common/extensions/features/simple_feature_provider.h"
36 #include "chrome/common/extensions/file_browser_handler.h"
37 #include "chrome/common/extensions/manifest.h"
38 #include "chrome/common/extensions/permissions/permission_set.h"
39 #include "chrome/common/extensions/permissions/permissions_info.h"
40 #include "chrome/common/extensions/url_pattern_set.h"
41 #include "chrome/common/extensions/user_script.h"
42 #include "chrome/common/url_constants.h"
43 #include "crypto/sha2.h"
44 #include "extensions/common/constants.h"
45 #include "googleurl/src/url_util.h"
46 #include "grit/chromium_strings.h"
47 #include "grit/theme_resources.h"
48 #include "third_party/skia/include/core/SkBitmap.h"
49 #include "ui/base/l10n/l10n_util.h"
50 #include "ui/base/resource/resource_bundle.h"
51 #include "webkit/glue/image_decoder.h"
52 #include "webkit/glue/web_intent_service_data.h"
55 #include "base/win/metro.h"
56 #include "grit/generated_resources.h"
59 namespace keys
= extension_manifest_keys
;
60 namespace values
= extension_manifest_values
;
61 namespace errors
= extension_manifest_errors
;
62 namespace info_keys
= extension_info_keys
;
64 using extensions::csp_validator::ContentSecurityPolicyIsLegal
;
65 using extensions::csp_validator::ContentSecurityPolicyIsSandboxed
;
66 using extensions::csp_validator::ContentSecurityPolicyIsSecure
;
68 namespace extensions
{
72 const int kModernManifestVersion
= 2;
73 const int kPEMOutputColumns
= 65;
75 const char kOverrideExtentUrlPatternFormat
[] = "chrome://%s/*";
77 // The maximum number of commands (including page action/browser actions) an
78 // extension can have.
79 const size_t kMaxCommandsPerExtension
= 4;
82 const char kKeyBeginHeaderMarker
[] = "-----BEGIN";
83 const char kKeyBeginFooterMarker
[] = "-----END";
84 const char kKeyInfoEndMarker
[] = "KEY-----";
85 const char kPublic
[] = "PUBLIC";
86 const char kPrivate
[] = "PRIVATE";
88 const int kRSAKeySize
= 1024;
90 const char kDefaultContentSecurityPolicy
[] =
91 "script-src 'self' chrome-extension-resource:; object-src 'self'";
93 #define PLATFORM_APP_LOCAL_CSP_SOURCES \
94 "'self' data: chrome-extension-resource:"
95 const char kDefaultPlatformAppContentSecurityPolicy
[] =
96 // Platform apps can only use local resources by default.
97 "default-src 'self' chrome-extension-resource:;"
98 // For remote resources, they can fetch them via XMLHttpRequest.
100 // And serve them via data: or same-origin (blob:, filesystem:) URLs
101 "style-src " PLATFORM_APP_LOCAL_CSP_SOURCES
" 'unsafe-inline';"
102 "img-src " PLATFORM_APP_LOCAL_CSP_SOURCES
";"
103 "frame-src " PLATFORM_APP_LOCAL_CSP_SOURCES
";"
104 "font-src " PLATFORM_APP_LOCAL_CSP_SOURCES
";"
105 // Media can be loaded from remote resources since:
106 // 1. <video> and <audio> have good fallback behavior when offline or under
107 // spotty connectivity.
108 // 2. Fetching via XHR and serving via blob: URLs currently does not allow
109 // streaming or partial buffering.
112 const char kDefaultSandboxedPageContentSecurityPolicy
[] =
113 "sandbox allow-scripts allow-forms allow-popups";
115 // Converts a normal hexadecimal string into the alphabet used by extensions.
116 // We use the characters 'a'-'p' instead of '0'-'f' to avoid ever having a
117 // completely numeric host, since some software interprets that as an IP
119 static void ConvertHexadecimalToIDAlphabet(std::string
* id
) {
120 for (size_t i
= 0; i
< id
->size(); ++i
) {
122 if (base::HexStringToInt(base::StringPiece(id
->begin() + i
,
123 id
->begin() + i
+ 1),
125 (*id
)[i
] = val
+ 'a';
132 // Strips leading slashes from the file path. Returns true iff the final path is
134 bool NormalizeAndValidatePath(std::string
* path
) {
135 size_t first_non_slash
= path
->find_first_not_of('/');
136 if (first_non_slash
== std::string::npos
) {
141 *path
= path
->substr(first_non_slash
);
145 // Loads icon paths defined in dictionary |icons_value| into ExtensionIconSet
146 // |icons|. |icons_value| is a dictionary value {icon size -> icon path}. Icons
147 // in |icons_value| whose size is not in |icon_sizes| will be ignored.
148 // Returns success. If load fails, |error| will be set.
149 bool LoadIconsFromDictionary(const DictionaryValue
* icons_value
,
150 const int* icon_sizes
,
151 size_t num_icon_sizes
,
152 ExtensionIconSet
* icons
,
155 for (size_t i
= 0; i
< num_icon_sizes
; ++i
) {
156 std::string key
= base::IntToString(icon_sizes
[i
]);
157 if (icons_value
->HasKey(key
)) {
158 std::string icon_path
;
159 if (!icons_value
->GetString(key
, &icon_path
)) {
160 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
161 errors::kInvalidIconPath
, key
);
165 if (!NormalizeAndValidatePath(&icon_path
)) {
166 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
167 errors::kInvalidIconPath
, key
);
171 icons
->Add(icon_sizes
[i
], icon_path
);
177 // A singleton object containing global data needed by the extension objects.
178 class ExtensionConfig
{
180 static ExtensionConfig
* GetInstance() {
181 return Singleton
<ExtensionConfig
>::get();
184 Extension::ScriptingWhitelist
* whitelist() { return &scripting_whitelist_
; }
187 friend struct DefaultSingletonTraits
<ExtensionConfig
>;
190 // Whitelist ChromeVox, an accessibility extension from Google that needs
191 // the ability to script webui pages. This is temporary and is not
192 // meant to be a general solution.
193 // TODO(dmazzoni): remove this once we have an extension API that
194 // allows any extension to request read-only access to webui pages.
195 scripting_whitelist_
.push_back("kgejglhpjiefppelpmljglcjbhoiplfn");
197 ~ExtensionConfig() { }
199 // A whitelist of extensions that can script anywhere. Do not add to this
200 // list (except in tests) without consulting the Extensions team first.
201 // Note: Component extensions have this right implicitly and do not need to be
202 // added to this list.
203 Extension::ScriptingWhitelist scripting_whitelist_
;
206 // Rank extension locations in a way that allows
207 // Extension::GetHigherPriorityLocation() to compare locations.
208 // An extension installed from two locations will have the location
209 // with the higher rank, as returned by this function. The actual
210 // integer values may change, and should never be persisted.
211 int GetLocationRank(Extension::Location location
) {
212 const int kInvalidRank
= -1;
213 int rank
= kInvalidRank
; // Will CHECK that rank is not kInvalidRank.
216 // Component extensions can not be overriden by any other type.
217 case Extension::COMPONENT
:
221 // Policy controlled extensions may not be overridden by any type
222 // that is not part of chrome.
223 case Extension::EXTERNAL_POLICY_DOWNLOAD
:
227 // A developer-loaded extension should override any installed type
228 // that a user can disable.
229 case Extension::LOAD
:
233 // The relative priority of various external sources is not important,
234 // but having some order ensures deterministic behavior.
235 case Extension::EXTERNAL_REGISTRY
:
239 case Extension::EXTERNAL_PREF
:
243 case Extension::EXTERNAL_PREF_DOWNLOAD
:
247 // User installed extensions are overridden by any external type.
248 case Extension::INTERNAL
:
253 NOTREACHED() << "Need to add new extension locaton " << location
;
256 CHECK(rank
!= kInvalidRank
);
260 bool ReadLaunchDimension(const extensions::Manifest
* manifest
,
263 bool is_valid_container
,
266 if (manifest
->Get(key
, &temp
)) {
267 if (!is_valid_container
) {
268 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
269 errors::kInvalidLaunchValueContainer
,
273 if (!temp
->GetAsInteger(target
) || *target
< 0) {
275 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
276 errors::kInvalidLaunchValue
,
286 const FilePath::CharType
Extension::kManifestFilename
[] =
287 FILE_PATH_LITERAL("manifest.json");
288 const FilePath::CharType
Extension::kLocaleFolder
[] =
289 FILE_PATH_LITERAL("_locales");
290 const FilePath::CharType
Extension::kMessagesFilename
[] =
291 FILE_PATH_LITERAL("messages.json");
294 const char Extension::kExtensionRegistryPath
[] =
295 "Software\\Google\\Chrome\\Extensions";
298 // first 16 bytes of SHA256 hashed public key.
299 const size_t Extension::kIdSize
= 16;
301 const char Extension::kMimeType
[] = "application/x-chrome-extension";
303 const int Extension::kPageActionIconMaxSize
= 19;
304 const int Extension::kBrowserActionIconMaxSize
= 19;
306 const int Extension::kValidWebExtentSchemes
=
307 URLPattern::SCHEME_HTTP
| URLPattern::SCHEME_HTTPS
;
309 const int Extension::kValidHostPermissionSchemes
=
310 UserScript::kValidUserScriptSchemes
| URLPattern::SCHEME_CHROMEUI
;
312 Extension::Requirements::Requirements()
318 Extension::Requirements::~Requirements() {}
320 Extension::InputComponentInfo::InputComponentInfo()
321 : type(INPUT_COMPONENT_TYPE_NONE
),
323 shortcut_ctrl(false),
324 shortcut_shift(false) {
327 Extension::InputComponentInfo::~InputComponentInfo() {}
329 Extension::TtsVoice::TtsVoice() {}
330 Extension::TtsVoice::~TtsVoice() {}
332 Extension::OAuth2Info::OAuth2Info() {}
333 Extension::OAuth2Info::~OAuth2Info() {}
335 Extension::ActionInfo::ActionInfo() {}
336 Extension::ActionInfo::~ActionInfo() {}
343 scoped_refptr
<Extension
> Extension::Create(const FilePath
& path
,
345 const DictionaryValue
& value
,
347 std::string
* utf8_error
) {
348 return Extension::Create(path
,
352 std::string(), // ID is ignored if empty.
356 scoped_refptr
<Extension
> Extension::Create(const FilePath
& path
,
358 const DictionaryValue
& value
,
360 const std::string
& explicit_id
,
361 std::string
* utf8_error
) {
364 scoped_ptr
<extensions::Manifest
> manifest(
365 new extensions::Manifest(location
,
366 scoped_ptr
<DictionaryValue
>(value
.DeepCopy())));
368 if (!InitExtensionID(manifest
.get(), path
, explicit_id
, flags
, &error
)) {
369 *utf8_error
= UTF16ToUTF8(error
);
373 InstallWarningVector install_warnings
;
374 manifest
->ValidateManifest(utf8_error
, &install_warnings
);
375 if (!utf8_error
->empty())
378 scoped_refptr
<Extension
> extension
= new Extension(path
, manifest
.Pass());
379 extension
->install_warnings_
.swap(install_warnings
);
381 if (!extension
->InitFromValue(flags
, &error
)) {
382 *utf8_error
= UTF16ToUTF8(error
);
386 if (!extension
->CheckPlatformAppFeatures(utf8_error
) ||
387 !extension
->CheckConflictingFeatures(utf8_error
)) {
395 Extension::Location
Extension::GetHigherPriorityLocation(
396 Extension::Location loc1
, Extension::Location loc2
) {
400 int loc1_rank
= GetLocationRank(loc1
);
401 int loc2_rank
= GetLocationRank(loc2
);
403 // If two different locations have the same rank, then we can not
404 // deterministicly choose a location.
405 CHECK(loc1_rank
!= loc2_rank
);
407 // Highest rank has highest priority.
408 return (loc1_rank
> loc2_rank
? loc1
: loc2
);
411 void Extension::OverrideLaunchUrl(const GURL
& override_url
) {
412 GURL
new_url(override_url
);
413 if (!new_url
.is_valid()) {
414 DLOG(WARNING
) << "Invalid override url given for " << name();
416 if (new_url
.has_port()) {
417 DLOG(WARNING
) << "Override URL passed for " << name()
418 << " should not contain a port. Removing it.";
420 GURL::Replacements remove_port
;
421 remove_port
.ClearPort();
422 new_url
= new_url
.ReplaceComponents(remove_port
);
425 launch_web_url_
= new_url
.spec();
427 URLPattern
pattern(kValidWebExtentSchemes
);
428 URLPattern::ParseResult result
= pattern
.Parse(new_url
.spec());
429 DCHECK_EQ(result
, URLPattern::PARSE_SUCCESS
);
430 pattern
.SetPath(pattern
.path() + '*');
431 extent_
.AddPattern(pattern
);
435 FilePath
Extension::MaybeNormalizePath(const FilePath
& path
) {
437 // Normalize any drive letter to upper-case. We do this for consistency with
438 // net_utils::FilePathToFileURL(), which does the same thing, to make string
439 // comparisons simpler.
440 std::wstring path_str
= path
.value();
441 if (path_str
.size() >= 2 && path_str
[0] >= L
'a' && path_str
[0] <= L
'z' &&
443 path_str
[0] += ('A' - 'a');
445 return FilePath(path_str
);
451 Extension::Location
Extension::location() const {
452 return manifest_
->location();
455 const std::string
& Extension::id() const {
456 return manifest_
->extension_id();
459 const std::string
Extension::VersionString() const {
460 return version()->GetString();
463 void Extension::AddInstallWarnings(
464 const InstallWarningVector
& new_warnings
) {
465 install_warnings_
.insert(install_warnings_
.end(),
466 new_warnings
.begin(), new_warnings
.end());
470 bool Extension::IsExtension(const FilePath
& file_name
) {
471 return file_name
.MatchesExtension(chrome::kExtensionFileExtension
);
475 bool Extension::IdIsValid(const std::string
& id
) {
476 // Verify that the id is legal.
477 if (id
.size() != (kIdSize
* 2))
480 // We only support lowercase IDs, because IDs can be used as URL components
481 // (where GURL will lowercase it).
482 std::string temp
= StringToLowerASCII(id
);
483 for (size_t i
= 0; i
< temp
.size(); i
++)
484 if (temp
[i
] < 'a' || temp
[i
] > 'p')
491 std::string
Extension::GenerateIdForPath(const FilePath
& path
) {
492 FilePath new_path
= Extension::MaybeNormalizePath(path
);
493 std::string path_bytes
=
494 std::string(reinterpret_cast<const char*>(new_path
.value().data()),
495 new_path
.value().size() * sizeof(FilePath::CharType
));
497 return GenerateId(path_bytes
, &id
) ? id
: "";
500 void Extension::GetBasicInfo(bool enabled
,
501 DictionaryValue
* info
) const {
502 info
->SetString(info_keys::kIdKey
, id());
503 info
->SetString(info_keys::kNameKey
, name());
504 info
->SetBoolean(info_keys::kEnabledKey
, enabled
);
505 info
->SetBoolean(info_keys::kOfflineEnabledKey
, offline_enabled());
506 info
->SetString(info_keys::kVersionKey
, VersionString());
507 info
->SetString(info_keys::kDescriptionKey
, description());
508 info
->SetString(info_keys::kOptionsUrlKey
,
509 options_url().possibly_invalid_spec());
510 info
->SetString(info_keys::kHomepageUrlKey
,
511 GetHomepageURL().possibly_invalid_spec());
512 info
->SetString(info_keys::kDetailsUrlKey
,
513 details_url().possibly_invalid_spec());
514 info
->SetBoolean(info_keys::kPackagedAppKey
, is_platform_app());
517 Extension::Type
Extension::GetType() const {
518 return converted_from_user_script() ? TYPE_USER_SCRIPT
: manifest_
->type();
522 GURL
Extension::GetResourceURL(const GURL
& extension_url
,
523 const std::string
& relative_path
) {
524 DCHECK(extension_url
.SchemeIs(extensions::kExtensionScheme
));
525 DCHECK_EQ("/", extension_url
.path());
527 std::string path
= relative_path
;
529 // If the relative path starts with "/", it is "absolute" relative to the
530 // extension base directory, but extension_url is already specified to refer
531 // to that base directory, so strip the leading "/" if present.
532 if (relative_path
.size() > 0 && relative_path
[0] == '/')
533 path
= relative_path
.substr(1);
535 GURL ret_val
= GURL(extension_url
.spec() + path
);
536 DCHECK(StartsWithASCII(ret_val
.spec(), extension_url
.spec(), false));
541 bool Extension::is_platform_app() const {
542 return manifest_
->is_platform_app();
545 bool Extension::is_hosted_app() const {
546 return manifest()->is_hosted_app();
549 bool Extension::is_legacy_packaged_app() const {
550 return manifest()->is_legacy_packaged_app();
553 bool Extension::is_theme() const {
554 return manifest()->is_theme();
557 GURL
Extension::GetBackgroundURL() const {
558 if (background_scripts_
.empty())
559 return background_url_
;
560 return GetResourceURL(extension_filenames::kGeneratedBackgroundPageFilename
);
563 bool Extension::ResourceMatches(const URLPatternSet
& pattern_set
,
564 const std::string
& resource
) const {
565 return pattern_set
.MatchesURL(extension_url_
.Resolve(resource
));
568 bool Extension::IsResourceWebAccessible(const std::string
& relative_path
)
570 // For old manifest versions which do not specify web_accessible_resources
571 // we always allow resource loads.
572 if (manifest_version_
< 2 && !HasWebAccessibleResources())
575 return ResourceMatches(web_accessible_resources_
, relative_path
);
578 bool Extension::HasWebAccessibleResources() const {
579 return web_accessible_resources_
.size() > 0;
582 bool Extension::IsSandboxedPage(const std::string
& relative_path
) const {
583 return ResourceMatches(sandboxed_pages_
, relative_path
);
587 std::string
Extension::GetResourceContentSecurityPolicy(
588 const std::string
& relative_path
) const {
589 return IsSandboxedPage(relative_path
) ?
590 sandboxed_pages_content_security_policy_
: content_security_policy_
;
593 bool Extension::GenerateId(const std::string
& input
, std::string
* output
) {
595 uint8 hash
[Extension::kIdSize
];
596 crypto::SHA256HashString(input
, hash
, sizeof(hash
));
597 *output
= StringToLowerASCII(base::HexEncode(hash
, sizeof(hash
)));
598 ConvertHexadecimalToIDAlphabet(output
);
603 // Helper method that loads a UserScript object from a dictionary in the
604 // content_script list of the manifest.
605 bool Extension::LoadUserScriptHelper(const DictionaryValue
* content_script
,
606 int definition_index
,
608 UserScript
* result
) {
610 if (content_script
->HasKey(keys::kRunAt
)) {
611 std::string run_location
;
612 if (!content_script
->GetString(keys::kRunAt
, &run_location
)) {
613 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
614 errors::kInvalidRunAt
,
615 base::IntToString(definition_index
));
619 if (run_location
== values::kRunAtDocumentStart
) {
620 result
->set_run_location(UserScript::DOCUMENT_START
);
621 } else if (run_location
== values::kRunAtDocumentEnd
) {
622 result
->set_run_location(UserScript::DOCUMENT_END
);
623 } else if (run_location
== values::kRunAtDocumentIdle
) {
624 result
->set_run_location(UserScript::DOCUMENT_IDLE
);
626 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
627 errors::kInvalidRunAt
,
628 base::IntToString(definition_index
));
634 if (content_script
->HasKey(keys::kAllFrames
)) {
635 bool all_frames
= false;
636 if (!content_script
->GetBoolean(keys::kAllFrames
, &all_frames
)) {
637 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
638 errors::kInvalidAllFrames
, base::IntToString(definition_index
));
641 result
->set_match_all_frames(all_frames
);
644 // matches (required)
645 const ListValue
* matches
= NULL
;
646 if (!content_script
->GetList(keys::kMatches
, &matches
)) {
647 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
648 errors::kInvalidMatches
,
649 base::IntToString(definition_index
));
653 if (matches
->GetSize() == 0) {
654 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
655 errors::kInvalidMatchCount
,
656 base::IntToString(definition_index
));
659 for (size_t j
= 0; j
< matches
->GetSize(); ++j
) {
660 std::string match_str
;
661 if (!matches
->GetString(j
, &match_str
)) {
662 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
663 errors::kInvalidMatch
,
664 base::IntToString(definition_index
),
665 base::IntToString(j
),
666 errors::kExpectString
);
670 URLPattern
pattern(UserScript::kValidUserScriptSchemes
);
671 if (CanExecuteScriptEverywhere())
672 pattern
.SetValidSchemes(URLPattern::SCHEME_ALL
);
674 URLPattern::ParseResult parse_result
= pattern
.Parse(match_str
);
675 if (parse_result
!= URLPattern::PARSE_SUCCESS
) {
676 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
677 errors::kInvalidMatch
,
678 base::IntToString(definition_index
),
679 base::IntToString(j
),
680 URLPattern::GetParseResultString(parse_result
));
684 if (pattern
.MatchesScheme(chrome::kFileScheme
) &&
685 !CanExecuteScriptEverywhere()) {
686 wants_file_access_
= true;
687 if (!(creation_flags_
& ALLOW_FILE_ACCESS
)) {
688 pattern
.SetValidSchemes(
689 pattern
.valid_schemes() & ~URLPattern::SCHEME_FILE
);
693 result
->add_url_pattern(pattern
);
697 if (content_script
->HasKey(keys::kExcludeMatches
)) { // optional
698 const ListValue
* exclude_matches
= NULL
;
699 if (!content_script
->GetList(keys::kExcludeMatches
, &exclude_matches
)) {
700 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
701 errors::kInvalidExcludeMatches
,
702 base::IntToString(definition_index
));
706 for (size_t j
= 0; j
< exclude_matches
->GetSize(); ++j
) {
707 std::string match_str
;
708 if (!exclude_matches
->GetString(j
, &match_str
)) {
709 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
710 errors::kInvalidExcludeMatch
,
711 base::IntToString(definition_index
),
712 base::IntToString(j
),
713 errors::kExpectString
);
717 URLPattern
pattern(UserScript::kValidUserScriptSchemes
);
718 if (CanExecuteScriptEverywhere())
719 pattern
.SetValidSchemes(URLPattern::SCHEME_ALL
);
720 URLPattern::ParseResult parse_result
= pattern
.Parse(match_str
);
721 if (parse_result
!= URLPattern::PARSE_SUCCESS
) {
722 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
723 errors::kInvalidExcludeMatch
,
724 base::IntToString(definition_index
), base::IntToString(j
),
725 URLPattern::GetParseResultString(parse_result
));
729 result
->add_exclude_url_pattern(pattern
);
733 // include/exclude globs (mostly for Greasemonkey compatibility)
734 if (!LoadGlobsHelper(content_script
, definition_index
, keys::kIncludeGlobs
,
735 error
, &UserScript::add_glob
, result
)) {
739 if (!LoadGlobsHelper(content_script
, definition_index
, keys::kExcludeGlobs
,
740 error
, &UserScript::add_exclude_glob
, result
)) {
745 const ListValue
* js
= NULL
;
746 if (content_script
->HasKey(keys::kJs
) &&
747 !content_script
->GetList(keys::kJs
, &js
)) {
748 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
749 errors::kInvalidJsList
,
750 base::IntToString(definition_index
));
754 const ListValue
* css
= NULL
;
755 if (content_script
->HasKey(keys::kCss
) &&
756 !content_script
->GetList(keys::kCss
, &css
)) {
757 *error
= ExtensionErrorUtils::
758 FormatErrorMessageUTF16(errors::kInvalidCssList
,
759 base::IntToString(definition_index
));
763 // The manifest needs to have at least one js or css user script definition.
764 if (((js
? js
->GetSize() : 0) + (css
? css
->GetSize() : 0)) == 0) {
765 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
766 errors::kMissingFile
,
767 base::IntToString(definition_index
));
772 for (size_t script_index
= 0; script_index
< js
->GetSize();
775 std::string relative
;
776 if (!js
->Get(script_index
, &value
) || !value
->GetAsString(&relative
)) {
777 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
779 base::IntToString(definition_index
),
780 base::IntToString(script_index
));
783 GURL url
= GetResourceURL(relative
);
784 ExtensionResource resource
= GetResource(relative
);
785 result
->js_scripts().push_back(UserScript::File(
786 resource
.extension_root(), resource
.relative_path(), url
));
791 for (size_t script_index
= 0; script_index
< css
->GetSize();
794 std::string relative
;
795 if (!css
->Get(script_index
, &value
) || !value
->GetAsString(&relative
)) {
796 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
798 base::IntToString(definition_index
),
799 base::IntToString(script_index
));
802 GURL url
= GetResourceURL(relative
);
803 ExtensionResource resource
= GetResource(relative
);
804 result
->css_scripts().push_back(UserScript::File(
805 resource
.extension_root(), resource
.relative_path(), url
));
812 bool Extension::LoadGlobsHelper(
813 const DictionaryValue
* content_script
,
814 int content_script_index
,
815 const char* globs_property_name
,
817 void(UserScript::*add_method
)(const std::string
& glob
),
818 UserScript
* instance
) {
819 if (!content_script
->HasKey(globs_property_name
))
820 return true; // they are optional
822 const ListValue
* list
= NULL
;
823 if (!content_script
->GetList(globs_property_name
, &list
)) {
824 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
825 errors::kInvalidGlobList
,
826 base::IntToString(content_script_index
),
827 globs_property_name
);
831 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
833 if (!list
->GetString(i
, &glob
)) {
834 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
835 errors::kInvalidGlob
,
836 base::IntToString(content_script_index
),
838 base::IntToString(i
));
842 (instance
->*add_method
)(glob
);
848 scoped_ptr
<Extension::ActionInfo
> Extension::LoadExtensionActionInfoHelper(
849 const DictionaryValue
* extension_action
,
850 ActionInfo::Type action_type
,
852 scoped_ptr
<ActionInfo
> result(new ActionInfo());
854 if (manifest_version_
== 1) {
855 // kPageActionIcons is obsolete, and used by very few extensions. Continue
856 // loading it, but only take the first icon as the default_icon path.
857 const ListValue
* icons
= NULL
;
858 if (extension_action
->HasKey(keys::kPageActionIcons
) &&
859 extension_action
->GetList(keys::kPageActionIcons
, &icons
)) {
860 for (ListValue::const_iterator iter
= icons
->begin();
861 iter
!= icons
->end(); ++iter
) {
863 if (!(*iter
)->GetAsString(&path
) || !NormalizeAndValidatePath(&path
)) {
864 *error
= ASCIIToUTF16(errors::kInvalidPageActionIconPath
);
865 return scoped_ptr
<ActionInfo
>();
868 result
->default_icon
.Add(extension_misc::EXTENSION_ICON_ACTION
, path
);
874 if (extension_action
->HasKey(keys::kPageActionId
)) {
875 if (!extension_action
->GetString(keys::kPageActionId
, &id
)) {
876 *error
= ASCIIToUTF16(errors::kInvalidPageActionId
);
877 return scoped_ptr
<ActionInfo
>();
883 // Read the page action |default_icon| (optional).
884 // The |default_icon| value can be either dictionary {icon size -> icon path}
885 // or non empty string value.
886 if (extension_action
->HasKey(keys::kPageActionDefaultIcon
)) {
887 const DictionaryValue
* icons_value
= NULL
;
888 std::string default_icon
;
889 if (extension_action
->GetDictionary(keys::kPageActionDefaultIcon
,
891 if (!LoadIconsFromDictionary(icons_value
,
892 extension_misc::kExtensionActionIconSizes
,
893 extension_misc::kNumExtensionActionIconSizes
,
894 &result
->default_icon
,
896 return scoped_ptr
<ActionInfo
>();
898 } else if (extension_action
->GetString(keys::kPageActionDefaultIcon
,
900 NormalizeAndValidatePath(&default_icon
)) {
901 result
->default_icon
.Add(extension_misc::EXTENSION_ICON_ACTION
,
904 *error
= ASCIIToUTF16(errors::kInvalidPageActionIconPath
);
905 return scoped_ptr
<ActionInfo
>();
909 // Read the page action title from |default_title| if present, |name| if not
911 if (extension_action
->HasKey(keys::kPageActionDefaultTitle
)) {
912 if (!extension_action
->GetString(keys::kPageActionDefaultTitle
,
913 &result
->default_title
)) {
914 *error
= ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle
);
915 return scoped_ptr
<ActionInfo
>();
917 } else if (manifest_version_
== 1 && extension_action
->HasKey(keys::kName
)) {
918 if (!extension_action
->GetString(keys::kName
, &result
->default_title
)) {
919 *error
= ASCIIToUTF16(errors::kInvalidPageActionName
);
920 return scoped_ptr
<ActionInfo
>();
924 // Read the action's |popup| (optional).
925 const char* popup_key
= NULL
;
926 if (extension_action
->HasKey(keys::kPageActionDefaultPopup
))
927 popup_key
= keys::kPageActionDefaultPopup
;
929 if (manifest_version_
== 1 &&
930 extension_action
->HasKey(keys::kPageActionPopup
)) {
932 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
933 errors::kInvalidPageActionOldAndNewKeys
,
934 keys::kPageActionDefaultPopup
,
935 keys::kPageActionPopup
);
936 return scoped_ptr
<ActionInfo
>();
938 popup_key
= keys::kPageActionPopup
;
942 const DictionaryValue
* popup
= NULL
;
945 if (extension_action
->GetString(popup_key
, &url_str
)) {
946 // On success, |url_str| is set. Nothing else to do.
947 } else if (manifest_version_
== 1 &&
948 extension_action
->GetDictionary(popup_key
, &popup
)) {
949 if (!popup
->GetString(keys::kPageActionPopupPath
, &url_str
)) {
950 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
951 errors::kInvalidPageActionPopupPath
, "<missing>");
952 return scoped_ptr
<ActionInfo
>();
955 *error
= ASCIIToUTF16(errors::kInvalidPageActionPopup
);
956 return scoped_ptr
<ActionInfo
>();
959 if (!url_str
.empty()) {
960 // An empty string is treated as having no popup.
961 result
->default_popup_url
= GetResourceURL(url_str
);
962 if (!result
->default_popup_url
.is_valid()) {
963 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
964 errors::kInvalidPageActionPopupPath
, url_str
);
965 return scoped_ptr
<ActionInfo
>();
968 DCHECK(result
->default_popup_url
.is_empty())
969 << "Shouldn't be possible for the popup to be set.";
973 return result
.Pass();
977 bool Extension::InitExtensionID(extensions::Manifest
* manifest
,
978 const FilePath
& path
,
979 const std::string
& explicit_id
,
982 if (!explicit_id
.empty()) {
983 manifest
->set_extension_id(explicit_id
);
987 if (manifest
->HasKey(keys::kPublicKey
)) {
988 std::string public_key
;
989 std::string public_key_bytes
;
990 std::string extension_id
;
991 if (!manifest
->GetString(keys::kPublicKey
, &public_key
) ||
992 !ParsePEMKeyBytes(public_key
, &public_key_bytes
) ||
993 !GenerateId(public_key_bytes
, &extension_id
)) {
994 *error
= ASCIIToUTF16(errors::kInvalidKey
);
997 manifest
->set_extension_id(extension_id
);
1001 if (creation_flags
& REQUIRE_KEY
) {
1002 *error
= ASCIIToUTF16(errors::kInvalidKey
);
1005 // If there is a path, we generate the ID from it. This is useful for
1006 // development mode, because it keeps the ID stable across restarts and
1007 // reloading the extension.
1008 std::string extension_id
= GenerateIdForPath(path
);
1009 if (extension_id
.empty()) {
1010 NOTREACHED() << "Could not create ID from path.";
1013 manifest
->set_extension_id(extension_id
);
1018 bool Extension::LoadRequiredFeatures(string16
* error
) {
1019 if (!LoadName(error
) ||
1020 !LoadVersion(error
))
1025 bool Extension::LoadName(string16
* error
) {
1026 string16 localized_name
;
1027 if (!manifest_
->GetString(keys::kName
, &localized_name
)) {
1028 *error
= ASCIIToUTF16(errors::kInvalidName
);
1031 non_localized_name_
= UTF16ToUTF8(localized_name
);
1032 base::i18n::AdjustStringForLocaleDirection(&localized_name
);
1033 name_
= UTF16ToUTF8(localized_name
);
1037 bool Extension::LoadDescription(string16
* error
) {
1038 if (manifest_
->HasKey(keys::kDescription
) &&
1039 !manifest_
->GetString(keys::kDescription
, &description_
)) {
1040 *error
= ASCIIToUTF16(errors::kInvalidDescription
);
1046 bool Extension::LoadAppFeatures(string16
* error
) {
1047 if (!LoadExtent(keys::kWebURLs
, &extent_
,
1048 errors::kInvalidWebURLs
, errors::kInvalidWebURL
, error
) ||
1049 !LoadLaunchURL(error
) ||
1050 !LoadLaunchContainer(error
)) {
1053 if (manifest_
->HasKey(keys::kDisplayInLauncher
) &&
1054 !manifest_
->GetBoolean(keys::kDisplayInLauncher
, &display_in_launcher_
)) {
1055 *error
= ASCIIToUTF16(errors::kInvalidDisplayInLauncher
);
1058 if (manifest_
->HasKey(keys::kDisplayInNewTabPage
)) {
1059 if (!manifest_
->GetBoolean(keys::kDisplayInNewTabPage
,
1060 &display_in_new_tab_page_
)) {
1061 *error
= ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage
);
1065 // Inherit default from display_in_launcher property.
1066 display_in_new_tab_page_
= display_in_launcher_
;
1071 bool Extension::LoadOAuth2Info(string16
* error
) {
1072 if (!manifest_
->HasKey(keys::kOAuth2
))
1075 if (!manifest_
->GetString(keys::kOAuth2ClientId
, &oauth2_info_
.client_id
) ||
1076 oauth2_info_
.client_id
.empty()) {
1077 *error
= ASCIIToUTF16(errors::kInvalidOAuth2ClientId
);
1081 ListValue
* list
= NULL
;
1082 if (!manifest_
->GetList(keys::kOAuth2Scopes
, &list
)) {
1083 *error
= ASCIIToUTF16(errors::kInvalidOAuth2Scopes
);
1087 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
1089 if (!list
->GetString(i
, &scope
)) {
1090 *error
= ASCIIToUTF16(errors::kInvalidOAuth2Scopes
);
1093 oauth2_info_
.scopes
.push_back(scope
);
1099 bool Extension::LoadExtent(const char* key
,
1100 URLPatternSet
* extent
,
1101 const char* list_error
,
1102 const char* value_error
,
1104 Value
* temp_pattern_value
= NULL
;
1105 if (!manifest_
->Get(key
, &temp_pattern_value
))
1108 if (temp_pattern_value
->GetType() != Value::TYPE_LIST
) {
1109 *error
= ASCIIToUTF16(list_error
);
1113 ListValue
* pattern_list
= static_cast<ListValue
*>(temp_pattern_value
);
1114 for (size_t i
= 0; i
< pattern_list
->GetSize(); ++i
) {
1115 std::string pattern_string
;
1116 if (!pattern_list
->GetString(i
, &pattern_string
)) {
1117 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(value_error
,
1118 base::UintToString(i
),
1119 errors::kExpectString
);
1123 URLPattern
pattern(kValidWebExtentSchemes
);
1124 URLPattern::ParseResult parse_result
= pattern
.Parse(pattern_string
);
1125 if (parse_result
== URLPattern::PARSE_ERROR_EMPTY_PATH
) {
1126 pattern_string
+= "/";
1127 parse_result
= pattern
.Parse(pattern_string
);
1130 if (parse_result
!= URLPattern::PARSE_SUCCESS
) {
1131 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1133 base::UintToString(i
),
1134 URLPattern::GetParseResultString(parse_result
));
1138 // Do not allow authors to claim "<all_urls>".
1139 if (pattern
.match_all_urls()) {
1140 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1142 base::UintToString(i
),
1143 errors::kCannotClaimAllURLsInExtent
);
1147 // Do not allow authors to claim "*" for host.
1148 if (pattern
.host().empty()) {
1149 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1151 base::UintToString(i
),
1152 errors::kCannotClaimAllHostsInExtent
);
1156 // We do not allow authors to put wildcards in their paths. Instead, we
1157 // imply one at the end.
1158 if (pattern
.path().find('*') != std::string::npos
) {
1159 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1161 base::UintToString(i
),
1162 errors::kNoWildCardsInPaths
);
1165 pattern
.SetPath(pattern
.path() + '*');
1167 extent
->AddPattern(pattern
);
1173 bool Extension::LoadLaunchURL(string16
* error
) {
1176 // launch URL can be either local (to chrome-extension:// root) or an absolute
1178 if (manifest_
->Get(keys::kLaunchLocalPath
, &temp
)) {
1179 if (manifest_
->Get(keys::kLaunchWebURL
, NULL
)) {
1180 *error
= ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive
);
1184 if (manifest_
->Get(keys::kWebURLs
, NULL
)) {
1185 *error
= ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive
);
1189 std::string launch_path
;
1190 if (!temp
->GetAsString(&launch_path
)) {
1191 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1192 errors::kInvalidLaunchValue
,
1193 keys::kLaunchLocalPath
);
1197 // Ensure the launch path is a valid relative URL.
1198 GURL resolved
= url().Resolve(launch_path
);
1199 if (!resolved
.is_valid() || resolved
.GetOrigin() != url()) {
1200 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1201 errors::kInvalidLaunchValue
,
1202 keys::kLaunchLocalPath
);
1206 launch_local_path_
= launch_path
;
1207 } else if (manifest_
->Get(keys::kLaunchWebURL
, &temp
)) {
1208 std::string launch_url
;
1209 if (!temp
->GetAsString(&launch_url
)) {
1210 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1211 errors::kInvalidLaunchValue
,
1212 keys::kLaunchWebURL
);
1216 // Ensure the launch URL is a valid absolute URL and web extent scheme.
1217 GURL
url(launch_url
);
1218 URLPattern
pattern(kValidWebExtentSchemes
);
1219 if (!url
.is_valid() || !pattern
.SetScheme(url
.scheme())) {
1220 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1221 errors::kInvalidLaunchValue
,
1222 keys::kLaunchWebURL
);
1226 launch_web_url_
= launch_url
;
1227 } else if (is_legacy_packaged_app() || is_hosted_app()) {
1228 *error
= ASCIIToUTF16(errors::kLaunchURLRequired
);
1232 // If there is no extent, we default the extent based on the launch URL.
1233 if (web_extent().is_empty() && !launch_web_url().empty()) {
1234 GURL
launch_url(launch_web_url());
1235 URLPattern
pattern(kValidWebExtentSchemes
);
1236 if (!pattern
.SetScheme("*")) {
1237 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1238 errors::kInvalidLaunchValue
,
1239 keys::kLaunchWebURL
);
1242 pattern
.SetHost(launch_url
.host());
1243 pattern
.SetPath("/*");
1244 extent_
.AddPattern(pattern
);
1247 // In order for the --apps-gallery-url switch to work with the gallery
1248 // process isolation, we must insert any provided value into the component
1249 // app's launch url and web extent.
1250 if (id() == extension_misc::kWebStoreAppId
) {
1251 std::string gallery_url_str
= CommandLine::ForCurrentProcess()->
1252 GetSwitchValueASCII(switches::kAppsGalleryURL
);
1254 // Empty string means option was not used.
1255 if (!gallery_url_str
.empty()) {
1256 GURL
gallery_url(gallery_url_str
);
1257 OverrideLaunchUrl(gallery_url
);
1259 } else if (id() == extension_misc::kCloudPrintAppId
) {
1260 // In order for the --cloud-print-service switch to work, we must update
1261 // the launch URL and web extent.
1262 // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is
1263 // currently under chrome/browser.
1264 const CommandLine
& command_line
= *CommandLine::ForCurrentProcess();
1265 GURL cloud_print_service_url
= GURL(command_line
.GetSwitchValueASCII(
1266 switches::kCloudPrintServiceURL
));
1267 if (!cloud_print_service_url
.is_empty()) {
1269 cloud_print_service_url
.path() + "/enable_chrome_connector");
1270 GURL::Replacements replacements
;
1271 replacements
.SetPathStr(path
);
1272 GURL cloud_print_enable_connector_url
=
1273 cloud_print_service_url
.ReplaceComponents(replacements
);
1274 OverrideLaunchUrl(cloud_print_enable_connector_url
);
1276 } else if (id() == extension_misc::kChromeAppId
) {
1277 // Override launch url to new tab.
1278 launch_web_url_
= chrome::kChromeUINewTabURL
;
1279 extent_
.ClearPatterns();
1285 bool Extension::LoadLaunchContainer(string16
* error
) {
1286 Value
* tmp_launcher_container
= NULL
;
1287 if (!manifest_
->Get(keys::kLaunchContainer
, &tmp_launcher_container
))
1290 std::string launch_container_string
;
1291 if (!tmp_launcher_container
->GetAsString(&launch_container_string
)) {
1292 *error
= ASCIIToUTF16(errors::kInvalidLaunchContainer
);
1296 if (launch_container_string
== values::kLaunchContainerPanel
) {
1297 launch_container_
= extension_misc::LAUNCH_PANEL
;
1298 } else if (launch_container_string
== values::kLaunchContainerTab
) {
1299 launch_container_
= extension_misc::LAUNCH_TAB
;
1301 *error
= ASCIIToUTF16(errors::kInvalidLaunchContainer
);
1305 bool can_specify_initial_size
=
1306 launch_container_
== extension_misc::LAUNCH_PANEL
||
1307 launch_container_
== extension_misc::LAUNCH_WINDOW
;
1309 // Validate the container width if present.
1310 if (!ReadLaunchDimension(manifest_
.get(),
1313 can_specify_initial_size
,
1318 // Validate container height if present.
1319 if (!ReadLaunchDimension(manifest_
.get(),
1320 keys::kLaunchHeight
,
1322 can_specify_initial_size
,
1330 bool Extension::LoadSharedFeatures(
1331 const APIPermissionSet
& api_permissions
,
1333 if (!LoadDescription(error
) ||
1334 !LoadHomepageURL(error
) ||
1335 !LoadUpdateURL(error
) ||
1336 !LoadIcons(error
) ||
1337 !LoadCommands(error
) ||
1338 !LoadPlugins(error
) ||
1339 !LoadNaClModules(error
) ||
1340 !LoadWebAccessibleResources(error
) ||
1341 !LoadSandboxedPages(error
) ||
1342 !LoadRequirements(error
) ||
1343 !LoadDefaultLocale(error
) ||
1344 !LoadOfflineEnabled(error
) ||
1345 !LoadOptionsPage(error
) ||
1346 // LoadBackgroundScripts() must be called before LoadBackgroundPage().
1347 !LoadBackgroundScripts(error
) ||
1348 !LoadBackgroundPage(api_permissions
, error
) ||
1349 !LoadBackgroundPersistent(api_permissions
, error
) ||
1350 !LoadBackgroundAllowJSAccess(api_permissions
, error
) ||
1351 !LoadWebIntentServices(error
) ||
1352 !LoadOAuth2Info(error
))
1358 bool Extension::LoadVersion(string16
* error
) {
1359 std::string version_str
;
1360 if (!manifest_
->GetString(keys::kVersion
, &version_str
)) {
1361 *error
= ASCIIToUTF16(errors::kInvalidVersion
);
1364 version_
.reset(new Version(version_str
));
1365 if (!version_
->IsValid() || version_
->components().size() > 4) {
1366 *error
= ASCIIToUTF16(errors::kInvalidVersion
);
1372 bool Extension::LoadManifestVersion(string16
* error
) {
1373 // Get the original value out of the dictionary so that we can validate it
1375 if (manifest_
->value()->HasKey(keys::kManifestVersion
)) {
1376 int manifest_version
= 1;
1377 if (!manifest_
->GetInteger(keys::kManifestVersion
, &manifest_version
) ||
1378 manifest_version
< 1) {
1379 *error
= ASCIIToUTF16(errors::kInvalidManifestVersion
);
1384 manifest_version_
= manifest_
->GetManifestVersion();
1385 if (creation_flags_
& REQUIRE_MODERN_MANIFEST_VERSION
&&
1386 manifest_version_
< kModernManifestVersion
&&
1387 !CommandLine::ForCurrentProcess()->HasSwitch(
1388 switches::kAllowLegacyExtensionManifests
)) {
1389 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1390 errors::kInvalidManifestVersionOld
,
1391 base::IntToString(kModernManifestVersion
));
1398 bool Extension::LoadHomepageURL(string16
* error
) {
1399 if (!manifest_
->HasKey(keys::kHomepageURL
))
1401 std::string tmp_homepage_url
;
1402 if (!manifest_
->GetString(keys::kHomepageURL
, &tmp_homepage_url
)) {
1403 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1404 errors::kInvalidHomepageURL
, "");
1407 homepage_url_
= GURL(tmp_homepage_url
);
1408 if (!homepage_url_
.is_valid() ||
1409 (!homepage_url_
.SchemeIs("http") &&
1410 !homepage_url_
.SchemeIs("https"))) {
1411 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1412 errors::kInvalidHomepageURL
, tmp_homepage_url
);
1418 bool Extension::LoadUpdateURL(string16
* error
) {
1419 if (!manifest_
->HasKey(keys::kUpdateURL
))
1421 std::string tmp_update_url
;
1422 if (!manifest_
->GetString(keys::kUpdateURL
, &tmp_update_url
)) {
1423 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1424 errors::kInvalidUpdateURL
, "");
1427 update_url_
= GURL(tmp_update_url
);
1428 if (!update_url_
.is_valid() ||
1429 update_url_
.has_ref()) {
1430 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1431 errors::kInvalidUpdateURL
, tmp_update_url
);
1437 bool Extension::LoadIcons(string16
* error
) {
1438 if (!manifest_
->HasKey(keys::kIcons
))
1440 DictionaryValue
* icons_value
= NULL
;
1441 if (!manifest_
->GetDictionary(keys::kIcons
, &icons_value
)) {
1442 *error
= ASCIIToUTF16(errors::kInvalidIcons
);
1446 return LoadIconsFromDictionary(icons_value
,
1447 extension_misc::kExtensionIconSizes
,
1448 extension_misc::kNumExtensionIconSizes
,
1453 bool Extension::LoadCommands(string16
* error
) {
1454 if (manifest_
->HasKey(keys::kCommands
)) {
1455 DictionaryValue
* commands
= NULL
;
1456 if (!manifest_
->GetDictionary(keys::kCommands
, &commands
)) {
1457 *error
= ASCIIToUTF16(errors::kInvalidCommandsKey
);
1461 if (commands
->size() > kMaxCommandsPerExtension
) {
1462 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1463 errors::kInvalidKeyBindingTooMany
,
1464 base::IntToString(kMaxCommandsPerExtension
));
1468 int command_index
= 0;
1469 for (DictionaryValue::key_iterator iter
= commands
->begin_keys();
1470 iter
!= commands
->end_keys(); ++iter
) {
1473 DictionaryValue
* command
= NULL
;
1474 if (!commands
->GetDictionary(*iter
, &command
)) {
1475 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1476 errors::kInvalidKeyBindingDictionary
,
1477 base::IntToString(command_index
));
1481 scoped_ptr
<extensions::Command
> binding(new extensions::Command());
1482 if (!binding
->Parse(command
, *iter
, command_index
, error
))
1483 return false; // |error| already set.
1485 std::string command_name
= binding
->command_name();
1486 if (command_name
== values::kPageActionCommandEvent
) {
1487 page_action_command_
.reset(binding
.release());
1488 } else if (command_name
== values::kBrowserActionCommandEvent
) {
1489 browser_action_command_
.reset(binding
.release());
1490 } else if (command_name
== values::kScriptBadgeCommandEvent
) {
1491 script_badge_command_
.reset(binding
.release());
1493 if (command_name
[0] != '_') // All commands w/underscore are reserved.
1494 named_commands_
[command_name
] = *binding
.get();
1499 if (manifest_
->HasKey(keys::kBrowserAction
) &&
1500 !browser_action_command_
.get()) {
1501 // If the extension defines a browser action, but no command for it, then
1502 // we synthesize a generic one, so the user can configure a shortcut for it.
1503 // No keyboard shortcut will be assigned to it, until the user selects one.
1504 browser_action_command_
.reset(
1505 new extensions::Command(
1506 values::kBrowserActionCommandEvent
, string16(), ""));
1512 bool Extension::LoadPlugins(string16
* error
) {
1513 if (!manifest_
->HasKey(keys::kPlugins
))
1516 ListValue
* list_value
= NULL
;
1517 if (!manifest_
->GetList(keys::kPlugins
, &list_value
)) {
1518 *error
= ASCIIToUTF16(errors::kInvalidPlugins
);
1522 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
1523 DictionaryValue
* plugin_value
= NULL
;
1524 if (!list_value
->GetDictionary(i
, &plugin_value
)) {
1525 *error
= ASCIIToUTF16(errors::kInvalidPlugins
);
1528 // Get plugins[i].path.
1529 std::string path_str
;
1530 if (!plugin_value
->GetString(keys::kPluginsPath
, &path_str
)) {
1531 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1532 errors::kInvalidPluginsPath
, base::IntToString(i
));
1536 // Get plugins[i].content (optional).
1537 bool is_public
= false;
1538 if (plugin_value
->HasKey(keys::kPluginsPublic
)) {
1539 if (!plugin_value
->GetBoolean(keys::kPluginsPublic
, &is_public
)) {
1540 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1541 errors::kInvalidPluginsPublic
, base::IntToString(i
));
1546 // We don't allow extensions to load NPAPI plugins on Chrome OS, or under
1547 // Windows 8 Metro mode, but still parse the entries to display consistent
1548 // error messages. If the extension actually requires the plugins then
1549 // LoadRequirements will prevent it loading.
1550 #if defined(OS_CHROMEOS)
1552 #elif defined(OS_WIN)
1553 if (base::win::IsMetroProcess()) {
1556 #endif // defined(OS_WIN).
1557 plugins_
.push_back(PluginInfo());
1558 plugins_
.back().path
= path().Append(FilePath::FromUTF8Unsafe(path_str
));
1559 plugins_
.back().is_public
= is_public
;
1564 bool Extension::LoadNaClModules(string16
* error
) {
1565 if (!manifest_
->HasKey(keys::kNaClModules
))
1567 ListValue
* list_value
= NULL
;
1568 if (!manifest_
->GetList(keys::kNaClModules
, &list_value
)) {
1569 *error
= ASCIIToUTF16(errors::kInvalidNaClModules
);
1573 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
1574 DictionaryValue
* module_value
= NULL
;
1575 if (!list_value
->GetDictionary(i
, &module_value
)) {
1576 *error
= ASCIIToUTF16(errors::kInvalidNaClModules
);
1580 // Get nacl_modules[i].path.
1581 std::string path_str
;
1582 if (!module_value
->GetString(keys::kNaClModulesPath
, &path_str
)) {
1583 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1584 errors::kInvalidNaClModulesPath
, base::IntToString(i
));
1588 // Get nacl_modules[i].mime_type.
1589 std::string mime_type
;
1590 if (!module_value
->GetString(keys::kNaClModulesMIMEType
, &mime_type
)) {
1591 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1592 errors::kInvalidNaClModulesMIMEType
, base::IntToString(i
));
1596 nacl_modules_
.push_back(NaClModuleInfo());
1597 nacl_modules_
.back().url
= GetResourceURL(path_str
);
1598 nacl_modules_
.back().mime_type
= mime_type
;
1604 bool Extension::LoadWebAccessibleResources(string16
* error
) {
1605 if (!manifest_
->HasKey(keys::kWebAccessibleResources
))
1607 ListValue
* list_value
= NULL
;
1608 if (!manifest_
->GetList(keys::kWebAccessibleResources
, &list_value
)) {
1609 *error
= ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList
);
1612 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
1613 std::string relative_path
;
1614 if (!list_value
->GetString(i
, &relative_path
)) {
1615 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1616 errors::kInvalidWebAccessibleResource
, base::IntToString(i
));
1619 URLPattern
pattern(URLPattern::SCHEME_EXTENSION
);
1620 if (pattern
.Parse(extension_url_
.spec()) != URLPattern::PARSE_SUCCESS
) {
1621 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1622 errors::kInvalidURLPatternError
, extension_url_
.spec());
1625 while (relative_path
[0] == '/')
1626 relative_path
= relative_path
.substr(1, relative_path
.length() - 1);
1627 pattern
.SetPath(pattern
.path() + relative_path
);
1628 web_accessible_resources_
.AddPattern(pattern
);
1634 bool Extension::LoadSandboxedPages(string16
* error
) {
1635 if (!manifest_
->HasPath(keys::kSandboxedPages
))
1638 ListValue
* list_value
= NULL
;
1639 if (!manifest_
->GetList(keys::kSandboxedPages
, &list_value
)) {
1640 *error
= ASCIIToUTF16(errors::kInvalidSandboxedPagesList
);
1643 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
1644 std::string relative_path
;
1645 if (!list_value
->GetString(i
, &relative_path
)) {
1646 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1647 errors::kInvalidSandboxedPage
, base::IntToString(i
));
1650 URLPattern
pattern(URLPattern::SCHEME_EXTENSION
);
1651 if (pattern
.Parse(extension_url_
.spec()) != URLPattern::PARSE_SUCCESS
) {
1652 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1653 errors::kInvalidURLPatternError
, extension_url_
.spec());
1656 while (relative_path
[0] == '/')
1657 relative_path
= relative_path
.substr(1, relative_path
.length() - 1);
1658 pattern
.SetPath(pattern
.path() + relative_path
);
1659 sandboxed_pages_
.AddPattern(pattern
);
1662 if (manifest_
->HasPath(keys::kSandboxedPagesCSP
)) {
1663 if (!manifest_
->GetString(
1664 keys::kSandboxedPagesCSP
, &sandboxed_pages_content_security_policy_
)) {
1665 *error
= ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP
);
1669 if (!ContentSecurityPolicyIsLegal(
1670 sandboxed_pages_content_security_policy_
) ||
1671 !ContentSecurityPolicyIsSandboxed(
1672 sandboxed_pages_content_security_policy_
, GetType())) {
1673 *error
= ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP
);
1677 sandboxed_pages_content_security_policy_
=
1678 kDefaultSandboxedPageContentSecurityPolicy
;
1679 CHECK(ContentSecurityPolicyIsSandboxed(
1680 sandboxed_pages_content_security_policy_
, GetType()));
1686 bool Extension::LoadRequirements(string16
* error
) {
1687 // Before parsing requirements from the manifest, automatically default the
1688 // NPAPI plugin requirement based on whether it includes NPAPI plugins.
1689 ListValue
* list_value
= NULL
;
1690 requirements_
.npapi
=
1691 manifest_
->GetList(keys::kPlugins
, &list_value
) && !list_value
->empty();
1693 if (!manifest_
->HasKey(keys::kRequirements
))
1696 DictionaryValue
* requirements_value
= NULL
;
1697 if (!manifest_
->GetDictionary(keys::kRequirements
, &requirements_value
)) {
1698 *error
= ASCIIToUTF16(errors::kInvalidRequirements
);
1702 for (DictionaryValue::key_iterator it
= requirements_value
->begin_keys();
1703 it
!= requirements_value
->end_keys(); ++it
) {
1704 DictionaryValue
* requirement_value
;
1705 if (!requirements_value
->GetDictionaryWithoutPathExpansion(
1706 *it
, &requirement_value
)) {
1707 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1708 errors::kInvalidRequirement
, *it
);
1712 if (*it
== "plugins") {
1713 for (DictionaryValue::key_iterator plugin_it
=
1714 requirement_value
->begin_keys();
1715 plugin_it
!= requirement_value
->end_keys(); ++plugin_it
) {
1716 bool plugin_required
= false;
1717 if (!requirement_value
->GetBoolean(*plugin_it
, &plugin_required
)) {
1718 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1719 errors::kInvalidRequirement
, *it
);
1722 if (*plugin_it
== "npapi") {
1723 requirements_
.npapi
= plugin_required
;
1725 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1726 errors::kInvalidRequirement
, *it
);
1730 } else if (*it
== "3D") {
1731 ListValue
* features
= NULL
;
1732 if (!requirement_value
->GetListWithoutPathExpansion("features",
1735 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1736 errors::kInvalidRequirement
, *it
);
1740 for (base::ListValue::iterator feature_it
= features
->begin();
1741 feature_it
!= features
->end();
1743 std::string feature
;
1744 if ((*feature_it
)->GetAsString(&feature
)) {
1745 if (feature
== "webgl") {
1746 requirements_
.webgl
= true;
1747 } else if (feature
== "css3d") {
1748 requirements_
.css3d
= true;
1750 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1751 errors::kInvalidRequirement
, *it
);
1757 *error
= ASCIIToUTF16(errors::kInvalidRequirements
);
1764 bool Extension::LoadDefaultLocale(string16
* error
) {
1765 if (!manifest_
->HasKey(keys::kDefaultLocale
))
1767 if (!manifest_
->GetString(keys::kDefaultLocale
, &default_locale_
) ||
1768 !l10n_util::IsValidLocaleSyntax(default_locale_
)) {
1769 *error
= ASCIIToUTF16(errors::kInvalidDefaultLocale
);
1775 bool Extension::LoadOfflineEnabled(string16
* error
) {
1776 // Defaults to false, except for platform apps which are offline by default.
1777 if (!manifest_
->HasKey(keys::kOfflineEnabled
)) {
1778 offline_enabled_
= is_platform_app();
1781 if (!manifest_
->GetBoolean(keys::kOfflineEnabled
, &offline_enabled_
)) {
1782 *error
= ASCIIToUTF16(errors::kInvalidOfflineEnabled
);
1788 bool Extension::LoadOptionsPage(string16
* error
) {
1789 if (!manifest_
->HasKey(keys::kOptionsPage
))
1791 std::string options_str
;
1792 if (!manifest_
->GetString(keys::kOptionsPage
, &options_str
)) {
1793 *error
= ASCIIToUTF16(errors::kInvalidOptionsPage
);
1797 if (is_hosted_app()) {
1798 // hosted apps require an absolute URL.
1799 GURL
options_url(options_str
);
1800 if (!options_url
.is_valid() ||
1801 !(options_url
.SchemeIs("http") || options_url
.SchemeIs("https"))) {
1802 *error
= ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp
);
1805 options_url_
= options_url
;
1807 GURL
absolute(options_str
);
1808 if (absolute
.is_valid()) {
1809 *error
= ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage
);
1812 options_url_
= GetResourceURL(options_str
);
1813 if (!options_url_
.is_valid()) {
1814 *error
= ASCIIToUTF16(errors::kInvalidOptionsPage
);
1822 bool Extension::LoadBackgroundScripts(string16
* error
) {
1823 const std::string
& key
= is_platform_app() ?
1824 keys::kPlatformAppBackgroundScripts
: keys::kBackgroundScripts
;
1825 return LoadBackgroundScripts(key
, error
);
1828 bool Extension::LoadBackgroundScripts(const std::string
& key
, string16
* error
) {
1829 Value
* background_scripts_value
= NULL
;
1830 if (!manifest_
->Get(key
, &background_scripts_value
))
1833 CHECK(background_scripts_value
);
1834 if (background_scripts_value
->GetType() != Value::TYPE_LIST
) {
1835 *error
= ASCIIToUTF16(errors::kInvalidBackgroundScripts
);
1839 ListValue
* background_scripts
=
1840 static_cast<ListValue
*>(background_scripts_value
);
1841 for (size_t i
= 0; i
< background_scripts
->GetSize(); ++i
) {
1843 if (!background_scripts
->GetString(i
, &script
)) {
1844 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1845 errors::kInvalidBackgroundScript
, base::IntToString(i
));
1848 background_scripts_
.push_back(script
);
1854 bool Extension::LoadBackgroundPage(
1855 const APIPermissionSet
& api_permissions
,
1857 if (is_platform_app()) {
1858 return LoadBackgroundPage(
1859 keys::kPlatformAppBackgroundPage
, api_permissions
, error
);
1862 if (!LoadBackgroundPage(keys::kBackgroundPage
, api_permissions
, error
))
1864 if (background_url_
.is_empty()) {
1865 return LoadBackgroundPage(
1866 keys::kBackgroundPageLegacy
, api_permissions
, error
);
1871 bool Extension::LoadBackgroundPage(
1872 const std::string
& key
,
1873 const APIPermissionSet
& api_permissions
,
1875 base::Value
* background_page_value
= NULL
;
1876 if (!manifest_
->Get(key
, &background_page_value
))
1879 if (!background_scripts_
.empty()) {
1880 *error
= ASCIIToUTF16(errors::kInvalidBackgroundCombination
);
1885 std::string background_str
;
1886 if (!background_page_value
->GetAsString(&background_str
)) {
1887 *error
= ASCIIToUTF16(errors::kInvalidBackground
);
1891 if (is_hosted_app()) {
1892 background_url_
= GURL(background_str
);
1894 // Make sure "background" permission is set.
1895 if (!api_permissions
.count(APIPermission::kBackground
)) {
1896 *error
= ASCIIToUTF16(errors::kBackgroundPermissionNeeded
);
1899 // Hosted apps require an absolute URL.
1900 if (!background_url_
.is_valid()) {
1901 *error
= ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp
);
1905 if (!(background_url_
.SchemeIs("https") ||
1906 (CommandLine::ForCurrentProcess()->HasSwitch(
1907 switches::kAllowHTTPBackgroundPage
) &&
1908 background_url_
.SchemeIs("http")))) {
1909 *error
= ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp
);
1913 background_url_
= GetResourceURL(background_str
);
1919 bool Extension::LoadBackgroundPersistent(
1920 const APIPermissionSet
& api_permissions
,
1922 if (is_platform_app()) {
1923 background_page_is_persistent_
= false;
1927 Value
* background_persistent
= NULL
;
1928 if (!manifest_
->Get(keys::kBackgroundPersistent
, &background_persistent
))
1931 if (!background_persistent
->GetAsBoolean(&background_page_is_persistent_
)) {
1932 *error
= ASCIIToUTF16(errors::kInvalidBackgroundPersistent
);
1936 if (!has_background_page()) {
1937 *error
= ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage
);
1944 bool Extension::LoadBackgroundAllowJSAccess(
1945 const APIPermissionSet
& api_permissions
,
1947 Value
* allow_js_access
= NULL
;
1948 if (!manifest_
->Get(keys::kBackgroundAllowJsAccess
, &allow_js_access
))
1951 if (!allow_js_access
->IsType(Value::TYPE_BOOLEAN
) ||
1952 !allow_js_access
->GetAsBoolean(&allow_background_js_access_
)) {
1953 *error
= ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess
);
1960 bool Extension::LoadWebIntentAction(const std::string
& action_name
,
1961 const DictionaryValue
& intent_service
,
1964 webkit_glue::WebIntentServiceData service
;
1967 service
.action
= UTF8ToUTF16(action_name
);
1969 const ListValue
* mime_types
= NULL
;
1970 if (!intent_service
.HasKey(keys::kIntentType
) ||
1971 !intent_service
.GetList(keys::kIntentType
, &mime_types
) ||
1972 mime_types
->GetSize() == 0) {
1973 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1974 errors::kInvalidIntentType
, action_name
);
1979 if (intent_service
.HasKey(keys::kIntentPath
)) {
1980 if (!intent_service
.GetString(keys::kIntentPath
, &href
)) {
1981 *error
= ASCIIToUTF16(errors::kInvalidIntentHref
);
1986 if (intent_service
.HasKey(keys::kIntentHref
)) {
1987 if (!href
.empty()) {
1988 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
1989 errors::kInvalidIntentHrefOldAndNewKey
, action_name
,
1990 keys::kIntentPath
, keys::kIntentHref
);
1993 if (!intent_service
.GetString(keys::kIntentHref
, &href
)) {
1994 *error
= ASCIIToUTF16(errors::kInvalidIntentHref
);
1999 // For packaged/hosted apps, empty href implies the respective launch URLs.
2001 if (is_hosted_app()) {
2002 href
= launch_web_url();
2003 } else if (is_legacy_packaged_app()) {
2004 href
= launch_local_path();
2008 // If there still is not an href, the manifest is malformed, unless this is a
2009 // platform app in which case the href should not be present.
2010 if (href
.empty() && !is_platform_app()) {
2011 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2012 errors::kInvalidIntentHrefEmpty
, action_name
);
2014 } else if (!href
.empty() && is_platform_app()) {
2015 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2016 errors::kInvalidIntentHrefInPlatformApp
, action_name
);
2020 GURL
service_url(href
);
2021 if (is_hosted_app()) {
2022 // Hosted apps require an absolute URL for intents.
2023 if (!service_url
.is_valid() ||
2024 !(web_extent().MatchesURL(service_url
))) {
2025 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2026 errors::kInvalidIntentPageInHostedApp
, action_name
);
2029 service
.service_url
= service_url
;
2030 } else if (is_platform_app()) {
2031 service
.service_url
= GetBackgroundURL();
2033 // We do not allow absolute intent URLs in non-hosted apps.
2034 if (service_url
.is_valid()) {
2035 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2036 errors::kCannotAccessPage
, href
);
2039 service
.service_url
= GetResourceURL(href
);
2042 if (intent_service
.HasKey(keys::kIntentTitle
) &&
2043 !intent_service
.GetString(keys::kIntentTitle
, &service
.title
)) {
2044 *error
= ASCIIToUTF16(errors::kInvalidIntentTitle
);
2048 if (intent_service
.HasKey(keys::kIntentDisposition
)) {
2049 if (is_platform_app()) {
2050 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2051 errors::kInvalidIntentDispositionInPlatformApp
, action_name
);
2054 if (!intent_service
.GetString(keys::kIntentDisposition
, &value
) ||
2055 (value
!= values::kIntentDispositionWindow
&&
2056 value
!= values::kIntentDispositionInline
)) {
2057 *error
= ASCIIToUTF16(errors::kInvalidIntentDisposition
);
2060 if (value
== values::kIntentDispositionInline
) {
2061 service
.disposition
=
2062 webkit_glue::WebIntentServiceData::DISPOSITION_INLINE
;
2064 service
.disposition
=
2065 webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW
;
2069 for (size_t i
= 0; i
< mime_types
->GetSize(); ++i
) {
2070 if (!mime_types
->GetString(i
, &service
.type
)) {
2071 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2072 errors::kInvalidIntentTypeElement
, action_name
,
2073 std::string(base::IntToString(i
)));
2076 intents_services_
.push_back(service
);
2081 bool Extension::LoadWebIntentServices(string16
* error
) {
2084 if (!manifest_
->HasKey(keys::kIntents
))
2087 DictionaryValue
* all_services
= NULL
;
2088 if (!manifest_
->GetDictionary(keys::kIntents
, &all_services
)) {
2089 *error
= ASCIIToUTF16(errors::kInvalidIntents
);
2093 for (DictionaryValue::key_iterator
iter(all_services
->begin_keys());
2094 iter
!= all_services
->end_keys(); ++iter
) {
2095 // Any entry in the intents dictionary can either have a list of
2096 // dictionaries, or just a single dictionary attached to that. Try
2097 // lists first, fall back to single dictionary.
2098 ListValue
* service_list
= NULL
;
2099 DictionaryValue
* one_service
= NULL
;
2100 if (all_services
->GetListWithoutPathExpansion(*iter
, &service_list
)) {
2101 for (size_t i
= 0; i
< service_list
->GetSize(); ++i
) {
2102 if (!service_list
->GetDictionary(i
, &one_service
)) {
2103 *error
= ASCIIToUTF16(errors::kInvalidIntent
);
2106 if (!LoadWebIntentAction(*iter
, *one_service
, error
))
2110 if (!all_services
->GetDictionaryWithoutPathExpansion(*iter
,
2112 *error
= ASCIIToUTF16(errors::kInvalidIntent
);
2115 if (!LoadWebIntentAction(*iter
, *one_service
, error
))
2122 bool Extension::LoadFileHandler(const std::string
& handler_id
,
2123 const DictionaryValue
& handler_info
,
2126 DCHECK(is_platform_app());
2127 webkit_glue::WebIntentServiceData service
;
2129 // TODO(jeremya): use a file-handler-specific data structure instead of web
2131 service
.action
= ASCIIToUTF16("http://webintents.org/view");
2133 const ListValue
* mime_types
= NULL
;
2134 if (!handler_info
.HasKey(keys::kFileHandlerTypes
) ||
2135 !handler_info
.GetList(keys::kFileHandlerTypes
, &mime_types
) ||
2136 mime_types
->GetSize() == 0) {
2137 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2138 errors::kInvalidFileHandlerType
, handler_id
);
2142 service
.service_url
= GetBackgroundURL();
2144 if (handler_info
.HasKey(keys::kFileHandlerTitle
) &&
2145 !handler_info
.GetString(keys::kFileHandlerTitle
, &service
.title
)) {
2146 *error
= ASCIIToUTF16(errors::kInvalidFileHandlerTitle
);
2150 for (size_t i
= 0; i
< mime_types
->GetSize(); ++i
) {
2151 if (!mime_types
->GetString(i
, &service
.type
)) {
2152 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2153 errors::kInvalidFileHandlerTypeElement
, handler_id
,
2154 std::string(base::IntToString(i
)));
2157 intents_services_
.push_back(service
);
2162 bool Extension::LoadFileHandlers(string16
* error
) {
2165 if (!manifest_
->HasKey(keys::kFileHandlers
))
2168 DictionaryValue
* all_handlers
= NULL
;
2169 if (!manifest_
->GetDictionary(keys::kFileHandlers
, &all_handlers
)) {
2170 *error
= ASCIIToUTF16(errors::kInvalidFileHandlers
);
2174 for (DictionaryValue::key_iterator
iter(all_handlers
->begin_keys());
2175 iter
!= all_handlers
->end_keys(); ++iter
) {
2176 // A file handler entry is a title and a list of MIME types to handle.
2177 DictionaryValue
* handler
= NULL
;
2178 if (all_handlers
->GetDictionaryWithoutPathExpansion(*iter
, &handler
)) {
2179 if (!LoadFileHandler(*iter
, *handler
, error
))
2182 *error
= ASCIIToUTF16(errors::kInvalidFileHandlers
);
2189 bool Extension::LoadExtensionFeatures(const APIPermissionSet
& api_permissions
,
2191 if (manifest_
->HasKey(keys::kConvertedFromUserScript
))
2192 manifest_
->GetBoolean(keys::kConvertedFromUserScript
,
2193 &converted_from_user_script_
);
2195 if (!LoadDevToolsPage(error
) ||
2196 !LoadInputComponents(api_permissions
, error
) ||
2197 !LoadContentScripts(error
) ||
2198 !LoadPageAction(error
) ||
2199 !LoadBrowserAction(error
) ||
2200 !LoadScriptBadge(error
) ||
2201 !LoadFileBrowserHandlers(error
) ||
2202 !LoadChromeURLOverrides(error
) ||
2203 !LoadOmnibox(error
) ||
2204 !LoadTextToSpeechVoices(error
) ||
2205 !LoadIncognitoMode(error
) ||
2206 !LoadFileHandlers(error
) ||
2207 !LoadContentSecurityPolicy(error
))
2213 bool Extension::LoadDevToolsPage(string16
* error
) {
2214 if (!manifest_
->HasKey(keys::kDevToolsPage
))
2216 std::string devtools_str
;
2217 if (!manifest_
->GetString(keys::kDevToolsPage
, &devtools_str
)) {
2218 *error
= ASCIIToUTF16(errors::kInvalidDevToolsPage
);
2221 devtools_url_
= GetResourceURL(devtools_str
);
2225 bool Extension::LoadInputComponents(const APIPermissionSet
& api_permissions
,
2227 if (!manifest_
->HasKey(keys::kInputComponents
))
2229 ListValue
* list_value
= NULL
;
2230 if (!manifest_
->GetList(keys::kInputComponents
, &list_value
)) {
2231 *error
= ASCIIToUTF16(errors::kInvalidInputComponents
);
2235 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
2236 DictionaryValue
* module_value
= NULL
;
2237 std::string name_str
;
2238 InputComponentType type
;
2240 std::string description_str
;
2241 std::string language_str
;
2242 std::set
<std::string
> layouts
;
2243 std::string shortcut_keycode_str
;
2244 bool shortcut_alt
= false;
2245 bool shortcut_ctrl
= false;
2246 bool shortcut_shift
= false;
2248 if (!list_value
->GetDictionary(i
, &module_value
)) {
2249 *error
= ASCIIToUTF16(errors::kInvalidInputComponents
);
2253 // Get input_components[i].name.
2254 if (!module_value
->GetString(keys::kName
, &name_str
)) {
2255 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2256 errors::kInvalidInputComponentName
, base::IntToString(i
));
2260 // Get input_components[i].type.
2261 std::string type_str
;
2262 if (module_value
->GetString(keys::kType
, &type_str
)) {
2263 if (type_str
== "ime") {
2264 type
= INPUT_COMPONENT_TYPE_IME
;
2266 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2267 errors::kInvalidInputComponentType
, base::IntToString(i
));
2271 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2272 errors::kInvalidInputComponentType
, base::IntToString(i
));
2276 // Get input_components[i].id.
2277 if (!module_value
->GetString(keys::kId
, &id_str
)) {
2281 // Get input_components[i].description.
2282 if (!module_value
->GetString(keys::kDescription
, &description_str
)) {
2283 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2284 errors::kInvalidInputComponentDescription
, base::IntToString(i
));
2287 // Get input_components[i].language.
2288 if (!module_value
->GetString(keys::kLanguage
, &language_str
)) {
2292 // Get input_components[i].layouts.
2293 ListValue
* layouts_value
= NULL
;
2294 if (!module_value
->GetList(keys::kLayouts
, &layouts_value
)) {
2295 *error
= ASCIIToUTF16(errors::kInvalidInputComponentLayouts
);
2299 for (size_t j
= 0; j
< layouts_value
->GetSize(); ++j
) {
2300 std::string layout_name_str
;
2301 if (!layouts_value
->GetString(j
, &layout_name_str
)) {
2302 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2303 errors::kInvalidInputComponentLayoutName
, base::IntToString(i
),
2304 base::IntToString(j
));
2307 layouts
.insert(layout_name_str
);
2310 if (module_value
->HasKey(keys::kShortcutKey
)) {
2311 DictionaryValue
* shortcut_value
= NULL
;
2312 if (!module_value
->GetDictionary(keys::kShortcutKey
, &shortcut_value
)) {
2313 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2314 errors::kInvalidInputComponentShortcutKey
, base::IntToString(i
));
2318 // Get input_components[i].shortcut_keycode.
2319 if (!shortcut_value
->GetString(keys::kKeycode
, &shortcut_keycode_str
)) {
2320 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2321 errors::kInvalidInputComponentShortcutKeycode
,
2322 base::IntToString(i
));
2326 // Get input_components[i].shortcut_alt.
2327 if (!shortcut_value
->GetBoolean(keys::kAltKey
, &shortcut_alt
)) {
2328 shortcut_alt
= false;
2331 // Get input_components[i].shortcut_ctrl.
2332 if (!shortcut_value
->GetBoolean(keys::kCtrlKey
, &shortcut_ctrl
)) {
2333 shortcut_ctrl
= false;
2336 // Get input_components[i].shortcut_shift.
2337 if (!shortcut_value
->GetBoolean(keys::kShiftKey
, &shortcut_shift
)) {
2338 shortcut_shift
= false;
2342 input_components_
.push_back(InputComponentInfo());
2343 input_components_
.back().name
= name_str
;
2344 input_components_
.back().type
= type
;
2345 input_components_
.back().id
= id_str
;
2346 input_components_
.back().description
= description_str
;
2347 input_components_
.back().language
= language_str
;
2348 input_components_
.back().layouts
.insert(layouts
.begin(), layouts
.end());
2349 input_components_
.back().shortcut_keycode
= shortcut_keycode_str
;
2350 input_components_
.back().shortcut_alt
= shortcut_alt
;
2351 input_components_
.back().shortcut_ctrl
= shortcut_ctrl
;
2352 input_components_
.back().shortcut_shift
= shortcut_shift
;
2358 bool Extension::LoadContentScripts(string16
* error
) {
2359 if (!manifest_
->HasKey(keys::kContentScripts
))
2361 ListValue
* list_value
;
2362 if (!manifest_
->GetList(keys::kContentScripts
, &list_value
)) {
2363 *error
= ASCIIToUTF16(errors::kInvalidContentScriptsList
);
2367 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
2368 DictionaryValue
* content_script
= NULL
;
2369 if (!list_value
->GetDictionary(i
, &content_script
)) {
2370 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2371 errors::kInvalidContentScript
, base::IntToString(i
));
2376 if (!LoadUserScriptHelper(content_script
, i
, error
, &script
))
2377 return false; // Failed to parse script context definition.
2378 script
.set_extension_id(id());
2379 if (converted_from_user_script_
) {
2380 script
.set_emulate_greasemonkey(true);
2381 script
.set_match_all_frames(true); // Greasemonkey matches all frames.
2383 content_scripts_
.push_back(script
);
2388 bool Extension::LoadPageAction(string16
* error
) {
2389 DictionaryValue
* page_action_value
= NULL
;
2391 if (manifest_
->HasKey(keys::kPageActions
)) {
2392 ListValue
* list_value
= NULL
;
2393 if (!manifest_
->GetList(keys::kPageActions
, &list_value
)) {
2394 *error
= ASCIIToUTF16(errors::kInvalidPageActionsList
);
2398 size_t list_value_length
= list_value
->GetSize();
2400 if (list_value_length
== 0u) {
2401 // A list with zero items is allowed, and is equivalent to not having
2402 // a page_actions key in the manifest. Don't set |page_action_value|.
2403 } else if (list_value_length
== 1u) {
2404 if (!list_value
->GetDictionary(0, &page_action_value
)) {
2405 *error
= ASCIIToUTF16(errors::kInvalidPageAction
);
2408 } else { // list_value_length > 1u.
2409 *error
= ASCIIToUTF16(errors::kInvalidPageActionsListSize
);
2412 } else if (manifest_
->HasKey(keys::kPageAction
)) {
2413 if (!manifest_
->GetDictionary(keys::kPageAction
, &page_action_value
)) {
2414 *error
= ASCIIToUTF16(errors::kInvalidPageAction
);
2419 // If page_action_value is not NULL, then there was a valid page action.
2420 if (page_action_value
) {
2421 page_action_info_
= LoadExtensionActionInfoHelper(
2422 page_action_value
, Extension::ActionInfo::TYPE_PAGE
, error
);
2423 if (!page_action_info_
.get())
2424 return false; // Failed to parse page action definition.
2430 bool Extension::LoadBrowserAction(string16
* error
) {
2431 if (!manifest_
->HasKey(keys::kBrowserAction
))
2433 DictionaryValue
* browser_action_value
= NULL
;
2434 if (!manifest_
->GetDictionary(keys::kBrowserAction
, &browser_action_value
)) {
2435 *error
= ASCIIToUTF16(errors::kInvalidBrowserAction
);
2439 browser_action_info_
= LoadExtensionActionInfoHelper(
2440 browser_action_value
, Extension::ActionInfo::TYPE_BROWSER
, error
);
2441 if (!browser_action_info_
.get())
2442 return false; // Failed to parse browser action definition.
2446 bool Extension::LoadScriptBadge(string16
* error
) {
2447 if (manifest_
->HasKey(keys::kScriptBadge
)) {
2448 if (!FeatureSwitch::script_badges()->IsEnabled()) {
2449 // So as to not confuse developers if they specify a script badge section
2450 // in the manifest, show a warning if the script badge declaration isn't
2451 // going to have any effect.
2452 install_warnings_
.push_back(
2453 InstallWarning(InstallWarning::FORMAT_TEXT
,
2454 errors::kScriptBadgeRequiresFlag
));
2457 DictionaryValue
* script_badge_value
= NULL
;
2458 if (!manifest_
->GetDictionary(keys::kScriptBadge
, &script_badge_value
)) {
2459 *error
= ASCIIToUTF16(errors::kInvalidScriptBadge
);
2463 script_badge_info_
= LoadExtensionActionInfoHelper(
2464 script_badge_value
, Extension::ActionInfo::TYPE_SCRIPT_BADGE
, error
);
2465 if (!script_badge_info_
.get())
2466 return false; // Failed to parse script badge definition.
2468 script_badge_info_
.reset(new ActionInfo());
2471 // Script badges always use their extension's title and icon so users can rely
2472 // on the visual appearance to know which extension is running. This isn't
2473 // bulletproof since an malicious extension could use a different 16x16 icon
2474 // that matches the icon of a trusted extension, and users wouldn't be warned
2475 // during installation.
2477 if (!script_badge_info_
->default_title
.empty()) {
2478 install_warnings_
.push_back(
2479 InstallWarning(InstallWarning::FORMAT_TEXT
,
2480 errors::kScriptBadgeTitleIgnored
));
2482 script_badge_info_
->default_title
= name();
2484 if (!script_badge_info_
->default_icon
.empty()) {
2485 install_warnings_
.push_back(
2486 InstallWarning(InstallWarning::FORMAT_TEXT
,
2487 errors::kScriptBadgeIconIgnored
));
2490 script_badge_info_
->default_icon
.Clear();
2491 for (size_t i
= 0; i
< extension_misc::kNumScriptBadgeIconSizes
; i
++) {
2492 std::string path
= icons().Get(extension_misc::kScriptBadgeIconSizes
[i
],
2493 ExtensionIconSet::MATCH_BIGGER
);
2495 script_badge_info_
->default_icon
.Add(
2496 extension_misc::kScriptBadgeIconSizes
[i
], path
);
2502 bool Extension::LoadFileBrowserHandlers(string16
* error
) {
2503 if (!manifest_
->HasKey(keys::kFileBrowserHandlers
))
2505 ListValue
* file_browser_handlers_value
= NULL
;
2506 if (!manifest_
->GetList(keys::kFileBrowserHandlers
,
2507 &file_browser_handlers_value
)) {
2508 *error
= ASCIIToUTF16(errors::kInvalidFileBrowserHandler
);
2511 file_browser_handlers_
.reset(
2512 LoadFileBrowserHandlersHelper(file_browser_handlers_value
, error
));
2513 if (!file_browser_handlers_
.get())
2514 return false; // Failed to parse file browser actions definition.
2518 Extension::FileBrowserHandlerList
* Extension::LoadFileBrowserHandlersHelper(
2519 const ListValue
* extension_actions
, string16
* error
) {
2520 scoped_ptr
<FileBrowserHandlerList
> result(
2521 new FileBrowserHandlerList());
2522 for (ListValue::const_iterator iter
= extension_actions
->begin();
2523 iter
!= extension_actions
->end();
2525 if (!(*iter
)->IsType(Value::TYPE_DICTIONARY
)) {
2526 *error
= ASCIIToUTF16(errors::kInvalidFileBrowserHandler
);
2529 scoped_ptr
<FileBrowserHandler
> action(
2530 LoadFileBrowserHandler(
2531 reinterpret_cast<DictionaryValue
*>(*iter
), error
));
2533 return NULL
; // Failed to parse file browser action definition.
2534 result
->push_back(linked_ptr
<FileBrowserHandler
>(action
.release()));
2536 return result
.release();
2539 FileBrowserHandler
* Extension::LoadFileBrowserHandler(
2540 const DictionaryValue
* file_browser_handler
, string16
* error
) {
2541 scoped_ptr
<FileBrowserHandler
> result(new FileBrowserHandler());
2542 result
->set_extension_id(id());
2545 // Read the file action |id| (mandatory).
2546 if (!file_browser_handler
->HasKey(keys::kPageActionId
) ||
2547 !file_browser_handler
->GetString(keys::kPageActionId
, &id
)) {
2548 *error
= ASCIIToUTF16(errors::kInvalidPageActionId
);
2553 // Read the page action title from |default_title| (mandatory).
2555 if (!file_browser_handler
->HasKey(keys::kPageActionDefaultTitle
) ||
2556 !file_browser_handler
->GetString(keys::kPageActionDefaultTitle
, &title
)) {
2557 *error
= ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle
);
2560 result
->set_title(title
);
2562 // Initialize access permissions (optional).
2563 const ListValue
* access_list_value
= NULL
;
2564 if (file_browser_handler
->HasKey(keys::kFileAccessList
)) {
2565 if (!file_browser_handler
->GetList(keys::kFileAccessList
,
2566 &access_list_value
) ||
2567 access_list_value
->empty()) {
2568 *error
= ASCIIToUTF16(errors::kInvalidFileAccessList
);
2571 for (size_t i
= 0; i
< access_list_value
->GetSize(); ++i
) {
2573 if (!access_list_value
->GetString(i
, &access
) ||
2574 result
->AddFileAccessPermission(access
)) {
2575 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2576 errors::kInvalidFileAccessValue
, base::IntToString(i
));
2581 if (!result
->ValidateFileAccessPermissions()) {
2582 *error
= ASCIIToUTF16(errors::kInvalidFileAccessList
);
2586 // Initialize file filters (mandatory, unless "create" access is specified,
2587 // in which case is ignored).
2588 if (!result
->HasCreateAccessPermission()) {
2589 const ListValue
* list_value
= NULL
;
2590 if (!file_browser_handler
->HasKey(keys::kFileFilters
) ||
2591 !file_browser_handler
->GetList(keys::kFileFilters
, &list_value
) ||
2592 list_value
->empty()) {
2593 *error
= ASCIIToUTF16(errors::kInvalidFileFiltersList
);
2596 for (size_t i
= 0; i
< list_value
->GetSize(); ++i
) {
2598 if (!list_value
->GetString(i
, &filter
)) {
2599 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2600 errors::kInvalidFileFilterValue
, base::IntToString(i
));
2603 StringToLowerASCII(&filter
);
2604 if (!StartsWithASCII(filter
,
2605 std::string(chrome::kFileSystemScheme
) + ':',
2607 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2608 errors::kInvalidURLPatternError
, filter
);
2611 // The user inputs filesystem:*; we don't actually implement scheme
2612 // wildcards in URLPattern, so transform to what will match correctly.
2613 filter
.replace(0, 11, "chrome-extension://*/");
2614 URLPattern
pattern(URLPattern::SCHEME_EXTENSION
);
2615 if (pattern
.Parse(filter
) != URLPattern::PARSE_SUCCESS
) {
2616 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2617 errors::kInvalidURLPatternError
, filter
);
2620 std::string path
= pattern
.path();
2621 bool allowed
= path
== "/*" || path
== "/*.*" ||
2622 (path
.compare(0, 3, "/*.") == 0 &&
2623 path
.find_first_of('*', 3) == std::string::npos
);
2625 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2626 errors::kInvalidURLPatternError
, filter
);
2629 result
->AddPattern(pattern
);
2633 std::string default_icon
;
2634 // Read the file browser action |default_icon| (optional).
2635 if (file_browser_handler
->HasKey(keys::kPageActionDefaultIcon
)) {
2636 if (!file_browser_handler
->GetString(
2637 keys::kPageActionDefaultIcon
, &default_icon
) ||
2638 default_icon
.empty()) {
2639 *error
= ASCIIToUTF16(errors::kInvalidPageActionIconPath
);
2642 result
->set_icon_path(default_icon
);
2645 return result
.release();
2648 bool Extension::LoadChromeURLOverrides(string16
* error
) {
2649 if (!manifest_
->HasKey(keys::kChromeURLOverrides
))
2651 DictionaryValue
* overrides
= NULL
;
2652 if (!manifest_
->GetDictionary(keys::kChromeURLOverrides
, &overrides
)) {
2653 *error
= ASCIIToUTF16(errors::kInvalidChromeURLOverrides
);
2657 // Validate that the overrides are all strings
2658 for (DictionaryValue::key_iterator iter
= overrides
->begin_keys();
2659 iter
!= overrides
->end_keys(); ++iter
) {
2660 std::string page
= *iter
;
2662 // Restrict override pages to a list of supported URLs.
2663 bool is_override
= (page
!= chrome::kChromeUINewTabHost
&&
2664 page
!= chrome::kChromeUIBookmarksHost
&&
2665 page
!= chrome::kChromeUIHistoryHost
);
2666 #if defined(OS_CHROMEOS)
2667 is_override
= (is_override
&&
2668 page
!= chrome::kChromeUIActivationMessageHost
&&
2669 page
!= chrome::kChromeUIWallpaperHost
);
2671 #if defined(FILE_MANAGER_EXTENSION)
2672 is_override
= (is_override
&&
2673 !(location() == COMPONENT
&&
2674 page
== chrome::kChromeUIFileManagerHost
));
2677 if (is_override
|| !overrides
->GetStringWithoutPathExpansion(*iter
, &val
)) {
2678 *error
= ASCIIToUTF16(errors::kInvalidChromeURLOverrides
);
2681 // Replace the entry with a fully qualified chrome-extension:// URL.
2682 chrome_url_overrides_
[page
] = GetResourceURL(val
);
2684 // For component extensions, add override URL to extent patterns.
2685 if (is_legacy_packaged_app() && location() == COMPONENT
) {
2686 URLPattern
pattern(URLPattern::SCHEME_CHROMEUI
);
2687 std::string url
= base::StringPrintf(kOverrideExtentUrlPatternFormat
,
2689 if (pattern
.Parse(url
) != URLPattern::PARSE_SUCCESS
) {
2690 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2691 errors::kInvalidURLPatternError
, url
);
2694 extent_
.AddPattern(pattern
);
2698 // An extension may override at most one page.
2699 if (overrides
->size() > 1) {
2700 *error
= ASCIIToUTF16(errors::kMultipleOverrides
);
2707 bool Extension::LoadOmnibox(string16
* error
) {
2708 if (!manifest_
->HasKey(keys::kOmnibox
))
2710 if (!manifest_
->GetString(keys::kOmniboxKeyword
, &omnibox_keyword_
) ||
2711 omnibox_keyword_
.empty()) {
2712 *error
= ASCIIToUTF16(errors::kInvalidOmniboxKeyword
);
2718 bool Extension::LoadTextToSpeechVoices(string16
* error
) {
2719 if (!manifest_
->HasKey(keys::kTtsEngine
))
2721 DictionaryValue
* tts_dict
= NULL
;
2722 if (!manifest_
->GetDictionary(keys::kTtsEngine
, &tts_dict
)) {
2723 *error
= ASCIIToUTF16(errors::kInvalidTts
);
2727 if (tts_dict
->HasKey(keys::kTtsVoices
)) {
2728 ListValue
* tts_voices
= NULL
;
2729 if (!tts_dict
->GetList(keys::kTtsVoices
, &tts_voices
)) {
2730 *error
= ASCIIToUTF16(errors::kInvalidTtsVoices
);
2734 for (size_t i
= 0; i
< tts_voices
->GetSize(); i
++) {
2735 DictionaryValue
* one_tts_voice
= NULL
;
2736 if (!tts_voices
->GetDictionary(i
, &one_tts_voice
)) {
2737 *error
= ASCIIToUTF16(errors::kInvalidTtsVoices
);
2741 TtsVoice voice_data
;
2742 if (one_tts_voice
->HasKey(keys::kTtsVoicesVoiceName
)) {
2743 if (!one_tts_voice
->GetString(
2744 keys::kTtsVoicesVoiceName
, &voice_data
.voice_name
)) {
2745 *error
= ASCIIToUTF16(errors::kInvalidTtsVoicesVoiceName
);
2749 if (one_tts_voice
->HasKey(keys::kTtsVoicesLang
)) {
2750 if (!one_tts_voice
->GetString(
2751 keys::kTtsVoicesLang
, &voice_data
.lang
) ||
2752 !l10n_util::IsValidLocaleSyntax(voice_data
.lang
)) {
2753 *error
= ASCIIToUTF16(errors::kInvalidTtsVoicesLang
);
2757 if (one_tts_voice
->HasKey(keys::kTtsVoicesGender
)) {
2758 if (!one_tts_voice
->GetString(
2759 keys::kTtsVoicesGender
, &voice_data
.gender
) ||
2760 (voice_data
.gender
!= keys::kTtsGenderMale
&&
2761 voice_data
.gender
!= keys::kTtsGenderFemale
)) {
2762 *error
= ASCIIToUTF16(errors::kInvalidTtsVoicesGender
);
2766 if (one_tts_voice
->HasKey(keys::kTtsVoicesEventTypes
)) {
2767 ListValue
* event_types_list
;
2768 if (!one_tts_voice
->GetList(
2769 keys::kTtsVoicesEventTypes
, &event_types_list
)) {
2770 *error
= ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes
);
2773 for (size_t i
= 0; i
< event_types_list
->GetSize(); i
++) {
2774 std::string event_type
;
2775 if (!event_types_list
->GetString(i
, &event_type
)) {
2776 *error
= ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes
);
2779 if (event_type
!= keys::kTtsVoicesEventTypeEnd
&&
2780 event_type
!= keys::kTtsVoicesEventTypeError
&&
2781 event_type
!= keys::kTtsVoicesEventTypeMarker
&&
2782 event_type
!= keys::kTtsVoicesEventTypeSentence
&&
2783 event_type
!= keys::kTtsVoicesEventTypeStart
&&
2784 event_type
!= keys::kTtsVoicesEventTypeWord
) {
2785 *error
= ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes
);
2788 if (voice_data
.event_types
.find(event_type
) !=
2789 voice_data
.event_types
.end()) {
2790 *error
= ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes
);
2793 voice_data
.event_types
.insert(event_type
);
2797 tts_voices_
.push_back(voice_data
);
2803 bool Extension::LoadIncognitoMode(string16
* error
) {
2804 // Apps default to split mode, extensions default to spanning.
2805 incognito_split_mode_
= is_app();
2806 if (!manifest_
->HasKey(keys::kIncognito
))
2809 if (!manifest_
->GetString(keys::kIncognito
, &value
)) {
2810 *error
= ASCIIToUTF16(errors::kInvalidIncognitoBehavior
);
2813 if (value
== values::kIncognitoSpanning
) {
2814 incognito_split_mode_
= false;
2815 } else if (value
== values::kIncognitoSplit
) {
2816 incognito_split_mode_
= true;
2818 *error
= ASCIIToUTF16(errors::kInvalidIncognitoBehavior
);
2824 bool Extension::LoadContentSecurityPolicy(string16
* error
) {
2825 const std::string
& key
= is_platform_app() ?
2826 keys::kPlatformAppContentSecurityPolicy
: keys::kContentSecurityPolicy
;
2828 if (manifest_
->HasPath(key
)) {
2829 std::string content_security_policy
;
2830 if (!manifest_
->GetString(key
, &content_security_policy
)) {
2831 *error
= ASCIIToUTF16(errors::kInvalidContentSecurityPolicy
);
2834 if (!ContentSecurityPolicyIsLegal(content_security_policy
)) {
2835 *error
= ASCIIToUTF16(errors::kInvalidContentSecurityPolicy
);
2838 if (manifest_version_
>= 2 &&
2839 !ContentSecurityPolicyIsSecure(content_security_policy
, GetType())) {
2840 *error
= ASCIIToUTF16(errors::kInsecureContentSecurityPolicy
);
2844 content_security_policy_
= content_security_policy
;
2845 } else if (manifest_version_
>= 2) {
2846 // Manifest version 2 introduced a default Content-Security-Policy.
2847 // TODO(abarth): Should we continue to let extensions override the
2848 // default Content-Security-Policy?
2849 content_security_policy_
= is_platform_app() ?
2850 kDefaultPlatformAppContentSecurityPolicy
:
2851 kDefaultContentSecurityPolicy
;
2852 CHECK(ContentSecurityPolicyIsSecure(content_security_policy_
, GetType()));
2857 bool Extension::LoadAppIsolation(const APIPermissionSet
& api_permissions
,
2859 // Platform apps always get isolated storage.
2860 if (is_platform_app()) {
2861 is_storage_isolated_
= true;
2865 // Other apps only get it if it is requested _and_ experimental APIs are
2867 if (!api_permissions
.count(APIPermission::kExperimental
) || !is_app())
2870 Value
* tmp_isolation
= NULL
;
2871 if (!manifest_
->Get(keys::kIsolation
, &tmp_isolation
))
2874 if (tmp_isolation
->GetType() != Value::TYPE_LIST
) {
2875 *error
= ASCIIToUTF16(errors::kInvalidIsolation
);
2879 ListValue
* isolation_list
= static_cast<ListValue
*>(tmp_isolation
);
2880 for (size_t i
= 0; i
< isolation_list
->GetSize(); ++i
) {
2881 std::string isolation_string
;
2882 if (!isolation_list
->GetString(i
, &isolation_string
)) {
2883 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
2884 errors::kInvalidIsolationValue
,
2885 base::UintToString(i
));
2889 // Check for isolated storage.
2890 if (isolation_string
== values::kIsolatedStorage
) {
2891 is_storage_isolated_
= true;
2893 DLOG(WARNING
) << "Did not recognize isolation type: " << isolation_string
;
2899 bool Extension::LoadThemeFeatures(string16
* error
) {
2900 if (!manifest_
->HasKey(keys::kTheme
))
2902 DictionaryValue
* theme_value
= NULL
;
2903 if (!manifest_
->GetDictionary(keys::kTheme
, &theme_value
)) {
2904 *error
= ASCIIToUTF16(errors::kInvalidTheme
);
2907 if (!LoadThemeImages(theme_value
, error
))
2909 if (!LoadThemeColors(theme_value
, error
))
2911 if (!LoadThemeTints(theme_value
, error
))
2913 if (!LoadThemeDisplayProperties(theme_value
, error
))
2919 bool Extension::LoadThemeImages(const DictionaryValue
* theme_value
,
2921 const DictionaryValue
* images_value
= NULL
;
2922 if (theme_value
->GetDictionary(keys::kThemeImages
, &images_value
)) {
2923 // Validate that the images are all strings
2924 for (DictionaryValue::key_iterator iter
= images_value
->begin_keys();
2925 iter
!= images_value
->end_keys(); ++iter
) {
2927 if (!images_value
->GetString(*iter
, &val
)) {
2928 *error
= ASCIIToUTF16(errors::kInvalidThemeImages
);
2932 theme_images_
.reset(images_value
->DeepCopy());
2937 bool Extension::LoadThemeColors(const DictionaryValue
* theme_value
,
2939 const DictionaryValue
* colors_value
= NULL
;
2940 if (theme_value
->GetDictionary(keys::kThemeColors
, &colors_value
)) {
2941 // Validate that the colors are RGB or RGBA lists
2942 for (DictionaryValue::key_iterator iter
= colors_value
->begin_keys();
2943 iter
!= colors_value
->end_keys(); ++iter
) {
2944 const ListValue
* color_list
= NULL
;
2947 // The color must be a list
2948 if (!colors_value
->GetListWithoutPathExpansion(*iter
, &color_list
) ||
2949 // And either 3 items (RGB) or 4 (RGBA)
2950 ((color_list
->GetSize() != 3) &&
2951 ((color_list
->GetSize() != 4) ||
2952 // For RGBA, the fourth item must be a real or int alpha value.
2953 // Note that GetDouble() can get an integer value.
2954 !color_list
->GetDouble(3, &alpha
))) ||
2955 // For both RGB and RGBA, the first three items must be ints (R,G,B)
2956 !color_list
->GetInteger(0, &color
) ||
2957 !color_list
->GetInteger(1, &color
) ||
2958 !color_list
->GetInteger(2, &color
)) {
2959 *error
= ASCIIToUTF16(errors::kInvalidThemeColors
);
2963 theme_colors_
.reset(colors_value
->DeepCopy());
2968 bool Extension::LoadThemeTints(const DictionaryValue
* theme_value
,
2970 const DictionaryValue
* tints_value
= NULL
;
2971 if (!theme_value
->GetDictionary(keys::kThemeTints
, &tints_value
))
2974 // Validate that the tints are all reals.
2975 for (DictionaryValue::key_iterator iter
= tints_value
->begin_keys();
2976 iter
!= tints_value
->end_keys(); ++iter
) {
2977 const ListValue
* tint_list
= NULL
;
2979 if (!tints_value
->GetListWithoutPathExpansion(*iter
, &tint_list
) ||
2980 tint_list
->GetSize() != 3 ||
2981 !tint_list
->GetDouble(0, &v
) ||
2982 !tint_list
->GetDouble(1, &v
) ||
2983 !tint_list
->GetDouble(2, &v
)) {
2984 *error
= ASCIIToUTF16(errors::kInvalidThemeTints
);
2988 theme_tints_
.reset(tints_value
->DeepCopy());
2992 bool Extension::LoadThemeDisplayProperties(const DictionaryValue
* theme_value
,
2994 const DictionaryValue
* display_properties_value
= NULL
;
2995 if (theme_value
->GetDictionary(keys::kThemeDisplayProperties
,
2996 &display_properties_value
)) {
2997 theme_display_properties_
.reset(
2998 display_properties_value
->DeepCopy());
3004 bool Extension::IsTrustedId(const std::string
& id
) {
3005 // See http://b/4946060 for more details.
3006 return id
== std::string("nckgahadagoaajjgafhacjanaoiihapd");
3009 Extension::Extension(const FilePath
& path
,
3010 scoped_ptr
<extensions::Manifest
> manifest
)
3011 : manifest_version_(0),
3012 incognito_split_mode_(false),
3013 offline_enabled_(false),
3014 converted_from_user_script_(false),
3015 background_page_is_persistent_(true),
3016 allow_background_js_access_(true),
3017 manifest_(manifest
.release()),
3018 is_storage_isolated_(false),
3019 launch_container_(extension_misc::LAUNCH_TAB
),
3022 display_in_launcher_(true),
3023 display_in_new_tab_page_(true),
3024 wants_file_access_(false),
3025 creation_flags_(0) {
3026 DCHECK(path
.empty() || path
.IsAbsolute());
3027 path_
= MaybeNormalizePath(path
);
3030 Extension::~Extension() {
3033 ExtensionResource
Extension::GetResource(
3034 const std::string
& relative_path
) const {
3035 std::string new_path
= relative_path
;
3036 // We have some legacy data where resources have leading slashes.
3037 // See: http://crbug.com/121164
3038 if (!new_path
.empty() && new_path
.at(0) == '/')
3039 new_path
.erase(0, 1);
3040 #if defined(OS_POSIX)
3041 FilePath
relative_file_path(new_path
);
3042 #elif defined(OS_WIN)
3043 FilePath
relative_file_path(UTF8ToWide(new_path
));
3045 ExtensionResource
r(id(), path(), relative_file_path
);
3046 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE
)) {
3047 r
.set_follow_symlinks_anywhere();
3052 ExtensionResource
Extension::GetResource(
3053 const FilePath
& relative_file_path
) const {
3054 ExtensionResource
r(id(), path(), relative_file_path
);
3055 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE
)) {
3056 r
.set_follow_symlinks_anywhere();
3061 // TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a
3062 // util class in base:
3063 // http://code.google.com/p/chromium/issues/detail?id=13572
3064 bool Extension::ParsePEMKeyBytes(const std::string
& input
,
3065 std::string
* output
) {
3069 if (input
.length() == 0)
3072 std::string working
= input
;
3073 if (StartsWithASCII(working
, kKeyBeginHeaderMarker
, true)) {
3074 working
= CollapseWhitespaceASCII(working
, true);
3075 size_t header_pos
= working
.find(kKeyInfoEndMarker
,
3076 sizeof(kKeyBeginHeaderMarker
) - 1);
3077 if (header_pos
== std::string::npos
)
3079 size_t start_pos
= header_pos
+ sizeof(kKeyInfoEndMarker
) - 1;
3080 size_t end_pos
= working
.rfind(kKeyBeginFooterMarker
);
3081 if (end_pos
== std::string::npos
)
3083 if (start_pos
>= end_pos
)
3086 working
= working
.substr(start_pos
, end_pos
- start_pos
);
3087 if (working
.length() == 0)
3091 return base::Base64Decode(working
, output
);
3094 bool Extension::ProducePEM(const std::string
& input
, std::string
* output
) {
3096 return (input
.length() == 0) ? false : base::Base64Encode(input
, output
);
3099 bool Extension::FormatPEMForFileOutput(const std::string
& input
,
3100 std::string
* output
,
3103 if (input
.length() == 0)
3106 output
->append(kKeyBeginHeaderMarker
);
3107 output
->append(" ");
3108 output
->append(is_public
? kPublic
: kPrivate
);
3109 output
->append(" ");
3110 output
->append(kKeyInfoEndMarker
);
3111 output
->append("\n");
3112 for (size_t i
= 0; i
< input
.length(); ) {
3113 int slice
= std::min
<int>(input
.length() - i
, kPEMOutputColumns
);
3114 output
->append(input
.substr(i
, slice
));
3115 output
->append("\n");
3118 output
->append(kKeyBeginFooterMarker
);
3119 output
->append(" ");
3120 output
->append(is_public
? kPublic
: kPrivate
);
3121 output
->append(" ");
3122 output
->append(kKeyInfoEndMarker
);
3123 output
->append("\n");
3129 void Extension::DecodeIcon(const Extension
* extension
,
3130 int preferred_icon_size
,
3131 ExtensionIconSet::MatchType match_type
,
3132 scoped_ptr
<SkBitmap
>* result
) {
3133 std::string path
= extension
->icons().Get(preferred_icon_size
, match_type
);
3134 int size
= extension
->icons().GetIconSizeFromPath(path
);
3135 ExtensionResource icon_resource
= extension
->GetResource(path
);
3136 DecodeIconFromPath(icon_resource
.GetFilePath(), size
, result
);
3140 void Extension::DecodeIcon(const Extension
* extension
,
3142 scoped_ptr
<SkBitmap
>* result
) {
3143 DecodeIcon(extension
, icon_size
, ExtensionIconSet::MATCH_EXACTLY
, result
);
3147 void Extension::DecodeIconFromPath(const FilePath
& icon_path
,
3149 scoped_ptr
<SkBitmap
>* result
) {
3150 if (icon_path
.empty())
3153 std::string file_contents
;
3154 if (!file_util::ReadFileToString(icon_path
, &file_contents
)) {
3155 DLOG(ERROR
) << "Could not read icon file: " << icon_path
.LossyDisplayName();
3159 // Decode the image using WebKit's image decoder.
3160 const unsigned char* data
=
3161 reinterpret_cast<const unsigned char*>(file_contents
.data());
3162 webkit_glue::ImageDecoder decoder
;
3163 scoped_ptr
<SkBitmap
> decoded(new SkBitmap());
3164 *decoded
= decoder
.Decode(data
, file_contents
.length());
3165 if (decoded
->empty()) {
3166 DLOG(ERROR
) << "Could not decode icon file: "
3167 << icon_path
.LossyDisplayName();
3171 if (decoded
->width() != icon_size
|| decoded
->height() != icon_size
) {
3172 DLOG(ERROR
) << "Icon file has unexpected size: "
3173 << base::IntToString(decoded
->width()) << "x"
3174 << base::IntToString(decoded
->height());
3178 result
->swap(decoded
);
3182 const gfx::ImageSkia
& Extension::GetDefaultIcon(bool is_app
) {
3183 int id
= is_app
? IDR_APP_DEFAULT_ICON
: IDR_EXTENSION_DEFAULT_ICON
;
3184 return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id
);
3188 GURL
Extension::GetBaseURLFromExtensionId(const std::string
& extension_id
) {
3189 return GURL(std::string(extensions::kExtensionScheme
) +
3190 content::kStandardSchemeSeparator
+ extension_id
+ "/");
3193 bool Extension::InitFromValue(int flags
, string16
* error
) {
3196 base::AutoLock
auto_lock(runtime_data_lock_
);
3198 // Initialize permissions with an empty, default permission set.
3199 runtime_data_
.SetActivePermissions(new PermissionSet());
3200 optional_permission_set_
= new PermissionSet();
3201 required_permission_set_
= new PermissionSet();
3203 creation_flags_
= flags
;
3205 // Important to load manifest version first because many other features
3206 // depend on its value.
3207 if (!LoadManifestVersion(error
))
3210 // Validate minimum Chrome version. We don't need to store this, since the
3211 // extension is not valid if it is incorrect
3212 if (!CheckMinimumChromeVersion(error
))
3215 if (!LoadRequiredFeatures(error
))
3218 // We don't need to validate because InitExtensionID already did that.
3219 manifest_
->GetString(keys::kPublicKey
, &public_key_
);
3221 extension_url_
= Extension::GetBaseURLFromExtensionId(id());
3223 // Load App settings. LoadExtent at least has to be done before
3224 // ParsePermissions(), because the valid permissions depend on what type of
3226 if (is_app() && !LoadAppFeatures(error
))
3229 APIPermissionSet api_permissions
;
3230 URLPatternSet host_permissions
;
3231 if (!ParsePermissions(keys::kPermissions
,
3234 &host_permissions
)) {
3238 // TODO(jeremya/kalman) do this via the features system by exposing the
3239 // app.window API to platform apps, with no dependency on any permissions.
3240 // See http://crbug.com/120069.
3241 if (is_platform_app()) {
3242 api_permissions
.insert(APIPermission::kAppCurrentWindowInternal
);
3243 api_permissions
.insert(APIPermission::kAppRuntime
);
3244 api_permissions
.insert(APIPermission::kAppWindow
);
3247 if (from_webstore()) {
3249 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id());
3252 APIPermissionSet optional_api_permissions
;
3253 URLPatternSet optional_host_permissions
;
3254 if (!ParsePermissions(keys::kOptionalPermissions
,
3256 &optional_api_permissions
,
3257 &optional_host_permissions
)) {
3261 if (!LoadAppIsolation(api_permissions
, error
))
3264 if (!LoadSharedFeatures(api_permissions
, error
))
3267 if (!LoadExtensionFeatures(api_permissions
, error
))
3270 if (!LoadThemeFeatures(error
))
3273 if (HasMultipleUISurfaces()) {
3274 *error
= ASCIIToUTF16(errors::kOneUISurfaceOnly
);
3278 runtime_data_
.SetActivePermissions(new PermissionSet(
3279 this, api_permissions
, host_permissions
));
3280 required_permission_set_
= new PermissionSet(
3281 this, api_permissions
, host_permissions
);
3282 optional_permission_set_
= new PermissionSet(
3283 optional_api_permissions
, optional_host_permissions
, URLPatternSet());
3288 GURL
Extension::GetHomepageURL() const {
3289 if (homepage_url_
.is_valid())
3290 return homepage_url_
;
3292 return UpdatesFromGallery() ?
3293 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()) : GURL();
3296 std::set
<FilePath
> Extension::GetBrowserImages() const {
3297 std::set
<FilePath
> image_paths
;
3298 // TODO(viettrungluu): These |FilePath::FromWStringHack(UTF8ToWide())|
3299 // indicate that we're doing something wrong.
3302 for (ExtensionIconSet::IconMap::const_iterator iter
= icons().map().begin();
3303 iter
!= icons().map().end(); ++iter
) {
3304 image_paths
.insert(FilePath::FromWStringHack(UTF8ToWide(iter
->second
)));
3308 DictionaryValue
* theme_images
= GetThemeImages();
3310 for (DictionaryValue::key_iterator it
= theme_images
->begin_keys();
3311 it
!= theme_images
->end_keys(); ++it
) {
3313 if (theme_images
->GetStringWithoutPathExpansion(*it
, &val
))
3314 image_paths
.insert(FilePath::FromWStringHack(UTF8ToWide(val
)));
3318 if (page_action_info() && !page_action_info()->default_icon
.empty()) {
3319 for (ExtensionIconSet::IconMap::const_iterator iter
=
3320 page_action_info()->default_icon
.map().begin();
3321 iter
!= page_action_info()->default_icon
.map().end();
3323 image_paths
.insert(FilePath::FromWStringHack(UTF8ToWide(iter
->second
)));
3327 if (browser_action_info() && !browser_action_info()->default_icon
.empty()) {
3328 for (ExtensionIconSet::IconMap::const_iterator iter
=
3329 browser_action_info()->default_icon
.map().begin();
3330 iter
!= browser_action_info()->default_icon
.map().end();
3332 image_paths
.insert(FilePath::FromWStringHack(UTF8ToWide(iter
->second
)));
3339 GURL
Extension::GetFullLaunchURL() const {
3340 return launch_local_path().empty() ? GURL(launch_web_url()) :
3341 url().Resolve(launch_local_path());
3344 static std::string
SizeToString(const gfx::Size
& max_size
) {
3345 return base::IntToString(max_size
.width()) + "x" +
3346 base::IntToString(max_size
.height());
3350 void Extension::SetScriptingWhitelist(
3351 const Extension::ScriptingWhitelist
& whitelist
) {
3352 ScriptingWhitelist
* current_whitelist
=
3353 ExtensionConfig::GetInstance()->whitelist();
3354 current_whitelist
->clear();
3355 for (ScriptingWhitelist::const_iterator it
= whitelist
.begin();
3356 it
!= whitelist
.end(); ++it
) {
3357 current_whitelist
->push_back(*it
);
3362 const Extension::ScriptingWhitelist
* Extension::GetScriptingWhitelist() {
3363 return ExtensionConfig::GetInstance()->whitelist();
3366 void Extension::SetCachedImage(const ExtensionResource
& source
,
3367 const SkBitmap
& image
,
3368 const gfx::Size
& original_size
) const {
3369 DCHECK(source
.extension_root() == path()); // The resource must come from
3371 const FilePath
& path
= source
.relative_path();
3372 gfx::Size
actual_size(image
.width(), image
.height());
3373 std::string location
;
3374 if (actual_size
!= original_size
)
3375 location
= SizeToString(actual_size
);
3376 image_cache_
[ImageCacheKey(path
, location
)] = image
;
3379 bool Extension::HasCachedImage(const ExtensionResource
& source
,
3380 const gfx::Size
& max_size
) const {
3381 DCHECK(source
.extension_root() == path()); // The resource must come from
3383 return GetCachedImageImpl(source
, max_size
) != NULL
;
3386 SkBitmap
Extension::GetCachedImage(const ExtensionResource
& source
,
3387 const gfx::Size
& max_size
) const {
3388 DCHECK(source
.extension_root() == path()); // The resource must come from
3390 SkBitmap
* image
= GetCachedImageImpl(source
, max_size
);
3391 return image
? *image
: SkBitmap();
3394 SkBitmap
* Extension::GetCachedImageImpl(const ExtensionResource
& source
,
3395 const gfx::Size
& max_size
) const {
3396 const FilePath
& path
= source
.relative_path();
3398 // Look for exact size match.
3399 ImageCache::iterator i
= image_cache_
.find(
3400 ImageCacheKey(path
, SizeToString(max_size
)));
3401 if (i
!= image_cache_
.end())
3402 return &(i
->second
);
3404 // If we have the original size version cached, return that if it's small
3406 i
= image_cache_
.find(ImageCacheKey(path
, std::string()));
3407 if (i
!= image_cache_
.end()) {
3408 const SkBitmap
& image
= i
->second
;
3409 if (image
.width() <= max_size
.width() &&
3410 image
.height() <= max_size
.height()) {
3411 return &(i
->second
);
3418 ExtensionResource
Extension::GetIconResource(
3419 int size
, ExtensionIconSet::MatchType match_type
) const {
3420 std::string path
= icons().Get(size
, match_type
);
3421 return path
.empty() ? ExtensionResource() : GetResource(path
);
3424 GURL
Extension::GetIconURL(int size
,
3425 ExtensionIconSet::MatchType match_type
) const {
3426 std::string path
= icons().Get(size
, match_type
);
3427 return path
.empty() ? GURL() : GetResourceURL(path
);
3430 bool Extension::ParsePermissions(const char* key
,
3432 APIPermissionSet
* api_permissions
,
3433 URLPatternSet
* host_permissions
) {
3434 if (manifest_
->HasKey(key
)) {
3435 ListValue
* permissions
= NULL
;
3436 if (!manifest_
->GetList(key
, &permissions
)) {
3437 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
3438 errors::kInvalidPermissions
, "");
3442 // NOTE: We need to get the APIPermission before we check if features
3443 // associated with them are available because the feature system does not
3444 // know about aliases.
3446 std::vector
<std::string
> host_data
;
3447 if (!APIPermissionSet::ParseFromJSON(permissions
, api_permissions
,
3451 // Verify feature availability of permissions.
3452 std::vector
<APIPermission::ID
> to_remove
;
3453 SimpleFeatureProvider
* permission_features
=
3454 SimpleFeatureProvider::GetPermissionFeatures();
3455 for (APIPermissionSet::const_iterator it
= api_permissions
->begin();
3456 it
!= api_permissions
->end(); ++it
) {
3457 extensions::Feature
* feature
=
3458 permission_features
->GetFeature(it
->name());
3460 // The feature should exist since we just got an APIPermission
3461 // for it. The two systems should be updated together whenever a
3462 // permission is added.
3465 Feature::Availability availability
=
3466 feature
->IsAvailableToManifest(
3469 Feature::ConvertLocation(location()),
3470 manifest_version());
3471 if (!availability
.is_available()) {
3472 // Don't fail, but warn the developer that the manifest contains
3473 // unrecognized permissions. This may happen legitimately if the
3474 // extensions requests platform- or channel-specific permissions.
3475 install_warnings_
.push_back(InstallWarning(InstallWarning::FORMAT_TEXT
,
3476 availability
.message()));
3477 to_remove
.push_back(it
->id());
3481 if (it
->id() == APIPermission::kExperimental
) {
3482 if (!CanSpecifyExperimentalPermission()) {
3483 *error
= ASCIIToUTF16(errors::kExperimentalFlagRequired
);
3489 // Remove permissions that are not available to this extension.
3490 for (std::vector
<APIPermission::ID
>::const_iterator it
= to_remove
.begin();
3491 it
!= to_remove
.end(); ++it
) {
3492 api_permissions
->erase(*it
);
3495 // Parse host pattern permissions.
3496 const int kAllowedSchemes
= CanExecuteScriptEverywhere() ?
3497 URLPattern::SCHEME_ALL
: kValidHostPermissionSchemes
;
3499 for (std::vector
<std::string
>::const_iterator it
= host_data
.begin();
3500 it
!= host_data
.end(); ++it
) {
3501 const std::string
& permission_str
= *it
;
3503 // Check if it's a host pattern permission.
3504 URLPattern pattern
= URLPattern(kAllowedSchemes
);
3505 URLPattern::ParseResult parse_result
= pattern
.Parse(permission_str
);
3506 if (parse_result
== URLPattern::PARSE_SUCCESS
) {
3507 if (!CanSpecifyHostPermission(pattern
, *api_permissions
)) {
3508 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
3509 errors::kInvalidPermissionScheme
, permission_str
);
3513 // The path component is not used for host permissions, so we force it
3514 // to match all paths.
3515 pattern
.SetPath("/*");
3517 if (pattern
.MatchesScheme(chrome::kFileScheme
) &&
3518 !CanExecuteScriptEverywhere()) {
3519 wants_file_access_
= true;
3520 if (!(creation_flags_
& ALLOW_FILE_ACCESS
)) {
3521 pattern
.SetValidSchemes(
3522 pattern
.valid_schemes() & ~URLPattern::SCHEME_FILE
);
3526 host_permissions
->AddPattern(pattern
);
3530 // It's probably an unknown API permission. Do not throw an error so
3531 // extensions can retain backwards compatability (http://crbug.com/42742).
3532 install_warnings_
.push_back(InstallWarning(
3533 InstallWarning::FORMAT_TEXT
,
3535 "Permission '%s' is unknown or URL pattern is malformed.",
3536 permission_str
.c_str())));
3542 bool Extension::CanSilentlyIncreasePermissions() const {
3543 return location() != INTERNAL
;
3546 bool Extension::CanSpecifyHostPermission(const URLPattern
& pattern
,
3547 const APIPermissionSet
& permissions
) const {
3548 if (!pattern
.match_all_urls() &&
3549 pattern
.MatchesScheme(chrome::kChromeUIScheme
)) {
3550 // Regular extensions are only allowed access to chrome://favicon.
3551 if (pattern
.host() == chrome::kChromeUIFaviconHost
)
3554 // Experimental extensions are also allowed chrome://thumb.
3555 if (pattern
.host() == chrome::kChromeUIThumbnailHost
) {
3556 return permissions
.find(APIPermission::kExperimental
) !=
3560 // Component extensions can have access to all of chrome://*.
3561 if (CanExecuteScriptEverywhere())
3567 // Otherwise, the valid schemes were handled by URLPattern.
3571 bool Extension::HasAPIPermission(APIPermission::ID permission
) const {
3572 base::AutoLock
auto_lock(runtime_data_lock_
);
3573 return runtime_data_
.GetActivePermissions()->HasAPIPermission(permission
);
3576 bool Extension::HasAPIPermission(const std::string
& function_name
) const {
3577 base::AutoLock
auto_lock(runtime_data_lock_
);
3578 return runtime_data_
.GetActivePermissions()->
3579 HasAccessToFunction(function_name
);
3582 bool Extension::HasAPIPermissionForTab(int tab_id
,
3583 APIPermission::ID permission
) const {
3584 base::AutoLock
auto_lock(runtime_data_lock_
);
3585 if (runtime_data_
.GetActivePermissions()->HasAPIPermission(permission
))
3587 scoped_refptr
<const PermissionSet
> tab_specific_permissions
=
3588 runtime_data_
.GetTabSpecificPermissions(tab_id
);
3589 return tab_specific_permissions
.get() &&
3590 tab_specific_permissions
->HasAPIPermission(permission
);
3593 bool Extension::CheckAPIPermissionWithParam(APIPermission::ID permission
,
3594 const APIPermission::CheckParam
* param
) const {
3595 base::AutoLock
auto_lock(runtime_data_lock_
);
3596 return runtime_data_
.GetActivePermissions()->
3597 CheckAPIPermissionWithParam(permission
, param
);
3600 const URLPatternSet
& Extension::GetEffectiveHostPermissions() const {
3601 base::AutoLock
auto_lock(runtime_data_lock_
);
3602 return runtime_data_
.GetActivePermissions()->effective_hosts();
3605 bool Extension::HasHostPermission(const GURL
& url
) const {
3606 if (url
.SchemeIs(chrome::kChromeUIScheme
) &&
3607 url
.host() != chrome::kChromeUIFaviconHost
&&
3608 url
.host() != chrome::kChromeUIThumbnailHost
&&
3609 location() != Extension::COMPONENT
) {
3613 base::AutoLock
auto_lock(runtime_data_lock_
);
3614 return runtime_data_
.GetActivePermissions()->
3615 HasExplicitAccessToOrigin(url
);
3618 bool Extension::HasEffectiveAccessToAllHosts() const {
3619 base::AutoLock
auto_lock(runtime_data_lock_
);
3620 return runtime_data_
.GetActivePermissions()->HasEffectiveAccessToAllHosts();
3623 bool Extension::HasFullPermissions() const {
3624 base::AutoLock
auto_lock(runtime_data_lock_
);
3625 return runtime_data_
.GetActivePermissions()->HasEffectiveFullAccess();
3628 PermissionMessages
Extension::GetPermissionMessages() const {
3629 base::AutoLock
auto_lock(runtime_data_lock_
);
3630 if (IsTrustedId(id())) {
3631 return PermissionMessages();
3633 return runtime_data_
.GetActivePermissions()->GetPermissionMessages(
3638 std::vector
<string16
> Extension::GetPermissionMessageStrings() const {
3639 base::AutoLock
auto_lock(runtime_data_lock_
);
3640 if (IsTrustedId(id()))
3641 return std::vector
<string16
>();
3643 return runtime_data_
.GetActivePermissions()->GetWarningMessages(GetType());
3646 bool Extension::ShouldSkipPermissionWarnings() const {
3647 return IsTrustedId(id());
3650 void Extension::SetActivePermissions(
3651 const PermissionSet
* permissions
) const {
3652 base::AutoLock
auto_lock(runtime_data_lock_
);
3653 runtime_data_
.SetActivePermissions(permissions
);
3656 scoped_refptr
<const PermissionSet
>
3657 Extension::GetActivePermissions() const {
3658 base::AutoLock
auto_lock(runtime_data_lock_
);
3659 return runtime_data_
.GetActivePermissions();
3662 bool Extension::HasMultipleUISurfaces() const {
3663 int num_surfaces
= 0;
3665 if (page_action_info())
3668 if (browser_action_info())
3674 return num_surfaces
> 1;
3677 bool Extension::CanExecuteScriptOnPage(const GURL
& document_url
,
3678 const GURL
& top_frame_url
,
3680 const UserScript
* script
,
3681 std::string
* error
) const {
3682 base::AutoLock
auto_lock(runtime_data_lock_
);
3683 // The gallery is special-cased as a restricted URL for scripting to prevent
3684 // access to special JS bindings we expose to the gallery (and avoid things
3685 // like extensions removing the "report abuse" link).
3686 // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing
3687 // against the store app extent?
3688 GURL
store_url(extension_urls::GetWebstoreLaunchURL());
3689 if ((document_url
.host() == store_url
.host()) &&
3690 !CanExecuteScriptEverywhere() &&
3691 !CommandLine::ForCurrentProcess()->HasSwitch(
3692 switches::kAllowScriptingGallery
)) {
3694 *error
= errors::kCannotScriptGallery
;
3698 if (document_url
.SchemeIs(chrome::kChromeUIScheme
) &&
3699 !CanExecuteScriptEverywhere()) {
3703 if (top_frame_url
.SchemeIs(extensions::kExtensionScheme
) &&
3704 top_frame_url
.GetOrigin() !=
3705 GetBaseURLFromExtensionId(id()).GetOrigin() &&
3706 !CanExecuteScriptEverywhere()) {
3710 // If a tab ID is specified, try the tab-specific permissions.
3712 scoped_refptr
<const PermissionSet
> tab_permissions
=
3713 runtime_data_
.GetTabSpecificPermissions(tab_id
);
3714 if (tab_permissions
.get() &&
3715 tab_permissions
->explicit_hosts().MatchesSecurityOrigin(document_url
)) {
3720 // If a script is specified, use its matches.
3722 return script
->MatchesURL(document_url
);
3724 // Otherwise, see if this extension has permission to execute script
3725 // programmatically on pages.
3726 if (runtime_data_
.GetActivePermissions()->HasExplicitAccessToOrigin(
3732 *error
= ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage
,
3733 document_url
.spec());
3739 bool Extension::ShowConfigureContextMenus() const {
3740 // Don't show context menu for component extensions. We might want to show
3741 // options for component extension button but now there is no component
3742 // extension with options. All other menu items like uninstall have
3743 // no sense for component extensions.
3744 return location() != Extension::COMPONENT
;
3747 bool Extension::CanSpecifyExperimentalPermission() const {
3748 if (location() == Extension::COMPONENT
)
3751 if (CommandLine::ForCurrentProcess()->HasSwitch(
3752 switches::kEnableExperimentalExtensionApis
)) {
3756 // We rely on the webstore to check access to experimental. This way we can
3757 // whitelist extensions to have access to experimental in just the store, and
3758 // not have to push a new version of the client.
3759 if (from_webstore())
3765 bool Extension::CanExecuteScriptEverywhere() const {
3766 if (location() == Extension::COMPONENT
)
3769 ScriptingWhitelist
* whitelist
= ExtensionConfig::GetInstance()->whitelist();
3771 for (ScriptingWhitelist::const_iterator it
= whitelist
->begin();
3772 it
!= whitelist
->end(); ++it
) {
3781 bool Extension::CanCaptureVisiblePage(const GURL
& page_url
,
3783 std::string
* error
) const {
3785 scoped_refptr
<const PermissionSet
> tab_permissions
=
3786 GetTabSpecificPermissions(tab_id
);
3787 if (tab_permissions
.get() &&
3788 tab_permissions
->explicit_hosts().MatchesSecurityOrigin(page_url
)) {
3793 if (HasHostPermission(page_url
) || page_url
.GetOrigin() == url())
3797 *error
= ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage
,
3803 bool Extension::UpdatesFromGallery() const {
3804 return extension_urls::IsWebstoreUpdateUrl(update_url());
3807 bool Extension::OverlapsWithOrigin(const GURL
& origin
) const {
3808 if (url() == origin
)
3811 if (web_extent().is_empty())
3814 // Note: patterns and extents ignore port numbers.
3815 URLPattern
origin_only_pattern(kValidWebExtentSchemes
);
3816 if (!origin_only_pattern
.SetScheme(origin
.scheme()))
3818 origin_only_pattern
.SetHost(origin
.host());
3819 origin_only_pattern
.SetPath("/*");
3821 URLPatternSet origin_only_pattern_list
;
3822 origin_only_pattern_list
.AddPattern(origin_only_pattern
);
3824 return web_extent().OverlapsWith(origin_only_pattern_list
);
3827 Extension::SyncType
Extension::GetSyncType() const {
3828 if (!IsSyncable()) {
3829 // We have a non-standard location.
3830 return SYNC_TYPE_NONE
;
3833 // Disallow extensions with non-gallery auto-update URLs for now.
3835 // TODO(akalin): Relax this restriction once we've put in UI to
3836 // approve synced extensions.
3837 if (!update_url().is_empty() && !UpdatesFromGallery())
3838 return SYNC_TYPE_NONE
;
3840 // Disallow extensions with native code plugins.
3842 // TODO(akalin): Relax this restriction once we've put in UI to
3843 // approve synced extensions.
3844 if (!plugins().empty()) {
3845 return SYNC_TYPE_NONE
;
3848 switch (GetType()) {
3849 case Extension::TYPE_EXTENSION
:
3850 return SYNC_TYPE_EXTENSION
;
3852 case Extension::TYPE_USER_SCRIPT
:
3853 // We only want to sync user scripts with gallery update URLs.
3854 if (UpdatesFromGallery())
3855 return SYNC_TYPE_EXTENSION
;
3857 return SYNC_TYPE_NONE
;
3859 case Extension::TYPE_HOSTED_APP
:
3860 case Extension::TYPE_LEGACY_PACKAGED_APP
:
3861 return SYNC_TYPE_APP
;
3864 return SYNC_TYPE_NONE
;
3868 bool Extension::IsSyncable() const {
3869 // TODO(akalin): Figure out if we need to allow some other types.
3871 // Default apps are not synced because otherwise they will pollute profiles
3872 // that don't already have them. Specially, if a user doesn't have default
3873 // apps, creates a new profile (which get default apps) and then enables sync
3874 // for it, then their profile everywhere gets the default apps.
3875 bool is_syncable
= (location() == Extension::INTERNAL
&&
3876 !was_installed_by_default());
3877 // Sync the chrome web store to maintain its position on the new tab page.
3878 is_syncable
|= (id() == extension_misc::kWebStoreAppId
);
3882 bool Extension::RequiresSortOrdinal() const {
3883 return is_app() && (display_in_launcher_
|| display_in_new_tab_page_
);
3886 bool Extension::ShouldDisplayInAppLauncher() const {
3887 // Only apps should be displayed in the launcher.
3888 return is_app() && display_in_launcher_
;
3891 bool Extension::ShouldDisplayInNewTabPage() const {
3892 // Only apps should be displayed on the NTP.
3893 return is_app() && display_in_new_tab_page_
;
3896 bool Extension::InstallWarning::operator==(const InstallWarning
& other
) const {
3897 return format
== other
.format
&& message
== other
.message
;
3900 void PrintTo(const Extension::InstallWarning
& warning
, ::std::ostream
* os
) {
3901 *os
<< "InstallWarning(";
3902 switch (warning
.format
) {
3903 case Extension::InstallWarning::FORMAT_TEXT
:
3904 *os
<< "FORMAT_TEXT, \"";
3906 case Extension::InstallWarning::FORMAT_HTML
:
3907 *os
<< "FORMAT_HTML, \"";
3910 // This is just for test error messages, so no need to escape '"'
3911 // characters inside the message.
3912 *os
<< warning
.message
<< "\")";
3915 ExtensionInfo::ExtensionInfo(const DictionaryValue
* manifest
,
3916 const std::string
& id
,
3917 const FilePath
& path
,
3918 Extension::Location location
)
3920 extension_path(path
),
3921 extension_location(location
) {
3923 extension_manifest
.reset(manifest
->DeepCopy());
3926 bool Extension::ShouldDisplayInExtensionSettings() const {
3927 // Don't show for themes since the settings UI isn't really useful for them.
3931 // Don't show component extensions because they are only extensions as an
3932 // implementation detail of Chrome.
3933 if (location() == Extension::COMPONENT
&&
3934 !CommandLine::ForCurrentProcess()->HasSwitch(
3935 switches::kShowComponentExtensionOptions
)) {
3939 // Always show unpacked extensions and apps.
3940 if (location() == Extension::LOAD
)
3943 // Unless they are unpacked, never show hosted apps. Note: We intentionally
3944 // show packaged apps and platform apps because there are some pieces of
3945 // functionality that are only available in chrome://extensions/ but which
3946 // are needed for packaged and platform apps. For example, inspecting
3947 // background pages. See http://crbug.com/116134.
3948 if (is_hosted_app())
3954 bool Extension::HasContentScriptAtURL(const GURL
& url
) const {
3955 for (UserScriptList::const_iterator it
= content_scripts_
.begin();
3956 it
!= content_scripts_
.end(); ++it
) {
3957 if (it
->MatchesURL(url
))
3963 scoped_refptr
<const PermissionSet
> Extension::GetTabSpecificPermissions(
3965 base::AutoLock
auto_lock(runtime_data_lock_
);
3966 return runtime_data_
.GetTabSpecificPermissions(tab_id
);
3969 void Extension::UpdateTabSpecificPermissions(
3971 scoped_refptr
<const PermissionSet
> permissions
) const {
3972 base::AutoLock
auto_lock(runtime_data_lock_
);
3973 runtime_data_
.UpdateTabSpecificPermissions(tab_id
, permissions
);
3976 void Extension::ClearTabSpecificPermissions(int tab_id
) const {
3977 base::AutoLock
auto_lock(runtime_data_lock_
);
3978 runtime_data_
.ClearTabSpecificPermissions(tab_id
);
3981 bool Extension::CheckMinimumChromeVersion(string16
* error
) const {
3982 if (!manifest_
->HasKey(keys::kMinimumChromeVersion
))
3984 std::string minimum_version_string
;
3985 if (!manifest_
->GetString(keys::kMinimumChromeVersion
,
3986 &minimum_version_string
)) {
3987 *error
= ASCIIToUTF16(errors::kInvalidMinimumChromeVersion
);
3991 Version
minimum_version(minimum_version_string
);
3992 if (!minimum_version
.IsValid()) {
3993 *error
= ASCIIToUTF16(errors::kInvalidMinimumChromeVersion
);
3997 chrome::VersionInfo current_version_info
;
3998 if (!current_version_info
.is_valid()) {
4003 Version
current_version(current_version_info
.Version());
4004 if (!current_version
.IsValid()) {
4009 if (current_version
.CompareTo(minimum_version
) < 0) {
4010 *error
= ExtensionErrorUtils::FormatErrorMessageUTF16(
4011 errors::kChromeVersionTooLow
,
4012 l10n_util::GetStringUTF8(IDS_PRODUCT_NAME
),
4013 minimum_version_string
);
4019 bool Extension::CheckPlatformAppFeatures(std::string
* utf8_error
) const {
4020 if (!is_platform_app())
4023 if (!has_background_page()) {
4024 *utf8_error
= errors::kBackgroundRequiredForPlatformApps
;
4028 if (!incognito_split_mode_
) {
4029 *utf8_error
= errors::kInvalidIncognitoModeForPlatformApp
;
4036 bool Extension::CheckConflictingFeatures(std::string
* utf8_error
) const {
4037 if (has_lazy_background_page() &&
4038 HasAPIPermission(APIPermission::kWebRequest
)) {
4039 *utf8_error
= errors::kWebRequestConflictsWithLazyBackground
;
4046 ExtensionInfo::~ExtensionInfo() {}
4048 Extension::RuntimeData::RuntimeData() {}
4049 Extension::RuntimeData::RuntimeData(const PermissionSet
* active
)
4050 : active_permissions_(active
) {}
4051 Extension::RuntimeData::~RuntimeData() {}
4053 scoped_refptr
<const PermissionSet
>
4054 Extension::RuntimeData::GetActivePermissions() const {
4055 return active_permissions_
;
4058 void Extension::RuntimeData::SetActivePermissions(
4059 const PermissionSet
* active
) {
4060 active_permissions_
= active
;
4063 scoped_refptr
<const PermissionSet
>
4064 Extension::RuntimeData::GetTabSpecificPermissions(int tab_id
) const {
4065 CHECK_GE(tab_id
, 0);
4066 TabPermissionsMap::const_iterator it
= tab_specific_permissions_
.find(tab_id
);
4067 return (it
!= tab_specific_permissions_
.end()) ? it
->second
: NULL
;
4070 void Extension::RuntimeData::UpdateTabSpecificPermissions(
4072 scoped_refptr
<const PermissionSet
> permissions
) {
4073 CHECK_GE(tab_id
, 0);
4074 if (tab_specific_permissions_
.count(tab_id
)) {
4075 tab_specific_permissions_
[tab_id
] = PermissionSet::CreateUnion(
4076 tab_specific_permissions_
[tab_id
],
4079 tab_specific_permissions_
[tab_id
] = permissions
;
4083 void Extension::RuntimeData::ClearTabSpecificPermissions(int tab_id
) {
4084 CHECK_GE(tab_id
, 0);
4085 tab_specific_permissions_
.erase(tab_id
);
4088 UnloadedExtensionInfo::UnloadedExtensionInfo(
4089 const Extension
* extension
,
4090 extension_misc::UnloadedExtensionReason reason
)
4092 already_disabled(false),
4093 extension(extension
) {}
4095 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
4096 const Extension
* extension
,
4097 const PermissionSet
* permissions
,
4100 extension(extension
),
4101 permissions(permissions
) {}
4103 } // namespace extensions