Extract SIGPIPE ignoring code to a common place.
[chromium-blink-merge.git] / chrome / common / extensions / extension.cc
blobbcd68065809d98c572eb949498c861f76ccb44d9
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"
7 #include <ostream>
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"
54 #if defined(OS_WIN)
55 #include "base/win/metro.h"
56 #include "grit/generated_resources.h"
57 #endif
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 {
70 namespace {
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;
81 // KEY MARKERS
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.
99 "connect-src *;"
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.
110 "media-src *;";
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
118 // address.
119 static void ConvertHexadecimalToIDAlphabet(std::string* id) {
120 for (size_t i = 0; i < id->size(); ++i) {
121 int val;
122 if (base::HexStringToInt(base::StringPiece(id->begin() + i,
123 id->begin() + i + 1),
124 &val)) {
125 (*id)[i] = val + 'a';
126 } else {
127 (*id)[i] = 'a';
132 // Strips leading slashes from the file path. Returns true iff the final path is
133 // non empty.
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) {
137 *path = "";
138 return false;
141 *path = path->substr(first_non_slash);
142 return true;
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,
153 string16* error) {
154 DCHECK(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);
162 return false;
165 if (!NormalizeAndValidatePath(&icon_path)) {
166 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
167 errors::kInvalidIconPath, key);
168 return false;
171 icons->Add(icon_sizes[i], icon_path);
174 return true;
177 // A singleton object containing global data needed by the extension objects.
178 class ExtensionConfig {
179 public:
180 static ExtensionConfig* GetInstance() {
181 return Singleton<ExtensionConfig>::get();
184 Extension::ScriptingWhitelist* whitelist() { return &scripting_whitelist_; }
186 private:
187 friend struct DefaultSingletonTraits<ExtensionConfig>;
189 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.
215 switch (location) {
216 // Component extensions can not be overriden by any other type.
217 case Extension::COMPONENT:
218 rank = 6;
219 break;
221 // Policy controlled extensions may not be overridden by any type
222 // that is not part of chrome.
223 case Extension::EXTERNAL_POLICY_DOWNLOAD:
224 rank = 5;
225 break;
227 // A developer-loaded extension should override any installed type
228 // that a user can disable.
229 case Extension::LOAD:
230 rank = 4;
231 break;
233 // The relative priority of various external sources is not important,
234 // but having some order ensures deterministic behavior.
235 case Extension::EXTERNAL_REGISTRY:
236 rank = 3;
237 break;
239 case Extension::EXTERNAL_PREF:
240 rank = 2;
241 break;
243 case Extension::EXTERNAL_PREF_DOWNLOAD:
244 rank = 1;
245 break;
247 // User installed extensions are overridden by any external type.
248 case Extension::INTERNAL:
249 rank = 0;
250 break;
252 default:
253 NOTREACHED() << "Need to add new extension locaton " << location;
256 CHECK(rank != kInvalidRank);
257 return rank;
260 bool ReadLaunchDimension(const extensions::Manifest* manifest,
261 const char* key,
262 int* target,
263 bool is_valid_container,
264 string16* error) {
265 Value* temp = NULL;
266 if (manifest->Get(key, &temp)) {
267 if (!is_valid_container) {
268 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
269 errors::kInvalidLaunchValueContainer,
270 key);
271 return false;
273 if (!temp->GetAsInteger(target) || *target < 0) {
274 *target = 0;
275 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
276 errors::kInvalidLaunchValue,
277 key);
278 return false;
281 return true;
284 } // namespace
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");
293 #if defined(OS_WIN)
294 const char Extension::kExtensionRegistryPath[] =
295 "Software\\Google\\Chrome\\Extensions";
296 #endif
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()
313 : webgl(false),
314 css3d(false),
315 npapi(false) {
318 Extension::Requirements::~Requirements() {}
320 Extension::InputComponentInfo::InputComponentInfo()
321 : type(INPUT_COMPONENT_TYPE_NONE),
322 shortcut_alt(false),
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() {}
339 // Extension
342 // static
343 scoped_refptr<Extension> Extension::Create(const FilePath& path,
344 Location location,
345 const DictionaryValue& value,
346 int flags,
347 std::string* utf8_error) {
348 return Extension::Create(path,
349 location,
350 value,
351 flags,
352 std::string(), // ID is ignored if empty.
353 utf8_error);
356 scoped_refptr<Extension> Extension::Create(const FilePath& path,
357 Location location,
358 const DictionaryValue& value,
359 int flags,
360 const std::string& explicit_id,
361 std::string* utf8_error) {
362 DCHECK(utf8_error);
363 string16 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);
370 return NULL;
373 InstallWarningVector install_warnings;
374 manifest->ValidateManifest(utf8_error, &install_warnings);
375 if (!utf8_error->empty())
376 return NULL;
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);
383 return NULL;
386 if (!extension->CheckPlatformAppFeatures(utf8_error) ||
387 !extension->CheckConflictingFeatures(utf8_error)) {
388 return NULL;
391 return extension;
394 // static
395 Extension::Location Extension::GetHigherPriorityLocation(
396 Extension::Location loc1, Extension::Location loc2) {
397 if (loc1 == loc2)
398 return loc1;
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();
415 } else {
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) {
436 #if defined(OS_WIN)
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' &&
442 path_str[1] == ':')
443 path_str[0] += ('A' - 'a');
445 return FilePath(path_str);
446 #else
447 return path;
448 #endif
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());
469 // static
470 bool Extension::IsExtension(const FilePath& file_name) {
471 return file_name.MatchesExtension(chrome::kExtensionFileExtension);
474 // static
475 bool Extension::IdIsValid(const std::string& id) {
476 // Verify that the id is legal.
477 if (id.size() != (kIdSize * 2))
478 return false;
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')
485 return false;
487 return true;
490 // static
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));
496 std::string id;
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();
521 // static
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));
538 return ret_val;
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)
569 const {
570 // For old manifest versions which do not specify web_accessible_resources
571 // we always allow resource loads.
572 if (manifest_version_ < 2 && !HasWebAccessibleResources())
573 return true;
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) {
594 DCHECK(output);
595 uint8 hash[Extension::kIdSize];
596 crypto::SHA256HashString(input, hash, sizeof(hash));
597 *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash)));
598 ConvertHexadecimalToIDAlphabet(output);
600 return true;
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,
607 string16* error,
608 UserScript* result) {
609 // run_at
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));
616 return false;
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);
625 } else {
626 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
627 errors::kInvalidRunAt,
628 base::IntToString(definition_index));
629 return false;
633 // all frames
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));
639 return false;
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));
650 return false;
653 if (matches->GetSize() == 0) {
654 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
655 errors::kInvalidMatchCount,
656 base::IntToString(definition_index));
657 return false;
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);
667 return false;
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));
681 return false;
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);
696 // exclude_matches
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));
703 return false;
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);
714 return false;
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));
726 return false;
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)) {
736 return false;
739 if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs,
740 error, &UserScript::add_exclude_glob, result)) {
741 return false;
744 // js and css keys
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));
751 return false;
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));
760 return false;
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));
768 return false;
771 if (js) {
772 for (size_t script_index = 0; script_index < js->GetSize();
773 ++script_index) {
774 const Value* value;
775 std::string relative;
776 if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) {
777 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
778 errors::kInvalidJs,
779 base::IntToString(definition_index),
780 base::IntToString(script_index));
781 return false;
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));
790 if (css) {
791 for (size_t script_index = 0; script_index < css->GetSize();
792 ++script_index) {
793 const Value* value;
794 std::string relative;
795 if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) {
796 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
797 errors::kInvalidCss,
798 base::IntToString(definition_index),
799 base::IntToString(script_index));
800 return false;
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));
809 return true;
812 bool Extension::LoadGlobsHelper(
813 const DictionaryValue* content_script,
814 int content_script_index,
815 const char* globs_property_name,
816 string16* error,
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);
828 return false;
831 for (size_t i = 0; i < list->GetSize(); ++i) {
832 std::string glob;
833 if (!list->GetString(i, &glob)) {
834 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
835 errors::kInvalidGlob,
836 base::IntToString(content_script_index),
837 globs_property_name,
838 base::IntToString(i));
839 return false;
842 (instance->*add_method)(glob);
845 return true;
848 scoped_ptr<Extension::ActionInfo> Extension::LoadExtensionActionInfoHelper(
849 const DictionaryValue* extension_action,
850 ActionInfo::Type action_type,
851 string16* error) {
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) {
862 std::string path;
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);
869 break;
873 std::string id;
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>();
879 result->id = id;
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,
890 &icons_value)) {
891 if (!LoadIconsFromDictionary(icons_value,
892 extension_misc::kExtensionActionIconSizes,
893 extension_misc::kNumExtensionActionIconSizes,
894 &result->default_icon,
895 error)) {
896 return scoped_ptr<ActionInfo>();
898 } else if (extension_action->GetString(keys::kPageActionDefaultIcon,
899 &default_icon) &&
900 NormalizeAndValidatePath(&default_icon)) {
901 result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION,
902 default_icon);
903 } else {
904 *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath);
905 return scoped_ptr<ActionInfo>();
909 // Read the page action title from |default_title| if present, |name| if not
910 // (both optional).
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)) {
931 if (popup_key) {
932 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
933 errors::kInvalidPageActionOldAndNewKeys,
934 keys::kPageActionDefaultPopup,
935 keys::kPageActionPopup);
936 return scoped_ptr<ActionInfo>();
938 popup_key = keys::kPageActionPopup;
941 if (popup_key) {
942 const DictionaryValue* popup = NULL;
943 std::string url_str;
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>();
954 } else {
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>();
967 } else {
968 DCHECK(result->default_popup_url.is_empty())
969 << "Shouldn't be possible for the popup to be set.";
973 return result.Pass();
976 // static
977 bool Extension::InitExtensionID(extensions::Manifest* manifest,
978 const FilePath& path,
979 const std::string& explicit_id,
980 int creation_flags,
981 string16* error) {
982 if (!explicit_id.empty()) {
983 manifest->set_extension_id(explicit_id);
984 return true;
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);
995 return false;
997 manifest->set_extension_id(extension_id);
998 return true;
1001 if (creation_flags & REQUIRE_KEY) {
1002 *error = ASCIIToUTF16(errors::kInvalidKey);
1003 return false;
1004 } else {
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.";
1011 return false;
1013 manifest->set_extension_id(extension_id);
1014 return true;
1018 bool Extension::LoadRequiredFeatures(string16* error) {
1019 if (!LoadName(error) ||
1020 !LoadVersion(error))
1021 return false;
1022 return true;
1025 bool Extension::LoadName(string16* error) {
1026 string16 localized_name;
1027 if (!manifest_->GetString(keys::kName, &localized_name)) {
1028 *error = ASCIIToUTF16(errors::kInvalidName);
1029 return false;
1031 non_localized_name_ = UTF16ToUTF8(localized_name);
1032 base::i18n::AdjustStringForLocaleDirection(&localized_name);
1033 name_ = UTF16ToUTF8(localized_name);
1034 return true;
1037 bool Extension::LoadDescription(string16* error) {
1038 if (manifest_->HasKey(keys::kDescription) &&
1039 !manifest_->GetString(keys::kDescription, &description_)) {
1040 *error = ASCIIToUTF16(errors::kInvalidDescription);
1041 return false;
1043 return true;
1046 bool Extension::LoadAppFeatures(string16* error) {
1047 if (!LoadExtent(keys::kWebURLs, &extent_,
1048 errors::kInvalidWebURLs, errors::kInvalidWebURL, error) ||
1049 !LoadLaunchURL(error) ||
1050 !LoadLaunchContainer(error)) {
1051 return false;
1053 if (manifest_->HasKey(keys::kDisplayInLauncher) &&
1054 !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) {
1055 *error = ASCIIToUTF16(errors::kInvalidDisplayInLauncher);
1056 return false;
1058 if (manifest_->HasKey(keys::kDisplayInNewTabPage)) {
1059 if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage,
1060 &display_in_new_tab_page_)) {
1061 *error = ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage);
1062 return false;
1064 } else {
1065 // Inherit default from display_in_launcher property.
1066 display_in_new_tab_page_ = display_in_launcher_;
1068 return true;
1071 bool Extension::LoadOAuth2Info(string16* error) {
1072 if (!manifest_->HasKey(keys::kOAuth2))
1073 return true;
1075 if (!manifest_->GetString(keys::kOAuth2ClientId, &oauth2_info_.client_id) ||
1076 oauth2_info_.client_id.empty()) {
1077 *error = ASCIIToUTF16(errors::kInvalidOAuth2ClientId);
1078 return false;
1081 ListValue* list = NULL;
1082 if (!manifest_->GetList(keys::kOAuth2Scopes, &list)) {
1083 *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes);
1084 return false;
1087 for (size_t i = 0; i < list->GetSize(); ++i) {
1088 std::string scope;
1089 if (!list->GetString(i, &scope)) {
1090 *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes);
1091 return false;
1093 oauth2_info_.scopes.push_back(scope);
1096 return true;
1099 bool Extension::LoadExtent(const char* key,
1100 URLPatternSet* extent,
1101 const char* list_error,
1102 const char* value_error,
1103 string16* error) {
1104 Value* temp_pattern_value = NULL;
1105 if (!manifest_->Get(key, &temp_pattern_value))
1106 return true;
1108 if (temp_pattern_value->GetType() != Value::TYPE_LIST) {
1109 *error = ASCIIToUTF16(list_error);
1110 return false;
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);
1120 return false;
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(
1132 value_error,
1133 base::UintToString(i),
1134 URLPattern::GetParseResultString(parse_result));
1135 return false;
1138 // Do not allow authors to claim "<all_urls>".
1139 if (pattern.match_all_urls()) {
1140 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1141 value_error,
1142 base::UintToString(i),
1143 errors::kCannotClaimAllURLsInExtent);
1144 return false;
1147 // Do not allow authors to claim "*" for host.
1148 if (pattern.host().empty()) {
1149 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1150 value_error,
1151 base::UintToString(i),
1152 errors::kCannotClaimAllHostsInExtent);
1153 return false;
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(
1160 value_error,
1161 base::UintToString(i),
1162 errors::kNoWildCardsInPaths);
1163 return false;
1165 pattern.SetPath(pattern.path() + '*');
1167 extent->AddPattern(pattern);
1170 return true;
1173 bool Extension::LoadLaunchURL(string16* error) {
1174 Value* temp = NULL;
1176 // launch URL can be either local (to chrome-extension:// root) or an absolute
1177 // web URL.
1178 if (manifest_->Get(keys::kLaunchLocalPath, &temp)) {
1179 if (manifest_->Get(keys::kLaunchWebURL, NULL)) {
1180 *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive);
1181 return false;
1184 if (manifest_->Get(keys::kWebURLs, NULL)) {
1185 *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive);
1186 return false;
1189 std::string launch_path;
1190 if (!temp->GetAsString(&launch_path)) {
1191 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1192 errors::kInvalidLaunchValue,
1193 keys::kLaunchLocalPath);
1194 return false;
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);
1203 return false;
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);
1213 return false;
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);
1223 return false;
1226 launch_web_url_ = launch_url;
1227 } else if (is_legacy_packaged_app() || is_hosted_app()) {
1228 *error = ASCIIToUTF16(errors::kLaunchURLRequired);
1229 return false;
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);
1240 return false;
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()) {
1268 std::string path(
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();
1282 return true;
1285 bool Extension::LoadLaunchContainer(string16* error) {
1286 Value* tmp_launcher_container = NULL;
1287 if (!manifest_->Get(keys::kLaunchContainer, &tmp_launcher_container))
1288 return true;
1290 std::string launch_container_string;
1291 if (!tmp_launcher_container->GetAsString(&launch_container_string)) {
1292 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer);
1293 return false;
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;
1300 } else {
1301 *error = ASCIIToUTF16(errors::kInvalidLaunchContainer);
1302 return false;
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(),
1311 keys::kLaunchWidth,
1312 &launch_width_,
1313 can_specify_initial_size,
1314 error)) {
1315 return false;
1318 // Validate container height if present.
1319 if (!ReadLaunchDimension(manifest_.get(),
1320 keys::kLaunchHeight,
1321 &launch_height_,
1322 can_specify_initial_size,
1323 error)) {
1324 return false;
1327 return true;
1330 bool Extension::LoadSharedFeatures(
1331 const APIPermissionSet& api_permissions,
1332 string16* error) {
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))
1353 return false;
1355 return true;
1358 bool Extension::LoadVersion(string16* error) {
1359 std::string version_str;
1360 if (!manifest_->GetString(keys::kVersion, &version_str)) {
1361 *error = ASCIIToUTF16(errors::kInvalidVersion);
1362 return false;
1364 version_.reset(new Version(version_str));
1365 if (!version_->IsValid() || version_->components().size() > 4) {
1366 *error = ASCIIToUTF16(errors::kInvalidVersion);
1367 return false;
1369 return true;
1372 bool Extension::LoadManifestVersion(string16* error) {
1373 // Get the original value out of the dictionary so that we can validate it
1374 // more strictly.
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);
1380 return false;
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));
1392 return false;
1395 return true;
1398 bool Extension::LoadHomepageURL(string16* error) {
1399 if (!manifest_->HasKey(keys::kHomepageURL))
1400 return true;
1401 std::string tmp_homepage_url;
1402 if (!manifest_->GetString(keys::kHomepageURL, &tmp_homepage_url)) {
1403 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1404 errors::kInvalidHomepageURL, "");
1405 return false;
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);
1413 return false;
1415 return true;
1418 bool Extension::LoadUpdateURL(string16* error) {
1419 if (!manifest_->HasKey(keys::kUpdateURL))
1420 return true;
1421 std::string tmp_update_url;
1422 if (!manifest_->GetString(keys::kUpdateURL, &tmp_update_url)) {
1423 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1424 errors::kInvalidUpdateURL, "");
1425 return false;
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);
1432 return false;
1434 return true;
1437 bool Extension::LoadIcons(string16* error) {
1438 if (!manifest_->HasKey(keys::kIcons))
1439 return true;
1440 DictionaryValue* icons_value = NULL;
1441 if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) {
1442 *error = ASCIIToUTF16(errors::kInvalidIcons);
1443 return false;
1446 return LoadIconsFromDictionary(icons_value,
1447 extension_misc::kExtensionIconSizes,
1448 extension_misc::kNumExtensionIconSizes,
1449 &icons_,
1450 error);
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);
1458 return false;
1461 if (commands->size() > kMaxCommandsPerExtension) {
1462 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1463 errors::kInvalidKeyBindingTooMany,
1464 base::IntToString(kMaxCommandsPerExtension));
1465 return false;
1468 int command_index = 0;
1469 for (DictionaryValue::key_iterator iter = commands->begin_keys();
1470 iter != commands->end_keys(); ++iter) {
1471 ++command_index;
1473 DictionaryValue* command = NULL;
1474 if (!commands->GetDictionary(*iter, &command)) {
1475 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1476 errors::kInvalidKeyBindingDictionary,
1477 base::IntToString(command_index));
1478 return false;
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());
1492 } else {
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(), ""));
1509 return true;
1512 bool Extension::LoadPlugins(string16* error) {
1513 if (!manifest_->HasKey(keys::kPlugins))
1514 return true;
1516 ListValue* list_value = NULL;
1517 if (!manifest_->GetList(keys::kPlugins, &list_value)) {
1518 *error = ASCIIToUTF16(errors::kInvalidPlugins);
1519 return false;
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);
1526 return false;
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));
1533 return false;
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));
1542 return false;
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)
1551 continue;
1552 #elif defined(OS_WIN)
1553 if (base::win::IsMetroProcess()) {
1554 continue;
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;
1561 return true;
1564 bool Extension::LoadNaClModules(string16* error) {
1565 if (!manifest_->HasKey(keys::kNaClModules))
1566 return true;
1567 ListValue* list_value = NULL;
1568 if (!manifest_->GetList(keys::kNaClModules, &list_value)) {
1569 *error = ASCIIToUTF16(errors::kInvalidNaClModules);
1570 return false;
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);
1577 return false;
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));
1585 return false;
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));
1593 return false;
1596 nacl_modules_.push_back(NaClModuleInfo());
1597 nacl_modules_.back().url = GetResourceURL(path_str);
1598 nacl_modules_.back().mime_type = mime_type;
1601 return true;
1604 bool Extension::LoadWebAccessibleResources(string16* error) {
1605 if (!manifest_->HasKey(keys::kWebAccessibleResources))
1606 return true;
1607 ListValue* list_value = NULL;
1608 if (!manifest_->GetList(keys::kWebAccessibleResources, &list_value)) {
1609 *error = ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList);
1610 return false;
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));
1617 return false;
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());
1623 return false;
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);
1631 return true;
1634 bool Extension::LoadSandboxedPages(string16* error) {
1635 if (!manifest_->HasPath(keys::kSandboxedPages))
1636 return true;
1638 ListValue* list_value = NULL;
1639 if (!manifest_->GetList(keys::kSandboxedPages, &list_value)) {
1640 *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesList);
1641 return false;
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));
1648 return false;
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());
1654 return false;
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);
1666 return false;
1669 if (!ContentSecurityPolicyIsLegal(
1670 sandboxed_pages_content_security_policy_) ||
1671 !ContentSecurityPolicyIsSandboxed(
1672 sandboxed_pages_content_security_policy_, GetType())) {
1673 *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP);
1674 return false;
1676 } else {
1677 sandboxed_pages_content_security_policy_ =
1678 kDefaultSandboxedPageContentSecurityPolicy;
1679 CHECK(ContentSecurityPolicyIsSandboxed(
1680 sandboxed_pages_content_security_policy_, GetType()));
1683 return true;
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))
1694 return true;
1696 DictionaryValue* requirements_value = NULL;
1697 if (!manifest_->GetDictionary(keys::kRequirements, &requirements_value)) {
1698 *error = ASCIIToUTF16(errors::kInvalidRequirements);
1699 return false;
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);
1709 return false;
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);
1720 return false;
1722 if (*plugin_it == "npapi") {
1723 requirements_.npapi = plugin_required;
1724 } else {
1725 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1726 errors::kInvalidRequirement, *it);
1727 return false;
1730 } else if (*it == "3D") {
1731 ListValue* features = NULL;
1732 if (!requirement_value->GetListWithoutPathExpansion("features",
1733 &features) ||
1734 !features) {
1735 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1736 errors::kInvalidRequirement, *it);
1737 return false;
1740 for (base::ListValue::iterator feature_it = features->begin();
1741 feature_it != features->end();
1742 ++feature_it) {
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;
1749 } else {
1750 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1751 errors::kInvalidRequirement, *it);
1752 return false;
1756 } else {
1757 *error = ASCIIToUTF16(errors::kInvalidRequirements);
1758 return false;
1761 return true;
1764 bool Extension::LoadDefaultLocale(string16* error) {
1765 if (!manifest_->HasKey(keys::kDefaultLocale))
1766 return true;
1767 if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) ||
1768 !l10n_util::IsValidLocaleSyntax(default_locale_)) {
1769 *error = ASCIIToUTF16(errors::kInvalidDefaultLocale);
1770 return false;
1772 return true;
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();
1779 return true;
1781 if (!manifest_->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) {
1782 *error = ASCIIToUTF16(errors::kInvalidOfflineEnabled);
1783 return false;
1785 return true;
1788 bool Extension::LoadOptionsPage(string16* error) {
1789 if (!manifest_->HasKey(keys::kOptionsPage))
1790 return true;
1791 std::string options_str;
1792 if (!manifest_->GetString(keys::kOptionsPage, &options_str)) {
1793 *error = ASCIIToUTF16(errors::kInvalidOptionsPage);
1794 return false;
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);
1803 return false;
1805 options_url_ = options_url;
1806 } else {
1807 GURL absolute(options_str);
1808 if (absolute.is_valid()) {
1809 *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage);
1810 return false;
1812 options_url_ = GetResourceURL(options_str);
1813 if (!options_url_.is_valid()) {
1814 *error = ASCIIToUTF16(errors::kInvalidOptionsPage);
1815 return false;
1819 return true;
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))
1831 return true;
1833 CHECK(background_scripts_value);
1834 if (background_scripts_value->GetType() != Value::TYPE_LIST) {
1835 *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts);
1836 return false;
1839 ListValue* background_scripts =
1840 static_cast<ListValue*>(background_scripts_value);
1841 for (size_t i = 0; i < background_scripts->GetSize(); ++i) {
1842 std::string script;
1843 if (!background_scripts->GetString(i, &script)) {
1844 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
1845 errors::kInvalidBackgroundScript, base::IntToString(i));
1846 return false;
1848 background_scripts_.push_back(script);
1851 return true;
1854 bool Extension::LoadBackgroundPage(
1855 const APIPermissionSet& api_permissions,
1856 string16* error) {
1857 if (is_platform_app()) {
1858 return LoadBackgroundPage(
1859 keys::kPlatformAppBackgroundPage, api_permissions, error);
1862 if (!LoadBackgroundPage(keys::kBackgroundPage, api_permissions, error))
1863 return false;
1864 if (background_url_.is_empty()) {
1865 return LoadBackgroundPage(
1866 keys::kBackgroundPageLegacy, api_permissions, error);
1868 return true;
1871 bool Extension::LoadBackgroundPage(
1872 const std::string& key,
1873 const APIPermissionSet& api_permissions,
1874 string16* error) {
1875 base::Value* background_page_value = NULL;
1876 if (!manifest_->Get(key, &background_page_value))
1877 return true;
1879 if (!background_scripts_.empty()) {
1880 *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination);
1881 return false;
1885 std::string background_str;
1886 if (!background_page_value->GetAsString(&background_str)) {
1887 *error = ASCIIToUTF16(errors::kInvalidBackground);
1888 return false;
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);
1897 return false;
1899 // Hosted apps require an absolute URL.
1900 if (!background_url_.is_valid()) {
1901 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp);
1902 return false;
1905 if (!(background_url_.SchemeIs("https") ||
1906 (CommandLine::ForCurrentProcess()->HasSwitch(
1907 switches::kAllowHTTPBackgroundPage) &&
1908 background_url_.SchemeIs("http")))) {
1909 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp);
1910 return false;
1912 } else {
1913 background_url_ = GetResourceURL(background_str);
1916 return true;
1919 bool Extension::LoadBackgroundPersistent(
1920 const APIPermissionSet& api_permissions,
1921 string16* error) {
1922 if (is_platform_app()) {
1923 background_page_is_persistent_ = false;
1924 return true;
1927 Value* background_persistent = NULL;
1928 if (!manifest_->Get(keys::kBackgroundPersistent, &background_persistent))
1929 return true;
1931 if (!background_persistent->GetAsBoolean(&background_page_is_persistent_)) {
1932 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent);
1933 return false;
1936 if (!has_background_page()) {
1937 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage);
1938 return false;
1941 return true;
1944 bool Extension::LoadBackgroundAllowJSAccess(
1945 const APIPermissionSet& api_permissions,
1946 string16* error) {
1947 Value* allow_js_access = NULL;
1948 if (!manifest_->Get(keys::kBackgroundAllowJsAccess, &allow_js_access))
1949 return true;
1951 if (!allow_js_access->IsType(Value::TYPE_BOOLEAN) ||
1952 !allow_js_access->GetAsBoolean(&allow_background_js_access_)) {
1953 *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess);
1954 return false;
1957 return true;
1960 bool Extension::LoadWebIntentAction(const std::string& action_name,
1961 const DictionaryValue& intent_service,
1962 string16* error) {
1963 DCHECK(error);
1964 webkit_glue::WebIntentServiceData service;
1965 std::string value;
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);
1975 return false;
1978 std::string href;
1979 if (intent_service.HasKey(keys::kIntentPath)) {
1980 if (!intent_service.GetString(keys::kIntentPath, &href)) {
1981 *error = ASCIIToUTF16(errors::kInvalidIntentHref);
1982 return false;
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);
1991 return false;
1993 if (!intent_service.GetString(keys::kIntentHref, &href)) {
1994 *error = ASCIIToUTF16(errors::kInvalidIntentHref);
1995 return false;
1999 // For packaged/hosted apps, empty href implies the respective launch URLs.
2000 if (href.empty()) {
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);
2013 return false;
2014 } else if (!href.empty() && is_platform_app()) {
2015 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2016 errors::kInvalidIntentHrefInPlatformApp, action_name);
2017 return false;
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);
2027 return false;
2029 service.service_url = service_url;
2030 } else if (is_platform_app()) {
2031 service.service_url = GetBackgroundURL();
2032 } else {
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);
2037 return false;
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);
2045 return false;
2048 if (intent_service.HasKey(keys::kIntentDisposition)) {
2049 if (is_platform_app()) {
2050 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2051 errors::kInvalidIntentDispositionInPlatformApp, action_name);
2052 return false;
2054 if (!intent_service.GetString(keys::kIntentDisposition, &value) ||
2055 (value != values::kIntentDispositionWindow &&
2056 value != values::kIntentDispositionInline)) {
2057 *error = ASCIIToUTF16(errors::kInvalidIntentDisposition);
2058 return false;
2060 if (value == values::kIntentDispositionInline) {
2061 service.disposition =
2062 webkit_glue::WebIntentServiceData::DISPOSITION_INLINE;
2063 } else {
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)));
2074 return false;
2076 intents_services_.push_back(service);
2078 return true;
2081 bool Extension::LoadWebIntentServices(string16* error) {
2082 DCHECK(error);
2084 if (!manifest_->HasKey(keys::kIntents))
2085 return true;
2087 DictionaryValue* all_services = NULL;
2088 if (!manifest_->GetDictionary(keys::kIntents, &all_services)) {
2089 *error = ASCIIToUTF16(errors::kInvalidIntents);
2090 return false;
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);
2104 return false;
2106 if (!LoadWebIntentAction(*iter, *one_service, error))
2107 return false;
2109 } else {
2110 if (!all_services->GetDictionaryWithoutPathExpansion(*iter,
2111 &one_service)) {
2112 *error = ASCIIToUTF16(errors::kInvalidIntent);
2113 return false;
2115 if (!LoadWebIntentAction(*iter, *one_service, error))
2116 return false;
2119 return true;
2122 bool Extension::LoadFileHandler(const std::string& handler_id,
2123 const DictionaryValue& handler_info,
2124 string16* error) {
2125 DCHECK(error);
2126 DCHECK(is_platform_app());
2127 webkit_glue::WebIntentServiceData service;
2129 // TODO(jeremya): use a file-handler-specific data structure instead of web
2130 // intents.
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);
2139 return false;
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);
2147 return false;
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)));
2155 return false;
2157 intents_services_.push_back(service);
2159 return true;
2162 bool Extension::LoadFileHandlers(string16* error) {
2163 DCHECK(error);
2165 if (!manifest_->HasKey(keys::kFileHandlers))
2166 return true;
2168 DictionaryValue* all_handlers = NULL;
2169 if (!manifest_->GetDictionary(keys::kFileHandlers, &all_handlers)) {
2170 *error = ASCIIToUTF16(errors::kInvalidFileHandlers);
2171 return false;
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))
2180 return false;
2181 } else {
2182 *error = ASCIIToUTF16(errors::kInvalidFileHandlers);
2183 return false;
2186 return true;
2189 bool Extension::LoadExtensionFeatures(const APIPermissionSet& api_permissions,
2190 string16* error) {
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))
2208 return false;
2210 return true;
2213 bool Extension::LoadDevToolsPage(string16* error) {
2214 if (!manifest_->HasKey(keys::kDevToolsPage))
2215 return true;
2216 std::string devtools_str;
2217 if (!manifest_->GetString(keys::kDevToolsPage, &devtools_str)) {
2218 *error = ASCIIToUTF16(errors::kInvalidDevToolsPage);
2219 return false;
2221 devtools_url_ = GetResourceURL(devtools_str);
2222 return true;
2225 bool Extension::LoadInputComponents(const APIPermissionSet& api_permissions,
2226 string16* error) {
2227 if (!manifest_->HasKey(keys::kInputComponents))
2228 return true;
2229 ListValue* list_value = NULL;
2230 if (!manifest_->GetList(keys::kInputComponents, &list_value)) {
2231 *error = ASCIIToUTF16(errors::kInvalidInputComponents);
2232 return false;
2235 for (size_t i = 0; i < list_value->GetSize(); ++i) {
2236 DictionaryValue* module_value = NULL;
2237 std::string name_str;
2238 InputComponentType type;
2239 std::string id_str;
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);
2250 return false;
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));
2257 return false;
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;
2265 } else {
2266 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2267 errors::kInvalidInputComponentType, base::IntToString(i));
2268 return false;
2270 } else {
2271 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2272 errors::kInvalidInputComponentType, base::IntToString(i));
2273 return false;
2276 // Get input_components[i].id.
2277 if (!module_value->GetString(keys::kId, &id_str)) {
2278 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));
2285 return false;
2287 // Get input_components[i].language.
2288 if (!module_value->GetString(keys::kLanguage, &language_str)) {
2289 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);
2296 return false;
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));
2305 return false;
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));
2315 return false;
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));
2323 return false;
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;
2355 return true;
2358 bool Extension::LoadContentScripts(string16* error) {
2359 if (!manifest_->HasKey(keys::kContentScripts))
2360 return true;
2361 ListValue* list_value;
2362 if (!manifest_->GetList(keys::kContentScripts, &list_value)) {
2363 *error = ASCIIToUTF16(errors::kInvalidContentScriptsList);
2364 return false;
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));
2372 return false;
2375 UserScript script;
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);
2385 return true;
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);
2395 return false;
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);
2406 return false;
2408 } else { // list_value_length > 1u.
2409 *error = ASCIIToUTF16(errors::kInvalidPageActionsListSize);
2410 return false;
2412 } else if (manifest_->HasKey(keys::kPageAction)) {
2413 if (!manifest_->GetDictionary(keys::kPageAction, &page_action_value)) {
2414 *error = ASCIIToUTF16(errors::kInvalidPageAction);
2415 return false;
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.
2427 return true;
2430 bool Extension::LoadBrowserAction(string16* error) {
2431 if (!manifest_->HasKey(keys::kBrowserAction))
2432 return true;
2433 DictionaryValue* browser_action_value = NULL;
2434 if (!manifest_->GetDictionary(keys::kBrowserAction, &browser_action_value)) {
2435 *error = ASCIIToUTF16(errors::kInvalidBrowserAction);
2436 return false;
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.
2443 return true;
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);
2460 return false;
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.
2467 } else {
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);
2494 if (!path.empty())
2495 script_badge_info_->default_icon.Add(
2496 extension_misc::kScriptBadgeIconSizes[i], path);
2499 return true;
2502 bool Extension::LoadFileBrowserHandlers(string16* error) {
2503 if (!manifest_->HasKey(keys::kFileBrowserHandlers))
2504 return true;
2505 ListValue* file_browser_handlers_value = NULL;
2506 if (!manifest_->GetList(keys::kFileBrowserHandlers,
2507 &file_browser_handlers_value)) {
2508 *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
2509 return false;
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.
2515 return true;
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();
2524 ++iter) {
2525 if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) {
2526 *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler);
2527 return NULL;
2529 scoped_ptr<FileBrowserHandler> action(
2530 LoadFileBrowserHandler(
2531 reinterpret_cast<DictionaryValue*>(*iter), error));
2532 if (!action.get())
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());
2544 std::string 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);
2549 return NULL;
2551 result->set_id(id);
2553 // Read the page action title from |default_title| (mandatory).
2554 std::string title;
2555 if (!file_browser_handler->HasKey(keys::kPageActionDefaultTitle) ||
2556 !file_browser_handler->GetString(keys::kPageActionDefaultTitle, &title)) {
2557 *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle);
2558 return NULL;
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);
2569 return NULL;
2571 for (size_t i = 0; i < access_list_value->GetSize(); ++i) {
2572 std::string access;
2573 if (!access_list_value->GetString(i, &access) ||
2574 result->AddFileAccessPermission(access)) {
2575 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2576 errors::kInvalidFileAccessValue, base::IntToString(i));
2577 return NULL;
2581 if (!result->ValidateFileAccessPermissions()) {
2582 *error = ASCIIToUTF16(errors::kInvalidFileAccessList);
2583 return NULL;
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);
2594 return NULL;
2596 for (size_t i = 0; i < list_value->GetSize(); ++i) {
2597 std::string filter;
2598 if (!list_value->GetString(i, &filter)) {
2599 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2600 errors::kInvalidFileFilterValue, base::IntToString(i));
2601 return NULL;
2603 StringToLowerASCII(&filter);
2604 if (!StartsWithASCII(filter,
2605 std::string(chrome::kFileSystemScheme) + ':',
2606 true)) {
2607 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2608 errors::kInvalidURLPatternError, filter);
2609 return NULL;
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);
2618 return NULL;
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);
2624 if (!allowed) {
2625 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2626 errors::kInvalidURLPatternError, filter);
2627 return NULL;
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);
2640 return NULL;
2642 result->set_icon_path(default_icon);
2645 return result.release();
2648 bool Extension::LoadChromeURLOverrides(string16* error) {
2649 if (!manifest_->HasKey(keys::kChromeURLOverrides))
2650 return true;
2651 DictionaryValue* overrides = NULL;
2652 if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) {
2653 *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
2654 return false;
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;
2661 std::string val;
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);
2670 #endif
2671 #if defined(FILE_MANAGER_EXTENSION)
2672 is_override = (is_override &&
2673 !(location() == COMPONENT &&
2674 page == chrome::kChromeUIFileManagerHost));
2675 #endif
2677 if (is_override || !overrides->GetStringWithoutPathExpansion(*iter, &val)) {
2678 *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides);
2679 return false;
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,
2688 page.c_str());
2689 if (pattern.Parse(url) != URLPattern::PARSE_SUCCESS) {
2690 *error = ExtensionErrorUtils::FormatErrorMessageUTF16(
2691 errors::kInvalidURLPatternError, url);
2692 return false;
2694 extent_.AddPattern(pattern);
2698 // An extension may override at most one page.
2699 if (overrides->size() > 1) {
2700 *error = ASCIIToUTF16(errors::kMultipleOverrides);
2701 return false;
2704 return true;
2707 bool Extension::LoadOmnibox(string16* error) {
2708 if (!manifest_->HasKey(keys::kOmnibox))
2709 return true;
2710 if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) ||
2711 omnibox_keyword_.empty()) {
2712 *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword);
2713 return false;
2715 return true;
2718 bool Extension::LoadTextToSpeechVoices(string16* error) {
2719 if (!manifest_->HasKey(keys::kTtsEngine))
2720 return true;
2721 DictionaryValue* tts_dict = NULL;
2722 if (!manifest_->GetDictionary(keys::kTtsEngine, &tts_dict)) {
2723 *error = ASCIIToUTF16(errors::kInvalidTts);
2724 return false;
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);
2731 return false;
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);
2738 return false;
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);
2746 return false;
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);
2754 return false;
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);
2763 return false;
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);
2771 return false;
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);
2777 return false;
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);
2786 return false;
2788 if (voice_data.event_types.find(event_type) !=
2789 voice_data.event_types.end()) {
2790 *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes);
2791 return false;
2793 voice_data.event_types.insert(event_type);
2797 tts_voices_.push_back(voice_data);
2800 return true;
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))
2807 return true;
2808 std::string value;
2809 if (!manifest_->GetString(keys::kIncognito, &value)) {
2810 *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior);
2811 return false;
2813 if (value == values::kIncognitoSpanning) {
2814 incognito_split_mode_ = false;
2815 } else if (value == values::kIncognitoSplit) {
2816 incognito_split_mode_ = true;
2817 } else {
2818 *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior);
2819 return false;
2821 return true;
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);
2832 return false;
2834 if (!ContentSecurityPolicyIsLegal(content_security_policy)) {
2835 *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy);
2836 return false;
2838 if (manifest_version_ >= 2 &&
2839 !ContentSecurityPolicyIsSecure(content_security_policy, GetType())) {
2840 *error = ASCIIToUTF16(errors::kInsecureContentSecurityPolicy);
2841 return false;
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()));
2854 return true;
2857 bool Extension::LoadAppIsolation(const APIPermissionSet& api_permissions,
2858 string16* error) {
2859 // Platform apps always get isolated storage.
2860 if (is_platform_app()) {
2861 is_storage_isolated_ = true;
2862 return true;
2865 // Other apps only get it if it is requested _and_ experimental APIs are
2866 // enabled.
2867 if (!api_permissions.count(APIPermission::kExperimental) || !is_app())
2868 return true;
2870 Value* tmp_isolation = NULL;
2871 if (!manifest_->Get(keys::kIsolation, &tmp_isolation))
2872 return true;
2874 if (tmp_isolation->GetType() != Value::TYPE_LIST) {
2875 *error = ASCIIToUTF16(errors::kInvalidIsolation);
2876 return false;
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));
2886 return false;
2889 // Check for isolated storage.
2890 if (isolation_string == values::kIsolatedStorage) {
2891 is_storage_isolated_ = true;
2892 } else {
2893 DLOG(WARNING) << "Did not recognize isolation type: " << isolation_string;
2896 return true;
2899 bool Extension::LoadThemeFeatures(string16* error) {
2900 if (!manifest_->HasKey(keys::kTheme))
2901 return true;
2902 DictionaryValue* theme_value = NULL;
2903 if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) {
2904 *error = ASCIIToUTF16(errors::kInvalidTheme);
2905 return false;
2907 if (!LoadThemeImages(theme_value, error))
2908 return false;
2909 if (!LoadThemeColors(theme_value, error))
2910 return false;
2911 if (!LoadThemeTints(theme_value, error))
2912 return false;
2913 if (!LoadThemeDisplayProperties(theme_value, error))
2914 return false;
2916 return true;
2919 bool Extension::LoadThemeImages(const DictionaryValue* theme_value,
2920 string16* error) {
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) {
2926 std::string val;
2927 if (!images_value->GetString(*iter, &val)) {
2928 *error = ASCIIToUTF16(errors::kInvalidThemeImages);
2929 return false;
2932 theme_images_.reset(images_value->DeepCopy());
2934 return true;
2937 bool Extension::LoadThemeColors(const DictionaryValue* theme_value,
2938 string16* error) {
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;
2945 double alpha = 0.0;
2946 int color = 0;
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);
2960 return false;
2963 theme_colors_.reset(colors_value->DeepCopy());
2965 return true;
2968 bool Extension::LoadThemeTints(const DictionaryValue* theme_value,
2969 string16* error) {
2970 const DictionaryValue* tints_value = NULL;
2971 if (!theme_value->GetDictionary(keys::kThemeTints, &tints_value))
2972 return true;
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;
2978 double v = 0.0;
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);
2985 return false;
2988 theme_tints_.reset(tints_value->DeepCopy());
2989 return true;
2992 bool Extension::LoadThemeDisplayProperties(const DictionaryValue* theme_value,
2993 string16* error) {
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());
3000 return true;
3003 // static
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),
3020 launch_width_(0),
3021 launch_height_(0),
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));
3044 #endif
3045 ExtensionResource r(id(), path(), relative_file_path);
3046 if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) {
3047 r.set_follow_symlinks_anywhere();
3049 return r;
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();
3058 return r;
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) {
3066 DCHECK(output);
3067 if (!output)
3068 return false;
3069 if (input.length() == 0)
3070 return false;
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)
3078 return false;
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)
3082 return false;
3083 if (start_pos >= end_pos)
3084 return false;
3086 working = working.substr(start_pos, end_pos - start_pos);
3087 if (working.length() == 0)
3088 return false;
3091 return base::Base64Decode(working, output);
3094 bool Extension::ProducePEM(const std::string& input, std::string* output) {
3095 DCHECK(output);
3096 return (input.length() == 0) ? false : base::Base64Encode(input, output);
3099 bool Extension::FormatPEMForFileOutput(const std::string& input,
3100 std::string* output,
3101 bool is_public) {
3102 DCHECK(output);
3103 if (input.length() == 0)
3104 return false;
3105 *output = "";
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");
3116 i += slice;
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");
3125 return true;
3128 // static
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);
3139 // static
3140 void Extension::DecodeIcon(const Extension* extension,
3141 int icon_size,
3142 scoped_ptr<SkBitmap>* result) {
3143 DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result);
3146 // static
3147 void Extension::DecodeIconFromPath(const FilePath& icon_path,
3148 int icon_size,
3149 scoped_ptr<SkBitmap>* result) {
3150 if (icon_path.empty())
3151 return;
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();
3156 return;
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();
3168 return;
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());
3175 return;
3178 result->swap(decoded);
3181 // static
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);
3187 // static
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) {
3194 DCHECK(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))
3208 return false;
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))
3213 return false;
3215 if (!LoadRequiredFeatures(error))
3216 return false;
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
3225 // package this is.
3226 if (is_app() && !LoadAppFeatures(error))
3227 return false;
3229 APIPermissionSet api_permissions;
3230 URLPatternSet host_permissions;
3231 if (!ParsePermissions(keys::kPermissions,
3232 error,
3233 &api_permissions,
3234 &host_permissions)) {
3235 return false;
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()) {
3248 details_url_ =
3249 GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id());
3252 APIPermissionSet optional_api_permissions;
3253 URLPatternSet optional_host_permissions;
3254 if (!ParsePermissions(keys::kOptionalPermissions,
3255 error,
3256 &optional_api_permissions,
3257 &optional_host_permissions)) {
3258 return false;
3261 if (!LoadAppIsolation(api_permissions, error))
3262 return false;
3264 if (!LoadSharedFeatures(api_permissions, error))
3265 return false;
3267 if (!LoadExtensionFeatures(api_permissions, error))
3268 return false;
3270 if (!LoadThemeFeatures(error))
3271 return false;
3273 if (HasMultipleUISurfaces()) {
3274 *error = ASCIIToUTF16(errors::kOneUISurfaceOnly);
3275 return false;
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());
3285 return true;
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.
3301 // Extension icons.
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)));
3307 // Theme images.
3308 DictionaryValue* theme_images = GetThemeImages();
3309 if (theme_images) {
3310 for (DictionaryValue::key_iterator it = theme_images->begin_keys();
3311 it != theme_images->end_keys(); ++it) {
3312 std::string val;
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();
3322 ++iter) {
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();
3331 ++iter) {
3332 image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second)));
3336 return image_paths;
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());
3349 // static
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);
3361 // static
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
3370 // this extension.
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
3382 // this extension.
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
3389 // this extension.
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
3405 // enough.
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);
3415 return NULL;
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,
3431 string16* error,
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, "");
3439 return false;
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,
3448 error, &host_data))
3449 return false;
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.
3463 CHECK(feature);
3465 Feature::Availability availability =
3466 feature->IsAvailableToManifest(
3467 id(),
3468 GetType(),
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());
3478 continue;
3481 if (it->id() == APIPermission::kExperimental) {
3482 if (!CanSpecifyExperimentalPermission()) {
3483 *error = ASCIIToUTF16(errors::kExperimentalFlagRequired);
3484 return false;
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);
3510 return false;
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);
3527 continue;
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,
3534 base::StringPrintf(
3535 "Permission '%s' is unknown or URL pattern is malformed.",
3536 permission_str.c_str())));
3539 return true;
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)
3552 return true;
3554 // Experimental extensions are also allowed chrome://thumb.
3555 if (pattern.host() == chrome::kChromeUIThumbnailHost) {
3556 return permissions.find(APIPermission::kExperimental) !=
3557 permissions.end();
3560 // Component extensions can have access to all of chrome://*.
3561 if (CanExecuteScriptEverywhere())
3562 return true;
3564 return false;
3567 // Otherwise, the valid schemes were handled by URLPattern.
3568 return true;
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))
3586 return true;
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) {
3610 return false;
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();
3632 } else {
3633 return runtime_data_.GetActivePermissions()->GetPermissionMessages(
3634 GetType());
3638 std::vector<string16> Extension::GetPermissionMessageStrings() const {
3639 base::AutoLock auto_lock(runtime_data_lock_);
3640 if (IsTrustedId(id()))
3641 return std::vector<string16>();
3642 else
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())
3666 ++num_surfaces;
3668 if (browser_action_info())
3669 ++num_surfaces;
3671 if (is_app())
3672 ++num_surfaces;
3674 return num_surfaces > 1;
3677 bool Extension::CanExecuteScriptOnPage(const GURL& document_url,
3678 const GURL& top_frame_url,
3679 int tab_id,
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)) {
3693 if (error)
3694 *error = errors::kCannotScriptGallery;
3695 return false;
3698 if (document_url.SchemeIs(chrome::kChromeUIScheme) &&
3699 !CanExecuteScriptEverywhere()) {
3700 return false;
3703 if (top_frame_url.SchemeIs(extensions::kExtensionScheme) &&
3704 top_frame_url.GetOrigin() !=
3705 GetBaseURLFromExtensionId(id()).GetOrigin() &&
3706 !CanExecuteScriptEverywhere()) {
3707 return false;
3710 // If a tab ID is specified, try the tab-specific permissions.
3711 if (tab_id >= 0) {
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)) {
3716 return true;
3720 // If a script is specified, use its matches.
3721 if (script)
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(
3727 document_url)) {
3728 return true;
3731 if (error) {
3732 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
3733 document_url.spec());
3736 return false;
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)
3749 return true;
3751 if (CommandLine::ForCurrentProcess()->HasSwitch(
3752 switches::kEnableExperimentalExtensionApis)) {
3753 return true;
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())
3760 return true;
3762 return false;
3765 bool Extension::CanExecuteScriptEverywhere() const {
3766 if (location() == Extension::COMPONENT)
3767 return true;
3769 ScriptingWhitelist* whitelist = ExtensionConfig::GetInstance()->whitelist();
3771 for (ScriptingWhitelist::const_iterator it = whitelist->begin();
3772 it != whitelist->end(); ++it) {
3773 if (id() == *it) {
3774 return true;
3778 return false;
3781 bool Extension::CanCaptureVisiblePage(const GURL& page_url,
3782 int tab_id,
3783 std::string* error) const {
3784 if (tab_id >= 0) {
3785 scoped_refptr<const PermissionSet> tab_permissions =
3786 GetTabSpecificPermissions(tab_id);
3787 if (tab_permissions.get() &&
3788 tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) {
3789 return true;
3793 if (HasHostPermission(page_url) || page_url.GetOrigin() == url())
3794 return true;
3796 if (error) {
3797 *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage,
3798 page_url.spec());
3800 return false;
3803 bool Extension::UpdatesFromGallery() const {
3804 return extension_urls::IsWebstoreUpdateUrl(update_url());
3807 bool Extension::OverlapsWithOrigin(const GURL& origin) const {
3808 if (url() == origin)
3809 return true;
3811 if (web_extent().is_empty())
3812 return false;
3814 // Note: patterns and extents ignore port numbers.
3815 URLPattern origin_only_pattern(kValidWebExtentSchemes);
3816 if (!origin_only_pattern.SetScheme(origin.scheme()))
3817 return false;
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;
3856 else
3857 return SYNC_TYPE_NONE;
3859 case Extension::TYPE_HOSTED_APP:
3860 case Extension::TYPE_LEGACY_PACKAGED_APP:
3861 return SYNC_TYPE_APP;
3863 default:
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);
3879 return is_syncable;
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, \"";
3905 break;
3906 case Extension::InstallWarning::FORMAT_HTML:
3907 *os << "FORMAT_HTML, \"";
3908 break;
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)
3919 : extension_id(id),
3920 extension_path(path),
3921 extension_location(location) {
3922 if (manifest)
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.
3928 if (is_theme())
3929 return false;
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)) {
3936 return false;
3939 // Always show unpacked extensions and apps.
3940 if (location() == Extension::LOAD)
3941 return true;
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())
3949 return false;
3951 return true;
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))
3958 return true;
3960 return false;
3963 scoped_refptr<const PermissionSet> Extension::GetTabSpecificPermissions(
3964 int tab_id) const {
3965 base::AutoLock auto_lock(runtime_data_lock_);
3966 return runtime_data_.GetTabSpecificPermissions(tab_id);
3969 void Extension::UpdateTabSpecificPermissions(
3970 int tab_id,
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))
3983 return true;
3984 std::string minimum_version_string;
3985 if (!manifest_->GetString(keys::kMinimumChromeVersion,
3986 &minimum_version_string)) {
3987 *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion);
3988 return false;
3991 Version minimum_version(minimum_version_string);
3992 if (!minimum_version.IsValid()) {
3993 *error = ASCIIToUTF16(errors::kInvalidMinimumChromeVersion);
3994 return false;
3997 chrome::VersionInfo current_version_info;
3998 if (!current_version_info.is_valid()) {
3999 NOTREACHED();
4000 return false;
4003 Version current_version(current_version_info.Version());
4004 if (!current_version.IsValid()) {
4005 DCHECK(false);
4006 return false;
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);
4014 return false;
4016 return true;
4019 bool Extension::CheckPlatformAppFeatures(std::string* utf8_error) const {
4020 if (!is_platform_app())
4021 return true;
4023 if (!has_background_page()) {
4024 *utf8_error = errors::kBackgroundRequiredForPlatformApps;
4025 return false;
4028 if (!incognito_split_mode_) {
4029 *utf8_error = errors::kInvalidIncognitoModeForPlatformApp;
4030 return false;
4033 return true;
4036 bool Extension::CheckConflictingFeatures(std::string* utf8_error) const {
4037 if (has_lazy_background_page() &&
4038 HasAPIPermission(APIPermission::kWebRequest)) {
4039 *utf8_error = errors::kWebRequestConflictsWithLazyBackground;
4040 return false;
4043 return true;
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(
4071 int tab_id,
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],
4077 permissions.get());
4078 } else {
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)
4091 : reason(reason),
4092 already_disabled(false),
4093 extension(extension) {}
4095 UpdatedExtensionPermissionsInfo::UpdatedExtensionPermissionsInfo(
4096 const Extension* extension,
4097 const PermissionSet* permissions,
4098 Reason reason)
4099 : reason(reason),
4100 extension(extension),
4101 permissions(permissions) {}
4103 } // namespace extensions