Index: chrome/common/extensions/extension.cc |
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc |
index 41ace00dcbf2e49fa221bf63962c65c144f7188d..3559155a84fe1f24a760941a2b8115b4b325c570 100644 |
--- a/chrome/common/extensions/extension.cc |
+++ b/chrome/common/extensions/extension.cc |
@@ -281,6 +281,11 @@ bool ReadLaunchDimension(const extensions::Manifest* manifest, |
return true; |
} |
+std::string SizeToString(const gfx::Size& max_size) { |
+ return base::IntToString(max_size.width()) + "x" + |
+ base::IntToString(max_size.height()); |
+} |
+ |
} // namespace |
const FilePath::CharType Extension::kManifestFilename[] = |
@@ -342,6 +347,10 @@ Extension::FileHandlerInfo::~FileHandlerInfo() {} |
// Extension |
// |
+bool Extension::InstallWarning::operator==(const InstallWarning& other) const { |
+ return format == other.format && message == other.message; |
+} |
+ |
// static |
scoped_refptr<Extension> Extension::Create(const FilePath& path, |
Location location, |
@@ -411,69 +420,6 @@ Extension::Location Extension::GetHigherPriorityLocation( |
return (loc1_rank > loc2_rank ? loc1 : loc2 ); |
} |
-void Extension::OverrideLaunchUrl(const GURL& override_url) { |
- GURL new_url(override_url); |
- if (!new_url.is_valid()) { |
- DLOG(WARNING) << "Invalid override url given for " << name(); |
- } else { |
- if (new_url.has_port()) { |
- DLOG(WARNING) << "Override URL passed for " << name() |
- << " should not contain a port. Removing it."; |
- |
- GURL::Replacements remove_port; |
- remove_port.ClearPort(); |
- new_url = new_url.ReplaceComponents(remove_port); |
- } |
- |
- launch_web_url_ = new_url.spec(); |
- |
- URLPattern pattern(kValidWebExtentSchemes); |
- URLPattern::ParseResult result = pattern.Parse(new_url.spec()); |
- DCHECK_EQ(result, URLPattern::PARSE_SUCCESS); |
- pattern.SetPath(pattern.path() + '*'); |
- extent_.AddPattern(pattern); |
- } |
-} |
- |
-FilePath Extension::MaybeNormalizePath(const FilePath& path) { |
-#if defined(OS_WIN) |
- // Normalize any drive letter to upper-case. We do this for consistency with |
- // net_utils::FilePathToFileURL(), which does the same thing, to make string |
- // comparisons simpler. |
- std::wstring path_str = path.value(); |
- if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' && |
- path_str[1] == ':') |
- path_str[0] += ('A' - 'a'); |
- |
- return FilePath(path_str); |
-#else |
- return path; |
-#endif |
-} |
- |
-Extension::Location Extension::location() const { |
- return manifest_->location(); |
-} |
- |
-const std::string& Extension::id() const { |
- return manifest_->extension_id(); |
-} |
- |
-const std::string Extension::VersionString() const { |
- return version()->GetString(); |
-} |
- |
-void Extension::AddInstallWarnings( |
- const InstallWarningVector& new_warnings) { |
- install_warnings_.insert(install_warnings_.end(), |
- new_warnings.begin(), new_warnings.end()); |
-} |
- |
-// static |
-bool Extension::IsExtension(const FilePath& file_name) { |
- return file_name.MatchesExtension(chrome::kExtensionFileExtension); |
-} |
- |
// static |
bool Extension::IdIsValid(const std::string& id) { |
// Verify that the id is legal. |
@@ -500,6 +446,11 @@ std::string Extension::GenerateIdForPath(const FilePath& path) { |
return GenerateId(path_bytes, &id) ? id : ""; |
} |
+// static |
+bool Extension::IsExtension(const FilePath& file_name) { |
+ return file_name.MatchesExtension(chrome::kExtensionFileExtension); |
+} |
+ |
void Extension::GetBasicInfo(bool enabled, |
DictionaryValue* info) const { |
info->SetString(info_keys::kIdKey, id()); |
@@ -541,28 +492,6 @@ GURL Extension::GetResourceURL(const GURL& extension_url, |
return ret_val; |
} |
-bool Extension::is_platform_app() const { |
- return manifest_->is_platform_app(); |
-} |
- |
-bool Extension::is_hosted_app() const { |
- return manifest()->is_hosted_app(); |
-} |
- |
-bool Extension::is_legacy_packaged_app() const { |
- return manifest()->is_legacy_packaged_app(); |
-} |
- |
-bool Extension::is_theme() const { |
- return manifest()->is_theme(); |
-} |
- |
-GURL Extension::GetBackgroundURL() const { |
- if (background_scripts_.empty()) |
- return background_url_; |
- return GetResourceURL(extension_filenames::kGeneratedBackgroundPageFilename); |
-} |
- |
bool Extension::ResourceMatches(const URLPatternSet& pattern_set, |
const std::string& resource) const { |
return pattern_set.MatchesURL(extension_url_.Resolve(resource)); |
@@ -578,10 +507,6 @@ bool Extension::IsResourceWebAccessible(const std::string& relative_path) |
return ResourceMatches(web_accessible_resources_, relative_path); |
} |
-bool Extension::HasWebAccessibleResources() const { |
- return web_accessible_resources_.size() > 0; |
-} |
- |
bool Extension::IsSandboxedPage(const std::string& relative_path) const { |
return ResourceMatches(sandboxed_pages_, relative_path); |
} |
@@ -592,2837 +517,3011 @@ std::string Extension::GetResourceContentSecurityPolicy( |
sandboxed_pages_content_security_policy_ : content_security_policy(); |
} |
-bool Extension::GenerateId(const std::string& input, std::string* output) { |
- DCHECK(output); |
- uint8 hash[Extension::kIdSize]; |
- crypto::SHA256HashString(input, hash, sizeof(hash)); |
- *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); |
- ConvertHexadecimalToIDAlphabet(output); |
- |
- return true; |
+bool Extension::HasWebAccessibleResources() const { |
+ return web_accessible_resources_.size() > 0; |
} |
-// Helper method that loads a UserScript object from a dictionary in the |
-// content_script list of the manifest. |
-bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, |
- int definition_index, |
- string16* error, |
- UserScript* result) { |
- // run_at |
- if (content_script->HasKey(keys::kRunAt)) { |
- std::string run_location; |
- if (!content_script->GetString(keys::kRunAt, &run_location)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidRunAt, |
- base::IntToString(definition_index)); |
- return false; |
- } |
- |
- if (run_location == values::kRunAtDocumentStart) { |
- result->set_run_location(UserScript::DOCUMENT_START); |
- } else if (run_location == values::kRunAtDocumentEnd) { |
- result->set_run_location(UserScript::DOCUMENT_END); |
- } else if (run_location == values::kRunAtDocumentIdle) { |
- result->set_run_location(UserScript::DOCUMENT_IDLE); |
- } else { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidRunAt, |
- base::IntToString(definition_index)); |
- return false; |
- } |
+ExtensionResource Extension::GetResource( |
+ const std::string& relative_path) const { |
+ std::string new_path = relative_path; |
+ // We have some legacy data where resources have leading slashes. |
+ // See: http://crbug.com/121164 |
+ if (!new_path.empty() && new_path.at(0) == '/') |
+ new_path.erase(0, 1); |
+#if defined(OS_POSIX) |
+ FilePath relative_file_path(new_path); |
+#elif defined(OS_WIN) |
+ FilePath relative_file_path(UTF8ToWide(new_path)); |
+#endif |
+ ExtensionResource r(id(), path(), relative_file_path); |
+ if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { |
+ r.set_follow_symlinks_anywhere(); |
} |
+ return r; |
+} |
- // all frames |
- if (content_script->HasKey(keys::kAllFrames)) { |
- bool all_frames = false; |
- if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidAllFrames, base::IntToString(definition_index)); |
- return false; |
- } |
- result->set_match_all_frames(all_frames); |
+ExtensionResource Extension::GetResource( |
+ const FilePath& relative_file_path) const { |
+ ExtensionResource r(id(), path(), relative_file_path); |
+ if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { |
+ r.set_follow_symlinks_anywhere(); |
} |
+ return r; |
+} |
- // matches (required) |
- const ListValue* matches = NULL; |
- if (!content_script->GetList(keys::kMatches, &matches)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidMatches, |
- base::IntToString(definition_index)); |
+// TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a |
+// util class in base: |
+// http://code.google.com/p/chromium/issues/detail?id=13572 |
+// static |
+bool Extension::ParsePEMKeyBytes(const std::string& input, |
+ std::string* output) { |
+ DCHECK(output); |
+ if (!output) |
return false; |
- } |
- |
- if (matches->GetSize() == 0) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidMatchCount, |
- base::IntToString(definition_index)); |
+ if (input.length() == 0) |
return false; |
- } |
- for (size_t j = 0; j < matches->GetSize(); ++j) { |
- std::string match_str; |
- if (!matches->GetString(j, &match_str)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidMatch, |
- base::IntToString(definition_index), |
- base::IntToString(j), |
- errors::kExpectString); |
- return false; |
- } |
- |
- URLPattern pattern(UserScript::kValidUserScriptSchemes); |
- if (CanExecuteScriptEverywhere()) |
- pattern.SetValidSchemes(URLPattern::SCHEME_ALL); |
- URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
- if (parse_result != URLPattern::PARSE_SUCCESS) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidMatch, |
- base::IntToString(definition_index), |
- base::IntToString(j), |
- URLPattern::GetParseResultString(parse_result)); |
+ std::string working = input; |
+ if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { |
+ working = CollapseWhitespaceASCII(working, true); |
+ size_t header_pos = working.find(kKeyInfoEndMarker, |
+ sizeof(kKeyBeginHeaderMarker) - 1); |
+ if (header_pos == std::string::npos) |
+ return false; |
+ size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; |
+ size_t end_pos = working.rfind(kKeyBeginFooterMarker); |
+ if (end_pos == std::string::npos) |
+ return false; |
+ if (start_pos >= end_pos) |
return false; |
- } |
- |
- if (pattern.MatchesScheme(chrome::kFileScheme) && |
- !CanExecuteScriptEverywhere()) { |
- wants_file_access_ = true; |
- if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { |
- pattern.SetValidSchemes( |
- pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); |
- } |
- } |
- result->add_url_pattern(pattern); |
+ working = working.substr(start_pos, end_pos - start_pos); |
+ if (working.length() == 0) |
+ return false; |
} |
- // exclude_matches |
- if (content_script->HasKey(keys::kExcludeMatches)) { // optional |
- const ListValue* exclude_matches = NULL; |
- if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidExcludeMatches, |
- base::IntToString(definition_index)); |
- return false; |
- } |
+ return base::Base64Decode(working, output); |
+} |
- for (size_t j = 0; j < exclude_matches->GetSize(); ++j) { |
- std::string match_str; |
- if (!exclude_matches->GetString(j, &match_str)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidExcludeMatch, |
- base::IntToString(definition_index), |
- base::IntToString(j), |
- errors::kExpectString); |
- return false; |
- } |
+// static |
+bool Extension::ProducePEM(const std::string& input, std::string* output) { |
+ DCHECK(output); |
+ return (input.length() == 0) ? false : base::Base64Encode(input, output); |
+} |
- URLPattern pattern(UserScript::kValidUserScriptSchemes); |
- if (CanExecuteScriptEverywhere()) |
- pattern.SetValidSchemes(URLPattern::SCHEME_ALL); |
- URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
- if (parse_result != URLPattern::PARSE_SUCCESS) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidExcludeMatch, |
- base::IntToString(definition_index), base::IntToString(j), |
- URLPattern::GetParseResultString(parse_result)); |
- return false; |
- } |
+// static |
+bool Extension::GenerateId(const std::string& input, std::string* output) { |
+ DCHECK(output); |
+ uint8 hash[Extension::kIdSize]; |
+ crypto::SHA256HashString(input, hash, sizeof(hash)); |
+ *output = StringToLowerASCII(base::HexEncode(hash, sizeof(hash))); |
+ ConvertHexadecimalToIDAlphabet(output); |
- result->add_exclude_url_pattern(pattern); |
- } |
- } |
+ return true; |
+} |
- // include/exclude globs (mostly for Greasemonkey compatibility) |
- if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, |
- error, &UserScript::add_glob, result)) { |
- return false; |
+// static |
+bool Extension::FormatPEMForFileOutput(const std::string& input, |
+ std::string* output, |
+ bool is_public) { |
+ DCHECK(output); |
+ if (input.length() == 0) |
+ return false; |
+ *output = ""; |
+ output->append(kKeyBeginHeaderMarker); |
+ output->append(" "); |
+ output->append(is_public ? kPublic : kPrivate); |
+ output->append(" "); |
+ output->append(kKeyInfoEndMarker); |
+ output->append("\n"); |
+ for (size_t i = 0; i < input.length(); ) { |
+ int slice = std::min<int>(input.length() - i, kPEMOutputColumns); |
+ output->append(input.substr(i, slice)); |
+ output->append("\n"); |
+ i += slice; |
} |
+ output->append(kKeyBeginFooterMarker); |
+ output->append(" "); |
+ output->append(is_public ? kPublic : kPrivate); |
+ output->append(" "); |
+ output->append(kKeyInfoEndMarker); |
+ output->append("\n"); |
- if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, |
- error, &UserScript::add_exclude_glob, result)) { |
- return false; |
- } |
+ return true; |
+} |
- // js and css keys |
- const ListValue* js = NULL; |
- if (content_script->HasKey(keys::kJs) && |
- !content_script->GetList(keys::kJs, &js)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidJsList, |
- base::IntToString(definition_index)); |
- return false; |
- } |
+// static |
+void Extension::DecodeIcon(const Extension* extension, |
+ int preferred_icon_size, |
+ ExtensionIconSet::MatchType match_type, |
+ scoped_ptr<SkBitmap>* result) { |
+ std::string path = extension->icons().Get(preferred_icon_size, match_type); |
+ int size = extension->icons().GetIconSizeFromPath(path); |
+ ExtensionResource icon_resource = extension->GetResource(path); |
+ DecodeIconFromPath(icon_resource.GetFilePath(), size, result); |
+} |
- const ListValue* css = NULL; |
- if (content_script->HasKey(keys::kCss) && |
- !content_script->GetList(keys::kCss, &css)) { |
- *error = ErrorUtils:: |
- FormatErrorMessageUTF16(errors::kInvalidCssList, |
- base::IntToString(definition_index)); |
- return false; |
- } |
+// static |
+void Extension::DecodeIcon(const Extension* extension, |
+ int icon_size, |
+ scoped_ptr<SkBitmap>* result) { |
+ DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result); |
+} |
- // The manifest needs to have at least one js or css user script definition. |
- if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kMissingFile, |
- base::IntToString(definition_index)); |
- return false; |
+// static |
+void Extension::DecodeIconFromPath(const FilePath& icon_path, |
+ int icon_size, |
+ scoped_ptr<SkBitmap>* result) { |
+ if (icon_path.empty()) |
+ return; |
+ |
+ std::string file_contents; |
+ if (!file_util::ReadFileToString(icon_path, &file_contents)) { |
+ DLOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName(); |
+ return; |
} |
- if (js) { |
- for (size_t script_index = 0; script_index < js->GetSize(); |
- ++script_index) { |
- const Value* value; |
- std::string relative; |
- if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidJs, |
- base::IntToString(definition_index), |
- base::IntToString(script_index)); |
- return false; |
- } |
- GURL url = GetResourceURL(relative); |
- ExtensionResource resource = GetResource(relative); |
- result->js_scripts().push_back(UserScript::File( |
- resource.extension_root(), resource.relative_path(), url)); |
- } |
+ // Decode the image using WebKit's image decoder. |
+ const unsigned char* data = |
+ reinterpret_cast<const unsigned char*>(file_contents.data()); |
+ webkit_glue::ImageDecoder decoder; |
+ scoped_ptr<SkBitmap> decoded(new SkBitmap()); |
+ *decoded = decoder.Decode(data, file_contents.length()); |
+ if (decoded->empty()) { |
+ DLOG(ERROR) << "Could not decode icon file: " |
+ << icon_path.LossyDisplayName(); |
+ return; |
} |
- if (css) { |
- for (size_t script_index = 0; script_index < css->GetSize(); |
- ++script_index) { |
- const Value* value; |
- std::string relative; |
- if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidCss, |
- base::IntToString(definition_index), |
- base::IntToString(script_index)); |
- return false; |
- } |
- GURL url = GetResourceURL(relative); |
- ExtensionResource resource = GetResource(relative); |
- result->css_scripts().push_back(UserScript::File( |
- resource.extension_root(), resource.relative_path(), url)); |
- } |
+ if (decoded->width() != icon_size || decoded->height() != icon_size) { |
+ DLOG(ERROR) << "Icon file has unexpected size: " |
+ << base::IntToString(decoded->width()) << "x" |
+ << base::IntToString(decoded->height()); |
+ return; |
} |
- return true; |
+ result->swap(decoded); |
} |
-bool Extension::LoadGlobsHelper( |
- const DictionaryValue* content_script, |
- int content_script_index, |
- const char* globs_property_name, |
- string16* error, |
- void(UserScript::*add_method)(const std::string& glob), |
- UserScript* instance) { |
- if (!content_script->HasKey(globs_property_name)) |
- return true; // they are optional |
+// static |
+const gfx::ImageSkia& Extension::GetDefaultIcon(bool is_app) { |
+ int id = is_app ? IDR_APP_DEFAULT_ICON : IDR_EXTENSION_DEFAULT_ICON; |
+ return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id); |
+} |
- const ListValue* list = NULL; |
- if (!content_script->GetList(globs_property_name, &list)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidGlobList, |
- base::IntToString(content_script_index), |
- globs_property_name); |
- return false; |
+// static |
+GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { |
+ return GURL(std::string(extensions::kExtensionScheme) + |
+ content::kStandardSchemeSeparator + extension_id + "/"); |
+} |
+ |
+// static |
+void Extension::SetScriptingWhitelist( |
+ const Extension::ScriptingWhitelist& whitelist) { |
+ ScriptingWhitelist* current_whitelist = |
+ ExtensionConfig::GetInstance()->whitelist(); |
+ current_whitelist->clear(); |
+ for (ScriptingWhitelist::const_iterator it = whitelist.begin(); |
+ it != whitelist.end(); ++it) { |
+ current_whitelist->push_back(*it); |
} |
+} |
- for (size_t i = 0; i < list->GetSize(); ++i) { |
- std::string glob; |
- if (!list->GetString(i, &glob)) { |
+// static |
+const Extension::ScriptingWhitelist* Extension::GetScriptingWhitelist() { |
+ return ExtensionConfig::GetInstance()->whitelist(); |
+} |
+ |
+bool Extension::ParsePermissions(const char* key, |
+ string16* error, |
+ APIPermissionSet* api_permissions, |
+ URLPatternSet* host_permissions) { |
+ if (manifest_->HasKey(key)) { |
+ ListValue* permissions = NULL; |
+ if (!manifest_->GetList(key, &permissions)) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidGlob, |
- base::IntToString(content_script_index), |
- globs_property_name, |
- base::IntToString(i)); |
+ errors::kInvalidPermissions, ""); |
return false; |
} |
- (instance->*add_method)(glob); |
- } |
+ // NOTE: We need to get the APIPermission before we check if features |
+ // associated with them are available because the feature system does not |
+ // know about aliases. |
- return true; |
-} |
+ std::vector<std::string> host_data; |
+ if (!APIPermissionSet::ParseFromJSON(permissions, api_permissions, |
+ error, &host_data)) |
+ return false; |
-scoped_ptr<Extension::ActionInfo> Extension::LoadExtensionActionInfoHelper( |
- const DictionaryValue* extension_action, |
- ActionInfo::Type action_type, |
- string16* error) { |
- scoped_ptr<ActionInfo> result(new ActionInfo()); |
+ // Verify feature availability of permissions. |
+ std::vector<APIPermission::ID> to_remove; |
+ SimpleFeatureProvider* permission_features = |
+ SimpleFeatureProvider::GetPermissionFeatures(); |
+ for (APIPermissionSet::const_iterator it = api_permissions->begin(); |
+ it != api_permissions->end(); ++it) { |
+ extensions::Feature* feature = |
+ permission_features->GetFeature(it->name()); |
- if (manifest_version_ == 1) { |
- // kPageActionIcons is obsolete, and used by very few extensions. Continue |
- // loading it, but only take the first icon as the default_icon path. |
- const ListValue* icons = NULL; |
- if (extension_action->HasKey(keys::kPageActionIcons) && |
- extension_action->GetList(keys::kPageActionIcons, &icons)) { |
- for (ListValue::const_iterator iter = icons->begin(); |
- iter != icons->end(); ++iter) { |
- std::string path; |
- if (!(*iter)->GetAsString(&path) || !NormalizeAndValidatePath(&path)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
- return scoped_ptr<ActionInfo>(); |
- } |
+ // The feature should exist since we just got an APIPermission |
+ // for it. The two systems should be updated together whenever a |
+ // permission is added. |
+ CHECK(feature); |
- result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, path); |
- break; |
+ Feature::Availability availability = |
+ feature->IsAvailableToManifest( |
+ id(), |
+ GetType(), |
+ Feature::ConvertLocation(location()), |
+ manifest_version()); |
+ if (!availability.is_available()) { |
+ // Don't fail, but warn the developer that the manifest contains |
+ // unrecognized permissions. This may happen legitimately if the |
+ // extensions requests platform- or channel-specific permissions. |
+ install_warnings_.push_back(InstallWarning(InstallWarning::FORMAT_TEXT, |
+ availability.message())); |
+ to_remove.push_back(it->id()); |
+ continue; |
} |
- } |
- std::string id; |
- if (extension_action->HasKey(keys::kPageActionId)) { |
- if (!extension_action->GetString(keys::kPageActionId, &id)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionId); |
- return scoped_ptr<ActionInfo>(); |
+ if (it->id() == APIPermission::kExperimental) { |
+ if (!CanSpecifyExperimentalPermission()) { |
+ *error = ASCIIToUTF16(errors::kExperimentalFlagRequired); |
+ return false; |
+ } |
} |
- result->id = id; |
} |
- } |
- // Read the page action |default_icon| (optional). |
- // The |default_icon| value can be either dictionary {icon size -> icon path} |
- // or non empty string value. |
- if (extension_action->HasKey(keys::kPageActionDefaultIcon)) { |
- const DictionaryValue* icons_value = NULL; |
- std::string default_icon; |
- if (extension_action->GetDictionary(keys::kPageActionDefaultIcon, |
- &icons_value)) { |
- if (!LoadIconsFromDictionary(icons_value, |
- extension_misc::kExtensionActionIconSizes, |
- extension_misc::kNumExtensionActionIconSizes, |
- &result->default_icon, |
- error)) { |
- return scoped_ptr<ActionInfo>(); |
- } |
- } else if (extension_action->GetString(keys::kPageActionDefaultIcon, |
- &default_icon) && |
- NormalizeAndValidatePath(&default_icon)) { |
- result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, |
- default_icon); |
- } else { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
- return scoped_ptr<ActionInfo>(); |
+ // Remove permissions that are not available to this extension. |
+ for (std::vector<APIPermission::ID>::const_iterator it = to_remove.begin(); |
+ it != to_remove.end(); ++it) { |
+ api_permissions->erase(*it); |
} |
- } |
- // Read the page action title from |default_title| if present, |name| if not |
- // (both optional). |
- if (extension_action->HasKey(keys::kPageActionDefaultTitle)) { |
- if (!extension_action->GetString(keys::kPageActionDefaultTitle, |
- &result->default_title)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); |
- return scoped_ptr<ActionInfo>(); |
- } |
- } else if (manifest_version_ == 1 && extension_action->HasKey(keys::kName)) { |
- if (!extension_action->GetString(keys::kName, &result->default_title)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionName); |
- return scoped_ptr<ActionInfo>(); |
- } |
- } |
+ // Parse host pattern permissions. |
+ const int kAllowedSchemes = CanExecuteScriptEverywhere() ? |
+ URLPattern::SCHEME_ALL : kValidHostPermissionSchemes; |
- // Read the action's |popup| (optional). |
- const char* popup_key = NULL; |
- if (extension_action->HasKey(keys::kPageActionDefaultPopup)) |
- popup_key = keys::kPageActionDefaultPopup; |
+ for (std::vector<std::string>::const_iterator it = host_data.begin(); |
+ it != host_data.end(); ++it) { |
+ const std::string& permission_str = *it; |
- if (manifest_version_ == 1 && |
- extension_action->HasKey(keys::kPageActionPopup)) { |
- if (popup_key) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidPageActionOldAndNewKeys, |
- keys::kPageActionDefaultPopup, |
- keys::kPageActionPopup); |
- return scoped_ptr<ActionInfo>(); |
- } |
- popup_key = keys::kPageActionPopup; |
- } |
+ // Check if it's a host pattern permission. |
+ URLPattern pattern = URLPattern(kAllowedSchemes); |
+ URLPattern::ParseResult parse_result = pattern.Parse(permission_str); |
+ if (parse_result == URLPattern::PARSE_SUCCESS) { |
+ if (!CanSpecifyHostPermission(pattern, *api_permissions)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidPermissionScheme, permission_str); |
+ return false; |
+ } |
- if (popup_key) { |
- const DictionaryValue* popup = NULL; |
- std::string url_str; |
+ // The path component is not used for host permissions, so we force it |
+ // to match all paths. |
+ pattern.SetPath("/*"); |
- if (extension_action->GetString(popup_key, &url_str)) { |
- // On success, |url_str| is set. Nothing else to do. |
- } else if (manifest_version_ == 1 && |
- extension_action->GetDictionary(popup_key, &popup)) { |
- if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidPageActionPopupPath, "<missing>"); |
- return scoped_ptr<ActionInfo>(); |
- } |
- } else { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionPopup); |
- return scoped_ptr<ActionInfo>(); |
- } |
+ if (pattern.MatchesScheme(chrome::kFileScheme) && |
+ !CanExecuteScriptEverywhere()) { |
+ wants_file_access_ = true; |
+ if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { |
+ pattern.SetValidSchemes( |
+ pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); |
+ } |
+ } |
- if (!url_str.empty()) { |
- // An empty string is treated as having no popup. |
- result->default_popup_url = GetResourceURL(url_str); |
- if (!result->default_popup_url.is_valid()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidPageActionPopupPath, url_str); |
- return scoped_ptr<ActionInfo>(); |
+ host_permissions->AddPattern(pattern); |
+ continue; |
} |
- } else { |
- DCHECK(result->default_popup_url.is_empty()) |
- << "Shouldn't be possible for the popup to be set."; |
+ |
+ // It's probably an unknown API permission. Do not throw an error so |
+ // extensions can retain backwards compatability (http://crbug.com/42742). |
+ install_warnings_.push_back(InstallWarning( |
+ InstallWarning::FORMAT_TEXT, |
+ base::StringPrintf( |
+ "Permission '%s' is unknown or URL pattern is malformed.", |
+ permission_str.c_str()))); |
} |
} |
+ return true; |
+} |
- return result.Pass(); |
+bool Extension::HasAPIPermission(APIPermission::ID permission) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetActivePermissions()->HasAPIPermission(permission); |
} |
-// static |
-bool Extension::InitExtensionID(extensions::Manifest* manifest, |
- const FilePath& path, |
- const std::string& explicit_id, |
- int creation_flags, |
- string16* error) { |
- if (!explicit_id.empty()) { |
- manifest->set_extension_id(explicit_id); |
- return true; |
- } |
+bool Extension::HasAPIPermission(const std::string& function_name) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetActivePermissions()-> |
+ HasAccessToFunction(function_name); |
+} |
- if (manifest->HasKey(keys::kPublicKey)) { |
- std::string public_key; |
- std::string public_key_bytes; |
- std::string extension_id; |
- if (!manifest->GetString(keys::kPublicKey, &public_key) || |
- !ParsePEMKeyBytes(public_key, &public_key_bytes) || |
- !GenerateId(public_key_bytes, &extension_id)) { |
- *error = ASCIIToUTF16(errors::kInvalidKey); |
- return false; |
- } |
- manifest->set_extension_id(extension_id); |
+bool Extension::HasAPIPermissionForTab(int tab_id, |
+ APIPermission::ID permission) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ if (runtime_data_.GetActivePermissions()->HasAPIPermission(permission)) |
return true; |
- } |
+ scoped_refptr<const PermissionSet> tab_specific_permissions = |
+ runtime_data_.GetTabSpecificPermissions(tab_id); |
+ return tab_specific_permissions.get() && |
+ tab_specific_permissions->HasAPIPermission(permission); |
+} |
- if (creation_flags & REQUIRE_KEY) { |
- *error = ASCIIToUTF16(errors::kInvalidKey); |
- return false; |
- } else { |
- // If there is a path, we generate the ID from it. This is useful for |
- // development mode, because it keeps the ID stable across restarts and |
- // reloading the extension. |
- std::string extension_id = GenerateIdForPath(path); |
- if (extension_id.empty()) { |
- NOTREACHED() << "Could not create ID from path."; |
- return false; |
- } |
- manifest->set_extension_id(extension_id); |
- return true; |
- } |
+bool Extension::CheckAPIPermissionWithParam(APIPermission::ID permission, |
+ const APIPermission::CheckParam* param) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetActivePermissions()-> |
+ CheckAPIPermissionWithParam(permission, param); |
} |
-bool Extension::LoadRequiredFeatures(string16* error) { |
- if (!LoadName(error) || |
- !LoadVersion(error)) |
- return false; |
- return true; |
+const URLPatternSet& Extension::GetEffectiveHostPermissions() const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetActivePermissions()->effective_hosts(); |
} |
-bool Extension::LoadName(string16* error) { |
- string16 localized_name; |
- if (!manifest_->GetString(keys::kName, &localized_name)) { |
- *error = ASCIIToUTF16(errors::kInvalidName); |
- return false; |
- } |
- non_localized_name_ = UTF16ToUTF8(localized_name); |
- base::i18n::AdjustStringForLocaleDirection(&localized_name); |
- name_ = UTF16ToUTF8(localized_name); |
- return true; |
+bool Extension::CanSilentlyIncreasePermissions() const { |
+ return location() != INTERNAL; |
} |
-bool Extension::LoadDescription(string16* error) { |
- if (manifest_->HasKey(keys::kDescription) && |
- !manifest_->GetString(keys::kDescription, &description_)) { |
- *error = ASCIIToUTF16(errors::kInvalidDescription); |
+bool Extension::HasHostPermission(const GURL& url) const { |
+ if (url.SchemeIs(chrome::kChromeUIScheme) && |
+ url.host() != chrome::kChromeUIFaviconHost && |
+ url.host() != chrome::kChromeUIThumbnailHost && |
+ location() != Extension::COMPONENT) { |
return false; |
} |
- return true; |
-} |
-bool Extension::LoadAppFeatures(string16* error) { |
- if (!LoadExtent(keys::kWebURLs, &extent_, |
- errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || |
- !LoadLaunchURL(error) || |
- !LoadLaunchContainer(error)) { |
- return false; |
- } |
- if (manifest_->HasKey(keys::kDisplayInLauncher) && |
- !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) { |
- *error = ASCIIToUTF16(errors::kInvalidDisplayInLauncher); |
- return false; |
- } |
- if (manifest_->HasKey(keys::kDisplayInNewTabPage)) { |
- if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage, |
- &display_in_new_tab_page_)) { |
- *error = ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); |
- return false; |
- } |
- } else { |
- // Inherit default from display_in_launcher property. |
- display_in_new_tab_page_ = display_in_launcher_; |
- } |
- return true; |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetActivePermissions()-> |
+ HasExplicitAccessToOrigin(url); |
} |
-bool Extension::LoadOAuth2Info(string16* error) { |
- if (!manifest_->HasKey(keys::kOAuth2)) |
- return true; |
+bool Extension::HasEffectiveAccessToAllHosts() const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetActivePermissions()->HasEffectiveAccessToAllHosts(); |
+} |
- if (!manifest_->GetString(keys::kOAuth2ClientId, &oauth2_info_.client_id) || |
- oauth2_info_.client_id.empty()) { |
- *error = ASCIIToUTF16(errors::kInvalidOAuth2ClientId); |
- return false; |
- } |
+bool Extension::HasFullPermissions() const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetActivePermissions()->HasEffectiveFullAccess(); |
+} |
- ListValue* list = NULL; |
- if (!manifest_->GetList(keys::kOAuth2Scopes, &list)) { |
- *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes); |
- return false; |
+PermissionMessages Extension::GetPermissionMessages() const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ if (IsTrustedId(id())) { |
+ return PermissionMessages(); |
+ } else { |
+ return runtime_data_.GetActivePermissions()->GetPermissionMessages( |
+ GetType()); |
} |
+} |
- for (size_t i = 0; i < list->GetSize(); ++i) { |
- std::string scope; |
- if (!list->GetString(i, &scope)) { |
- *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes); |
- return false; |
- } |
- oauth2_info_.scopes.push_back(scope); |
- } |
+std::vector<string16> Extension::GetPermissionMessageStrings() const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ if (IsTrustedId(id())) |
+ return std::vector<string16>(); |
+ else |
+ return runtime_data_.GetActivePermissions()->GetWarningMessages(GetType()); |
+} |
- return true; |
+bool Extension::ShouldSkipPermissionWarnings() const { |
+ return IsTrustedId(id()); |
} |
-bool Extension::LoadExtent(const char* key, |
- URLPatternSet* extent, |
- const char* list_error, |
- const char* value_error, |
- string16* error) { |
- Value* temp_pattern_value = NULL; |
- if (!manifest_->Get(key, &temp_pattern_value)) |
- return true; |
+void Extension::SetActivePermissions( |
+ const PermissionSet* permissions) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ runtime_data_.SetActivePermissions(permissions); |
+} |
- if (temp_pattern_value->GetType() != Value::TYPE_LIST) { |
- *error = ASCIIToUTF16(list_error); |
- return false; |
- } |
+scoped_refptr<const PermissionSet> |
+ Extension::GetActivePermissions() const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetActivePermissions(); |
+} |
- ListValue* pattern_list = static_cast<ListValue*>(temp_pattern_value); |
- for (size_t i = 0; i < pattern_list->GetSize(); ++i) { |
- std::string pattern_string; |
- if (!pattern_list->GetString(i, &pattern_string)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16(value_error, |
- base::UintToString(i), |
- errors::kExpectString); |
- return false; |
- } |
+bool Extension::ShowConfigureContextMenus() const { |
+ // Don't show context menu for component extensions. We might want to show |
+ // options for component extension button but now there is no component |
+ // extension with options. All other menu items like uninstall have |
+ // no sense for component extensions. |
+ return location() != Extension::COMPONENT; |
+} |
- URLPattern pattern(kValidWebExtentSchemes); |
- URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); |
- if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { |
- pattern_string += "/"; |
- parse_result = pattern.Parse(pattern_string); |
- } |
+GURL Extension::GetHomepageURL() const { |
+ if (homepage_url_.is_valid()) |
+ return homepage_url_; |
- if (parse_result != URLPattern::PARSE_SUCCESS) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- value_error, |
- base::UintToString(i), |
- URLPattern::GetParseResultString(parse_result)); |
- return false; |
- } |
+ return UpdatesFromGallery() ? |
+ GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()) : GURL(); |
+} |
- // Do not allow authors to claim "<all_urls>". |
- if (pattern.match_all_urls()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- value_error, |
- base::UintToString(i), |
- errors::kCannotClaimAllURLsInExtent); |
- return false; |
- } |
+std::set<FilePath> Extension::GetBrowserImages() const { |
+ std::set<FilePath> image_paths; |
+ // TODO(viettrungluu): These |FilePath::FromWStringHack(UTF8ToWide())| |
+ // indicate that we're doing something wrong. |
- // Do not allow authors to claim "*" for host. |
- if (pattern.host().empty()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- value_error, |
- base::UintToString(i), |
- errors::kCannotClaimAllHostsInExtent); |
- return false; |
+ // Extension icons. |
+ for (ExtensionIconSet::IconMap::const_iterator iter = icons().map().begin(); |
+ iter != icons().map().end(); ++iter) { |
+ image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
+ } |
+ |
+ // Theme images. |
+ DictionaryValue* theme_images = GetThemeImages(); |
+ if (theme_images) { |
+ for (DictionaryValue::key_iterator it = theme_images->begin_keys(); |
+ it != theme_images->end_keys(); ++it) { |
+ std::string val; |
+ if (theme_images->GetStringWithoutPathExpansion(*it, &val)) |
+ image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(val))); |
} |
+ } |
- // We do not allow authors to put wildcards in their paths. Instead, we |
- // imply one at the end. |
- if (pattern.path().find('*') != std::string::npos) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- value_error, |
- base::UintToString(i), |
- errors::kNoWildCardsInPaths); |
- return false; |
+ if (page_action_info() && !page_action_info()->default_icon.empty()) { |
+ for (ExtensionIconSet::IconMap::const_iterator iter = |
+ page_action_info()->default_icon.map().begin(); |
+ iter != page_action_info()->default_icon.map().end(); |
+ ++iter) { |
+ image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
} |
- pattern.SetPath(pattern.path() + '*'); |
+ } |
- extent->AddPattern(pattern); |
+ if (browser_action_info() && !browser_action_info()->default_icon.empty()) { |
+ for (ExtensionIconSet::IconMap::const_iterator iter = |
+ browser_action_info()->default_icon.map().begin(); |
+ iter != browser_action_info()->default_icon.map().end(); |
+ ++iter) { |
+ image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
+ } |
} |
- return true; |
+ return image_paths; |
} |
-bool Extension::LoadLaunchURL(string16* error) { |
- Value* temp = NULL; |
+ExtensionResource Extension::GetIconResource( |
+ int size, ExtensionIconSet::MatchType match_type) const { |
+ std::string path = icons().Get(size, match_type); |
+ return path.empty() ? ExtensionResource() : GetResource(path); |
+} |
- // launch URL can be either local (to chrome-extension:// root) or an absolute |
- // web URL. |
- if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { |
- if (manifest_->Get(keys::kLaunchWebURL, NULL)) { |
- *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); |
- return false; |
- } |
+GURL Extension::GetIconURL(int size, |
+ ExtensionIconSet::MatchType match_type) const { |
+ std::string path = icons().Get(size, match_type); |
+ return path.empty() ? GURL() : GetResourceURL(path); |
+} |
- if (manifest_->Get(keys::kWebURLs, NULL)) { |
- *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive); |
- return false; |
- } |
+GURL Extension::GetFullLaunchURL() const { |
+ return launch_local_path().empty() ? GURL(launch_web_url()) : |
+ url().Resolve(launch_local_path()); |
+} |
- std::string launch_path; |
- if (!temp->GetAsString(&launch_path)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidLaunchValue, |
- keys::kLaunchLocalPath); |
- return false; |
- } |
+void Extension::SetCachedImage(const ExtensionResource& source, |
+ const SkBitmap& image, |
+ const gfx::Size& original_size) const { |
+ DCHECK(source.extension_root() == path()); // The resource must come from |
+ // this extension. |
+ const FilePath& path = source.relative_path(); |
+ gfx::Size actual_size(image.width(), image.height()); |
+ std::string location; |
+ if (actual_size != original_size) |
+ location = SizeToString(actual_size); |
+ image_cache_[ImageCacheKey(path, location)] = image; |
+} |
- // Ensure the launch path is a valid relative URL. |
- GURL resolved = url().Resolve(launch_path); |
- if (!resolved.is_valid() || resolved.GetOrigin() != url()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidLaunchValue, |
- keys::kLaunchLocalPath); |
- return false; |
- } |
+bool Extension::HasCachedImage(const ExtensionResource& source, |
+ const gfx::Size& max_size) const { |
+ DCHECK(source.extension_root() == path()); // The resource must come from |
+ // this extension. |
+ return GetCachedImageImpl(source, max_size) != NULL; |
+} |
- launch_local_path_ = launch_path; |
- } else if (manifest_->Get(keys::kLaunchWebURL, &temp)) { |
- std::string launch_url; |
- if (!temp->GetAsString(&launch_url)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidLaunchValue, |
- keys::kLaunchWebURL); |
- return false; |
- } |
- |
- // Ensure the launch URL is a valid absolute URL and web extent scheme. |
- GURL url(launch_url); |
- URLPattern pattern(kValidWebExtentSchemes); |
- if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidLaunchValue, |
- keys::kLaunchWebURL); |
- return false; |
- } |
+SkBitmap Extension::GetCachedImage(const ExtensionResource& source, |
+ const gfx::Size& max_size) const { |
+ DCHECK(source.extension_root() == path()); // The resource must come from |
+ // this extension. |
+ SkBitmap* image = GetCachedImageImpl(source, max_size); |
+ return image ? *image : SkBitmap(); |
+} |
- launch_web_url_ = launch_url; |
- } else if (is_legacy_packaged_app() || is_hosted_app()) { |
- *error = ASCIIToUTF16(errors::kLaunchURLRequired); |
+bool Extension::CanExecuteScriptOnPage(const GURL& document_url, |
+ const GURL& top_frame_url, |
+ int tab_id, |
+ const UserScript* script, |
+ std::string* error) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ // The gallery is special-cased as a restricted URL for scripting to prevent |
+ // access to special JS bindings we expose to the gallery (and avoid things |
+ // like extensions removing the "report abuse" link). |
+ // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing |
+ // against the store app extent? |
+ GURL store_url(extension_urls::GetWebstoreLaunchURL()); |
+ if ((document_url.host() == store_url.host()) && |
+ !CanExecuteScriptEverywhere() && |
+ !CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kAllowScriptingGallery)) { |
+ if (error) |
+ *error = errors::kCannotScriptGallery; |
return false; |
} |
- // If there is no extent, we default the extent based on the launch URL. |
- if (web_extent().is_empty() && !launch_web_url().empty()) { |
- GURL launch_url(launch_web_url()); |
- URLPattern pattern(kValidWebExtentSchemes); |
- if (!pattern.SetScheme("*")) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidLaunchValue, |
- keys::kLaunchWebURL); |
- return false; |
- } |
- pattern.SetHost(launch_url.host()); |
- pattern.SetPath("/*"); |
- extent_.AddPattern(pattern); |
- } |
- |
- // In order for the --apps-gallery-url switch to work with the gallery |
- // process isolation, we must insert any provided value into the component |
- // app's launch url and web extent. |
- if (id() == extension_misc::kWebStoreAppId) { |
- std::string gallery_url_str = CommandLine::ForCurrentProcess()-> |
- GetSwitchValueASCII(switches::kAppsGalleryURL); |
- |
- // Empty string means option was not used. |
- if (!gallery_url_str.empty()) { |
- GURL gallery_url(gallery_url_str); |
- OverrideLaunchUrl(gallery_url); |
- } |
- } else if (id() == extension_misc::kCloudPrintAppId) { |
- // In order for the --cloud-print-service switch to work, we must update |
- // the launch URL and web extent. |
- // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is |
- // currently under chrome/browser. |
- const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
- GURL cloud_print_service_url = GURL(command_line.GetSwitchValueASCII( |
- switches::kCloudPrintServiceURL)); |
- if (!cloud_print_service_url.is_empty()) { |
- std::string path( |
- cloud_print_service_url.path() + "/enable_chrome_connector"); |
- GURL::Replacements replacements; |
- replacements.SetPathStr(path); |
- GURL cloud_print_enable_connector_url = |
- cloud_print_service_url.ReplaceComponents(replacements); |
- OverrideLaunchUrl(cloud_print_enable_connector_url); |
- } |
- } else if (id() == extension_misc::kChromeAppId) { |
- // Override launch url to new tab. |
- launch_web_url_ = chrome::kChromeUINewTabURL; |
- extent_.ClearPatterns(); |
+ if (document_url.SchemeIs(chrome::kChromeUIScheme) && |
+ !CanExecuteScriptEverywhere()) { |
+ return false; |
} |
- return true; |
-} |
- |
-bool Extension::LoadLaunchContainer(string16* error) { |
- Value* tmp_launcher_container = NULL; |
- if (!manifest_->Get(keys::kLaunchContainer, &tmp_launcher_container)) |
- return true; |
- |
- std::string launch_container_string; |
- if (!tmp_launcher_container->GetAsString(&launch_container_string)) { |
- *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
+ if (top_frame_url.SchemeIs(extensions::kExtensionScheme) && |
+ top_frame_url.GetOrigin() != |
+ GetBaseURLFromExtensionId(id()).GetOrigin() && |
+ !CanExecuteScriptEverywhere()) { |
return false; |
} |
- if (launch_container_string == values::kLaunchContainerPanel) { |
- launch_container_ = extension_misc::LAUNCH_PANEL; |
- } else if (launch_container_string == values::kLaunchContainerTab) { |
- launch_container_ = extension_misc::LAUNCH_TAB; |
- } else { |
- *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
- return false; |
+ // If a tab ID is specified, try the tab-specific permissions. |
+ if (tab_id >= 0) { |
+ scoped_refptr<const PermissionSet> tab_permissions = |
+ runtime_data_.GetTabSpecificPermissions(tab_id); |
+ if (tab_permissions.get() && |
+ tab_permissions->explicit_hosts().MatchesSecurityOrigin(document_url)) { |
+ return true; |
+ } |
} |
- bool can_specify_initial_size = |
- launch_container_ == extension_misc::LAUNCH_PANEL || |
- launch_container_ == extension_misc::LAUNCH_WINDOW; |
+ // If a script is specified, use its matches. |
+ if (script) |
+ return script->MatchesURL(document_url); |
- // Validate the container width if present. |
- if (!ReadLaunchDimension(manifest_.get(), |
- keys::kLaunchWidth, |
- &launch_width_, |
- can_specify_initial_size, |
- error)) { |
- return false; |
+ // Otherwise, see if this extension has permission to execute script |
+ // programmatically on pages. |
+ if (runtime_data_.GetActivePermissions()->HasExplicitAccessToOrigin( |
+ document_url)) { |
+ return true; |
} |
- // Validate container height if present. |
- if (!ReadLaunchDimension(manifest_.get(), |
- keys::kLaunchHeight, |
- &launch_height_, |
- can_specify_initial_size, |
- error)) { |
- return false; |
+ if (error) { |
+ *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, |
+ document_url.spec()); |
} |
- return true; |
+ return false; |
} |
-bool Extension::LoadSharedFeatures( |
- const APIPermissionSet& api_permissions, |
- string16* error) { |
- if (!LoadDescription(error) || |
- !LoadHomepageURL(error) || |
- !LoadUpdateURL(error) || |
- !LoadIcons(error) || |
- !LoadCommands(error) || |
- !LoadPlugins(error) || |
- !LoadNaClModules(error) || |
- !LoadWebAccessibleResources(error) || |
- !LoadSandboxedPages(error) || |
- !LoadRequirements(error) || |
- !LoadDefaultLocale(error) || |
- !LoadOfflineEnabled(error) || |
- !LoadOptionsPage(error) || |
- // LoadBackgroundScripts() must be called before LoadBackgroundPage(). |
- !LoadBackgroundScripts(error) || |
- !LoadBackgroundPage(api_permissions, error) || |
- !LoadBackgroundPersistent(api_permissions, error) || |
- !LoadBackgroundAllowJSAccess(api_permissions, error) || |
- !LoadWebIntentServices(error) || |
- !LoadOAuth2Info(error)) |
- return false; |
+bool Extension::CanExecuteScriptEverywhere() const { |
+ if (location() == Extension::COMPONENT) |
+ return true; |
- return true; |
-} |
+ ScriptingWhitelist* whitelist = ExtensionConfig::GetInstance()->whitelist(); |
-bool Extension::LoadVersion(string16* error) { |
- std::string version_str; |
- if (!manifest_->GetString(keys::kVersion, &version_str)) { |
- *error = ASCIIToUTF16(errors::kInvalidVersion); |
- return false; |
- } |
- version_.reset(new Version(version_str)); |
- if (!version_->IsValid() || version_->components().size() > 4) { |
- *error = ASCIIToUTF16(errors::kInvalidVersion); |
- return false; |
+ for (ScriptingWhitelist::const_iterator it = whitelist->begin(); |
+ it != whitelist->end(); ++it) { |
+ if (id() == *it) { |
+ return true; |
+ } |
} |
- return true; |
+ |
+ return false; |
} |
-bool Extension::LoadManifestVersion(string16* error) { |
- // Get the original value out of the dictionary so that we can validate it |
- // more strictly. |
- if (manifest_->value()->HasKey(keys::kManifestVersion)) { |
- int manifest_version = 1; |
- if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) || |
- manifest_version < 1) { |
- *error = ASCIIToUTF16(errors::kInvalidManifestVersion); |
- return false; |
+bool Extension::CanCaptureVisiblePage(const GURL& page_url, |
+ int tab_id, |
+ std::string* error) const { |
+ if (tab_id >= 0) { |
+ scoped_refptr<const PermissionSet> tab_permissions = |
+ GetTabSpecificPermissions(tab_id); |
+ if (tab_permissions.get() && |
+ tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) { |
+ return true; |
} |
} |
- manifest_version_ = manifest_->GetManifestVersion(); |
- if (creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION && |
- manifest_version_ < kModernManifestVersion && |
- !CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kAllowLegacyExtensionManifests)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidManifestVersionOld, |
- base::IntToString(kModernManifestVersion)); |
- return false; |
+ if (HasHostPermission(page_url) || page_url.GetOrigin() == url()) |
+ return true; |
+ |
+ if (error) { |
+ *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, |
+ page_url.spec()); |
} |
+ return false; |
+} |
- return true; |
+bool Extension::UpdatesFromGallery() const { |
+ return extension_urls::IsWebstoreUpdateUrl(update_url()); |
} |
-bool Extension::LoadHomepageURL(string16* error) { |
- if (!manifest_->HasKey(keys::kHomepageURL)) |
+bool Extension::OverlapsWithOrigin(const GURL& origin) const { |
+ if (url() == origin) |
return true; |
- std::string tmp_homepage_url; |
- if (!manifest_->GetString(keys::kHomepageURL, &tmp_homepage_url)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidHomepageURL, ""); |
- return false; |
- } |
- homepage_url_ = GURL(tmp_homepage_url); |
- if (!homepage_url_.is_valid() || |
- (!homepage_url_.SchemeIs("http") && |
- !homepage_url_.SchemeIs("https"))) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidHomepageURL, tmp_homepage_url); |
+ |
+ if (web_extent().is_empty()) |
return false; |
- } |
- return true; |
-} |
-bool Extension::LoadUpdateURL(string16* error) { |
- if (!manifest_->HasKey(keys::kUpdateURL)) |
- return true; |
- std::string tmp_update_url; |
- if (!manifest_->GetString(keys::kUpdateURL, &tmp_update_url)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidUpdateURL, ""); |
- return false; |
- } |
- update_url_ = GURL(tmp_update_url); |
- if (!update_url_.is_valid() || |
- update_url_.has_ref()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidUpdateURL, tmp_update_url); |
+ // Note: patterns and extents ignore port numbers. |
+ URLPattern origin_only_pattern(kValidWebExtentSchemes); |
+ if (!origin_only_pattern.SetScheme(origin.scheme())) |
return false; |
- } |
- return true; |
-} |
+ origin_only_pattern.SetHost(origin.host()); |
+ origin_only_pattern.SetPath("/*"); |
-bool Extension::LoadIcons(string16* error) { |
- if (!manifest_->HasKey(keys::kIcons)) |
- return true; |
- DictionaryValue* icons_value = NULL; |
- if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidIcons); |
- return false; |
- } |
+ URLPatternSet origin_only_pattern_list; |
+ origin_only_pattern_list.AddPattern(origin_only_pattern); |
- return LoadIconsFromDictionary(icons_value, |
- extension_misc::kExtensionIconSizes, |
- extension_misc::kNumExtensionIconSizes, |
- &icons_, |
- error); |
+ return web_extent().OverlapsWith(origin_only_pattern_list); |
} |
-bool Extension::LoadCommands(string16* error) { |
- if (manifest_->HasKey(keys::kCommands)) { |
- DictionaryValue* commands = NULL; |
- if (!manifest_->GetDictionary(keys::kCommands, &commands)) { |
- *error = ASCIIToUTF16(errors::kInvalidCommandsKey); |
- return false; |
- } |
+Extension::SyncType Extension::GetSyncType() const { |
+ if (!IsSyncable()) { |
+ // We have a non-standard location. |
+ return SYNC_TYPE_NONE; |
+ } |
- if (commands->size() > kMaxCommandsPerExtension) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidKeyBindingTooMany, |
- base::IntToString(kMaxCommandsPerExtension)); |
- return false; |
- } |
+ // Disallow extensions with non-gallery auto-update URLs for now. |
+ // |
+ // TODO(akalin): Relax this restriction once we've put in UI to |
+ // approve synced extensions. |
+ if (!update_url().is_empty() && !UpdatesFromGallery()) |
+ return SYNC_TYPE_NONE; |
- int command_index = 0; |
- for (DictionaryValue::key_iterator iter = commands->begin_keys(); |
- iter != commands->end_keys(); ++iter) { |
- ++command_index; |
+ // Disallow extensions with native code plugins. |
+ // |
+ // TODO(akalin): Relax this restriction once we've put in UI to |
+ // approve synced extensions. |
+ if (!plugins().empty()) { |
+ return SYNC_TYPE_NONE; |
+ } |
- DictionaryValue* command = NULL; |
- if (!commands->GetDictionary(*iter, &command)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidKeyBindingDictionary, |
- base::IntToString(command_index)); |
- return false; |
- } |
+ switch (GetType()) { |
+ case Extension::TYPE_EXTENSION: |
+ return SYNC_TYPE_EXTENSION; |
- scoped_ptr<extensions::Command> binding(new extensions::Command()); |
- if (!binding->Parse(command, *iter, command_index, error)) |
- return false; // |error| already set. |
+ case Extension::TYPE_USER_SCRIPT: |
+ // We only want to sync user scripts with gallery update URLs. |
+ if (UpdatesFromGallery()) |
+ return SYNC_TYPE_EXTENSION; |
+ else |
+ return SYNC_TYPE_NONE; |
- std::string command_name = binding->command_name(); |
- if (command_name == values::kPageActionCommandEvent) { |
- page_action_command_.reset(binding.release()); |
- } else if (command_name == values::kBrowserActionCommandEvent) { |
- browser_action_command_.reset(binding.release()); |
- } else if (command_name == values::kScriptBadgeCommandEvent) { |
- script_badge_command_.reset(binding.release()); |
- } else { |
- if (command_name[0] != '_') // All commands w/underscore are reserved. |
- named_commands_[command_name] = *binding.get(); |
- } |
- } |
- } |
+ case Extension::TYPE_HOSTED_APP: |
+ case Extension::TYPE_LEGACY_PACKAGED_APP: |
+ case Extension::TYPE_PLATFORM_APP: |
+ return SYNC_TYPE_APP; |
- if (manifest_->HasKey(keys::kBrowserAction) && |
- !browser_action_command_.get()) { |
- // If the extension defines a browser action, but no command for it, then |
- // we synthesize a generic one, so the user can configure a shortcut for it. |
- // No keyboard shortcut will be assigned to it, until the user selects one. |
- browser_action_command_.reset( |
- new extensions::Command( |
- values::kBrowserActionCommandEvent, string16(), "")); |
+ default: |
+ return SYNC_TYPE_NONE; |
} |
- |
- return true; |
} |
-bool Extension::LoadPlugins(string16* error) { |
- if (!manifest_->HasKey(keys::kPlugins)) |
- return true; |
+bool Extension::IsSyncable() const { |
+ // TODO(akalin): Figure out if we need to allow some other types. |
- ListValue* list_value = NULL; |
- if (!manifest_->GetList(keys::kPlugins, &list_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidPlugins); |
- return false; |
- } |
+ // Default apps are not synced because otherwise they will pollute profiles |
+ // that don't already have them. Specially, if a user doesn't have default |
+ // apps, creates a new profile (which get default apps) and then enables sync |
+ // for it, then their profile everywhere gets the default apps. |
+ bool is_syncable = (location() == Extension::INTERNAL && |
+ !was_installed_by_default()); |
+ // Sync the chrome web store to maintain its position on the new tab page. |
+ is_syncable |= (id() == extension_misc::kWebStoreAppId); |
+ return is_syncable; |
+} |
- for (size_t i = 0; i < list_value->GetSize(); ++i) { |
- DictionaryValue* plugin_value = NULL; |
- if (!list_value->GetDictionary(i, &plugin_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidPlugins); |
- return false; |
- } |
- // Get plugins[i].path. |
- std::string path_str; |
- if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidPluginsPath, base::IntToString(i)); |
- return false; |
- } |
+bool Extension::RequiresSortOrdinal() const { |
+ return is_app() && (display_in_launcher_ || display_in_new_tab_page_); |
+} |
- // Get plugins[i].content (optional). |
- bool is_public = false; |
- if (plugin_value->HasKey(keys::kPluginsPublic)) { |
- if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidPluginsPublic, base::IntToString(i)); |
- return false; |
- } |
- } |
+bool Extension::ShouldDisplayInAppLauncher() const { |
+ // Only apps should be displayed in the launcher. |
+ return is_app() && display_in_launcher_; |
+} |
- // We don't allow extensions to load NPAPI plugins on Chrome OS, or under |
- // Windows 8 Metro mode, but still parse the entries to display consistent |
- // error messages. If the extension actually requires the plugins then |
- // LoadRequirements will prevent it loading. |
-#if defined(OS_CHROMEOS) |
- continue; |
-#elif defined(OS_WIN) |
- if (base::win::IsMetroProcess()) { |
- continue; |
- } |
-#endif // defined(OS_WIN). |
- plugins_.push_back(PluginInfo()); |
- plugins_.back().path = path().Append(FilePath::FromUTF8Unsafe(path_str)); |
- plugins_.back().is_public = is_public; |
- } |
- return true; |
+bool Extension::ShouldDisplayInNewTabPage() const { |
+ // Only apps should be displayed on the NTP. |
+ return is_app() && display_in_new_tab_page_; |
} |
-bool Extension::LoadNaClModules(string16* error) { |
- if (!manifest_->HasKey(keys::kNaClModules)) |
- return true; |
- ListValue* list_value = NULL; |
- if (!manifest_->GetList(keys::kNaClModules, &list_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidNaClModules); |
+bool Extension::ShouldDisplayInExtensionSettings() const { |
+ // Don't show for themes since the settings UI isn't really useful for them. |
+ if (is_theme()) |
+ return false; |
+ |
+ // Don't show component extensions because they are only extensions as an |
+ // implementation detail of Chrome. |
+ if (location() == Extension::COMPONENT && |
+ !CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kShowComponentExtensionOptions)) { |
return false; |
} |
- for (size_t i = 0; i < list_value->GetSize(); ++i) { |
- DictionaryValue* module_value = NULL; |
- if (!list_value->GetDictionary(i, &module_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidNaClModules); |
- return false; |
- } |
+ // Always show unpacked extensions and apps. |
+ if (location() == Extension::LOAD) |
+ return true; |
- // Get nacl_modules[i].path. |
- std::string path_str; |
- if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidNaClModulesPath, base::IntToString(i)); |
- return false; |
- } |
+ // Unless they are unpacked, never show hosted apps. Note: We intentionally |
+ // show packaged apps and platform apps because there are some pieces of |
+ // functionality that are only available in chrome://extensions/ but which |
+ // are needed for packaged and platform apps. For example, inspecting |
+ // background pages. See http://crbug.com/116134. |
+ if (is_hosted_app()) |
+ return false; |
- // Get nacl_modules[i].mime_type. |
- std::string mime_type; |
- if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidNaClModulesMIMEType, base::IntToString(i)); |
- return false; |
- } |
+ return true; |
+} |
- nacl_modules_.push_back(NaClModuleInfo()); |
- nacl_modules_.back().url = GetResourceURL(path_str); |
- nacl_modules_.back().mime_type = mime_type; |
+bool Extension::HasContentScriptAtURL(const GURL& url) const { |
+ for (UserScriptList::const_iterator it = content_scripts_.begin(); |
+ it != content_scripts_.end(); ++it) { |
+ if (it->MatchesURL(url)) |
+ return true; |
} |
+ return false; |
+} |
- return true; |
+scoped_refptr<const PermissionSet> Extension::GetTabSpecificPermissions( |
+ int tab_id) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ return runtime_data_.GetTabSpecificPermissions(tab_id); |
} |
-bool Extension::LoadWebAccessibleResources(string16* error) { |
- if (!manifest_->HasKey(keys::kWebAccessibleResources)) |
- return true; |
- ListValue* list_value = NULL; |
- if (!manifest_->GetList(keys::kWebAccessibleResources, &list_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList); |
- return false; |
- } |
- for (size_t i = 0; i < list_value->GetSize(); ++i) { |
- std::string relative_path; |
- if (!list_value->GetString(i, &relative_path)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidWebAccessibleResource, base::IntToString(i)); |
- return false; |
- } |
- URLPattern pattern(URLPattern::SCHEME_EXTENSION); |
- if (pattern.Parse(extension_url_.spec()) != URLPattern::PARSE_SUCCESS) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidURLPatternError, extension_url_.spec()); |
- return false; |
- } |
- while (relative_path[0] == '/') |
- relative_path = relative_path.substr(1, relative_path.length() - 1); |
- pattern.SetPath(pattern.path() + relative_path); |
- web_accessible_resources_.AddPattern(pattern); |
- } |
+void Extension::UpdateTabSpecificPermissions( |
+ int tab_id, |
+ scoped_refptr<const PermissionSet> permissions) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ runtime_data_.UpdateTabSpecificPermissions(tab_id, permissions); |
+} |
- return true; |
+void Extension::ClearTabSpecificPermissions(int tab_id) const { |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ runtime_data_.ClearTabSpecificPermissions(tab_id); |
} |
-bool Extension::LoadSandboxedPages(string16* error) { |
- if (!manifest_->HasPath(keys::kSandboxedPages)) |
- return true; |
+Extension::Location Extension::location() const { |
+ return manifest_->location(); |
+} |
- ListValue* list_value = NULL; |
- if (!manifest_->GetList(keys::kSandboxedPages, &list_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesList); |
- return false; |
- } |
- for (size_t i = 0; i < list_value->GetSize(); ++i) { |
- std::string relative_path; |
- if (!list_value->GetString(i, &relative_path)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidSandboxedPage, base::IntToString(i)); |
- return false; |
- } |
- URLPattern pattern(URLPattern::SCHEME_EXTENSION); |
- if (pattern.Parse(extension_url_.spec()) != URLPattern::PARSE_SUCCESS) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidURLPatternError, extension_url_.spec()); |
- return false; |
- } |
- while (relative_path[0] == '/') |
- relative_path = relative_path.substr(1, relative_path.length() - 1); |
- pattern.SetPath(pattern.path() + relative_path); |
- sandboxed_pages_.AddPattern(pattern); |
- } |
+const std::string& Extension::id() const { |
+ return manifest_->extension_id(); |
+} |
- if (manifest_->HasPath(keys::kSandboxedPagesCSP)) { |
- if (!manifest_->GetString( |
- keys::kSandboxedPagesCSP, &sandboxed_pages_content_security_policy_)) { |
- *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP); |
- return false; |
- } |
+const std::string Extension::VersionString() const { |
+ return version()->GetString(); |
+} |
- if (!ContentSecurityPolicyIsLegal( |
- sandboxed_pages_content_security_policy_) || |
- !ContentSecurityPolicyIsSandboxed( |
- sandboxed_pages_content_security_policy_, GetType())) { |
- *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP); |
- return false; |
- } |
- } else { |
- sandboxed_pages_content_security_policy_ = |
- kDefaultSandboxedPageContentSecurityPolicy; |
- CHECK(ContentSecurityPolicyIsSandboxed( |
- sandboxed_pages_content_security_policy_, GetType())); |
- } |
+void Extension::AddInstallWarnings( |
+ const InstallWarningVector& new_warnings) { |
+ install_warnings_.insert(install_warnings_.end(), |
+ new_warnings.begin(), new_warnings.end()); |
+} |
- return true; |
+bool Extension::is_platform_app() const { |
+ return manifest_->is_platform_app(); |
} |
-bool Extension::LoadRequirements(string16* error) { |
- // Before parsing requirements from the manifest, automatically default the |
- // NPAPI plugin requirement based on whether it includes NPAPI plugins. |
- ListValue* list_value = NULL; |
- requirements_.npapi = |
- manifest_->GetList(keys::kPlugins, &list_value) && !list_value->empty(); |
+bool Extension::is_hosted_app() const { |
+ return manifest()->is_hosted_app(); |
+} |
- if (!manifest_->HasKey(keys::kRequirements)) |
- return true; |
+bool Extension::is_legacy_packaged_app() const { |
+ return manifest()->is_legacy_packaged_app(); |
+} |
- DictionaryValue* requirements_value = NULL; |
- if (!manifest_->GetDictionary(keys::kRequirements, &requirements_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidRequirements); |
- return false; |
- } |
+bool Extension::is_theme() const { |
+ return manifest()->is_theme(); |
+} |
- for (DictionaryValue::key_iterator it = requirements_value->begin_keys(); |
- it != requirements_value->end_keys(); ++it) { |
- DictionaryValue* requirement_value; |
- if (!requirements_value->GetDictionaryWithoutPathExpansion( |
- *it, &requirement_value)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidRequirement, *it); |
- return false; |
- } |
+GURL Extension::GetBackgroundURL() const { |
+ if (background_scripts_.empty()) |
+ return background_url_; |
+ return GetResourceURL(extension_filenames::kGeneratedBackgroundPageFilename); |
+} |
- if (*it == "plugins") { |
- for (DictionaryValue::key_iterator plugin_it = |
- requirement_value->begin_keys(); |
- plugin_it != requirement_value->end_keys(); ++plugin_it) { |
- bool plugin_required = false; |
- if (!requirement_value->GetBoolean(*plugin_it, &plugin_required)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidRequirement, *it); |
- return false; |
- } |
- if (*plugin_it == "npapi") { |
- requirements_.npapi = plugin_required; |
- } else { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidRequirement, *it); |
- return false; |
- } |
- } |
- } else if (*it == "3D") { |
- ListValue* features = NULL; |
- if (!requirement_value->GetListWithoutPathExpansion("features", |
- &features) || |
- !features) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidRequirement, *it); |
- return false; |
- } |
+Extension::RuntimeData::RuntimeData() {} |
+Extension::RuntimeData::RuntimeData(const PermissionSet* active) |
+ : active_permissions_(active) {} |
+Extension::RuntimeData::~RuntimeData() {} |
- for (base::ListValue::iterator feature_it = features->begin(); |
- feature_it != features->end(); |
- ++feature_it) { |
- std::string feature; |
- if ((*feature_it)->GetAsString(&feature)) { |
- if (feature == "webgl") { |
- requirements_.webgl = true; |
- } else if (feature == "css3d") { |
- requirements_.css3d = true; |
- } else { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidRequirement, *it); |
- return false; |
- } |
- } |
- } |
- } else { |
- *error = ASCIIToUTF16(errors::kInvalidRequirements); |
- return false; |
- } |
- } |
- return true; |
+void Extension::RuntimeData::SetActivePermissions( |
+ const PermissionSet* active) { |
+ active_permissions_ = active; |
} |
-bool Extension::LoadDefaultLocale(string16* error) { |
- if (!manifest_->HasKey(keys::kDefaultLocale)) |
- return true; |
- if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) || |
- !l10n_util::IsValidLocaleSyntax(default_locale_)) { |
- *error = ASCIIToUTF16(errors::kInvalidDefaultLocale); |
- return false; |
- } |
- return true; |
+scoped_refptr<const PermissionSet> |
+ Extension::RuntimeData::GetActivePermissions() const { |
+ return active_permissions_; |
} |
-bool Extension::LoadOfflineEnabled(string16* error) { |
- // Defaults to false, except for platform apps which are offline by default. |
- if (!manifest_->HasKey(keys::kOfflineEnabled)) { |
- offline_enabled_ = is_platform_app(); |
- return true; |
- } |
- if (!manifest_->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) { |
- *error = ASCIIToUTF16(errors::kInvalidOfflineEnabled); |
- return false; |
- } |
- return true; |
+scoped_refptr<const PermissionSet> |
+ Extension::RuntimeData::GetTabSpecificPermissions(int tab_id) const { |
+ CHECK_GE(tab_id, 0); |
+ TabPermissionsMap::const_iterator it = tab_specific_permissions_.find(tab_id); |
+ return (it != tab_specific_permissions_.end()) ? it->second : NULL; |
} |
-bool Extension::LoadOptionsPage(string16* error) { |
- if (!manifest_->HasKey(keys::kOptionsPage)) |
- return true; |
- std::string options_str; |
- if (!manifest_->GetString(keys::kOptionsPage, &options_str)) { |
- *error = ASCIIToUTF16(errors::kInvalidOptionsPage); |
- return false; |
- } |
- |
- if (is_hosted_app()) { |
- // hosted apps require an absolute URL. |
- GURL options_url(options_str); |
- if (!options_url.is_valid() || |
- !(options_url.SchemeIs("http") || options_url.SchemeIs("https"))) { |
- *error = ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp); |
- return false; |
- } |
- options_url_ = options_url; |
+void Extension::RuntimeData::UpdateTabSpecificPermissions( |
+ int tab_id, |
+ scoped_refptr<const PermissionSet> permissions) { |
+ CHECK_GE(tab_id, 0); |
+ if (tab_specific_permissions_.count(tab_id)) { |
+ tab_specific_permissions_[tab_id] = PermissionSet::CreateUnion( |
+ tab_specific_permissions_[tab_id], |
+ permissions.get()); |
} else { |
- GURL absolute(options_str); |
- if (absolute.is_valid()) { |
- *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage); |
- return false; |
- } |
- options_url_ = GetResourceURL(options_str); |
- if (!options_url_.is_valid()) { |
- *error = ASCIIToUTF16(errors::kInvalidOptionsPage); |
- return false; |
- } |
+ tab_specific_permissions_[tab_id] = permissions; |
} |
+} |
- return true; |
+void Extension::RuntimeData::ClearTabSpecificPermissions(int tab_id) { |
+ CHECK_GE(tab_id, 0); |
+ tab_specific_permissions_.erase(tab_id); |
} |
-bool Extension::LoadBackgroundScripts(string16* error) { |
- const std::string& key = is_platform_app() ? |
- keys::kPlatformAppBackgroundScripts : keys::kBackgroundScripts; |
- return LoadBackgroundScripts(key, error); |
-} |
- |
-bool Extension::LoadBackgroundScripts(const std::string& key, string16* error) { |
- Value* background_scripts_value = NULL; |
- if (!manifest_->Get(key, &background_scripts_value)) |
+// static |
+bool Extension::InitExtensionID(extensions::Manifest* manifest, |
+ const FilePath& path, |
+ const std::string& explicit_id, |
+ int creation_flags, |
+ string16* error) { |
+ if (!explicit_id.empty()) { |
+ manifest->set_extension_id(explicit_id); |
return true; |
+ } |
- CHECK(background_scripts_value); |
- if (background_scripts_value->GetType() != Value::TYPE_LIST) { |
- *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts); |
- return false; |
+ if (manifest->HasKey(keys::kPublicKey)) { |
+ std::string public_key; |
+ std::string public_key_bytes; |
+ std::string extension_id; |
+ if (!manifest->GetString(keys::kPublicKey, &public_key) || |
+ !ParsePEMKeyBytes(public_key, &public_key_bytes) || |
+ !GenerateId(public_key_bytes, &extension_id)) { |
+ *error = ASCIIToUTF16(errors::kInvalidKey); |
+ return false; |
+ } |
+ manifest->set_extension_id(extension_id); |
+ return true; |
} |
- ListValue* background_scripts = |
- static_cast<ListValue*>(background_scripts_value); |
- for (size_t i = 0; i < background_scripts->GetSize(); ++i) { |
- std::string script; |
- if (!background_scripts->GetString(i, &script)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidBackgroundScript, base::IntToString(i)); |
+ if (creation_flags & REQUIRE_KEY) { |
+ *error = ASCIIToUTF16(errors::kInvalidKey); |
+ return false; |
+ } else { |
+ // If there is a path, we generate the ID from it. This is useful for |
+ // development mode, because it keeps the ID stable across restarts and |
+ // reloading the extension. |
+ std::string extension_id = GenerateIdForPath(path); |
+ if (extension_id.empty()) { |
+ NOTREACHED() << "Could not create ID from path."; |
return false; |
} |
- background_scripts_.push_back(script); |
+ manifest->set_extension_id(extension_id); |
+ return true; |
} |
+} |
- return true; |
+// static |
+FilePath Extension::MaybeNormalizePath(const FilePath& path) { |
+#if defined(OS_WIN) |
+ // Normalize any drive letter to upper-case. We do this for consistency with |
+ // net_utils::FilePathToFileURL(), which does the same thing, to make string |
+ // comparisons simpler. |
+ std::wstring path_str = path.value(); |
+ if (path_str.size() >= 2 && path_str[0] >= L'a' && path_str[0] <= L'z' && |
+ path_str[1] == ':') |
+ path_str[0] += ('A' - 'a'); |
+ |
+ return FilePath(path_str); |
+#else |
+ return path; |
+#endif |
} |
-bool Extension::LoadBackgroundPage( |
- const APIPermissionSet& api_permissions, |
- string16* error) { |
- if (is_platform_app()) { |
- return LoadBackgroundPage( |
- keys::kPlatformAppBackgroundPage, api_permissions, error); |
- } |
+// static |
+bool Extension::IsTrustedId(const std::string& id) { |
+ // See http://b/4946060 for more details. |
+ return id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); |
+} |
- if (!LoadBackgroundPage(keys::kBackgroundPage, api_permissions, error)) |
- return false; |
- if (background_url_.is_empty()) { |
- return LoadBackgroundPage( |
- keys::kBackgroundPageLegacy, api_permissions, error); |
- } |
- return true; |
+Extension::Extension(const FilePath& path, |
+ scoped_ptr<extensions::Manifest> manifest) |
+ : manifest_version_(0), |
+ incognito_split_mode_(false), |
+ offline_enabled_(false), |
+ converted_from_user_script_(false), |
+ background_page_is_persistent_(true), |
+ allow_background_js_access_(true), |
+ manifest_(manifest.release()), |
+ is_storage_isolated_(false), |
+ launch_container_(extension_misc::LAUNCH_TAB), |
+ launch_width_(0), |
+ launch_height_(0), |
+ display_in_launcher_(true), |
+ display_in_new_tab_page_(true), |
+ wants_file_access_(false), |
+ creation_flags_(0) { |
+ DCHECK(path.empty() || path.IsAbsolute()); |
+ path_ = MaybeNormalizePath(path); |
} |
-bool Extension::LoadBackgroundPage( |
- const std::string& key, |
- const APIPermissionSet& api_permissions, |
- string16* error) { |
- base::Value* background_page_value = NULL; |
- if (!manifest_->Get(key, &background_page_value)) |
- return true; |
+Extension::~Extension() { |
+} |
- if (!background_scripts_.empty()) { |
- *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination); |
+bool Extension::InitFromValue(int flags, string16* error) { |
+ DCHECK(error); |
+ |
+ base::AutoLock auto_lock(runtime_data_lock_); |
+ |
+ // Initialize permissions with an empty, default permission set. |
+ runtime_data_.SetActivePermissions(new PermissionSet()); |
+ optional_permission_set_ = new PermissionSet(); |
+ required_permission_set_ = new PermissionSet(); |
+ |
+ creation_flags_ = flags; |
+ |
+ // Important to load manifest version first because many other features |
+ // depend on its value. |
+ if (!LoadManifestVersion(error)) |
return false; |
- } |
+ // Validate minimum Chrome version. We don't need to store this, since the |
+ // extension is not valid if it is incorrect |
+ if (!CheckMinimumChromeVersion(error)) |
+ return false; |
- std::string background_str; |
- if (!background_page_value->GetAsString(&background_str)) { |
- *error = ASCIIToUTF16(errors::kInvalidBackground); |
+ if (!LoadRequiredFeatures(error)) |
return false; |
- } |
- if (is_hosted_app()) { |
- background_url_ = GURL(background_str); |
+ // We don't need to validate because InitExtensionID already did that. |
+ manifest_->GetString(keys::kPublicKey, &public_key_); |
- // Make sure "background" permission is set. |
- if (!api_permissions.count(APIPermission::kBackground)) { |
- *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded); |
- return false; |
- } |
- // Hosted apps require an absolute URL. |
- if (!background_url_.is_valid()) { |
- *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); |
- return false; |
- } |
+ extension_url_ = Extension::GetBaseURLFromExtensionId(id()); |
- if (!(background_url_.SchemeIs("https") || |
- (CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kAllowHTTPBackgroundPage) && |
- background_url_.SchemeIs("http")))) { |
- *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); |
- return false; |
- } |
- } else { |
- background_url_ = GetResourceURL(background_str); |
- } |
+ // Load App settings. LoadExtent at least has to be done before |
+ // ParsePermissions(), because the valid permissions depend on what type of |
+ // package this is. |
+ if (is_app() && !LoadAppFeatures(error)) |
+ return false; |
- return true; |
-} |
+ APIPermissionSet api_permissions; |
+ URLPatternSet host_permissions; |
+ if (!ParsePermissions(keys::kPermissions, |
+ error, |
+ &api_permissions, |
+ &host_permissions)) { |
+ return false; |
+ } |
-bool Extension::LoadBackgroundPersistent( |
- const APIPermissionSet& api_permissions, |
- string16* error) { |
+ // TODO(jeremya/kalman) do this via the features system by exposing the |
+ // app.window API to platform apps, with no dependency on any permissions. |
+ // See http://crbug.com/120069. |
if (is_platform_app()) { |
- background_page_is_persistent_ = false; |
- return true; |
+ api_permissions.insert(APIPermission::kAppCurrentWindowInternal); |
+ api_permissions.insert(APIPermission::kAppRuntime); |
+ api_permissions.insert(APIPermission::kAppWindow); |
} |
- Value* background_persistent = NULL; |
- if (!manifest_->Get(keys::kBackgroundPersistent, &background_persistent)) |
- return true; |
+ if (from_webstore()) { |
+ details_url_ = |
+ GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()); |
+ } |
- if (!background_persistent->GetAsBoolean(&background_page_is_persistent_)) { |
- *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent); |
+ APIPermissionSet optional_api_permissions; |
+ URLPatternSet optional_host_permissions; |
+ if (!ParsePermissions(keys::kOptionalPermissions, |
+ error, |
+ &optional_api_permissions, |
+ &optional_host_permissions)) { |
return false; |
} |
- if (!has_background_page()) { |
- *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage); |
+ if (!LoadAppIsolation(api_permissions, error)) |
return false; |
- } |
- return true; |
-} |
+ if (!LoadSharedFeatures(api_permissions, error)) |
+ return false; |
-bool Extension::LoadBackgroundAllowJSAccess( |
- const APIPermissionSet& api_permissions, |
- string16* error) { |
- Value* allow_js_access = NULL; |
- if (!manifest_->Get(keys::kBackgroundAllowJsAccess, &allow_js_access)) |
- return true; |
+ if (!LoadExtensionFeatures(&api_permissions, error)) |
+ return false; |
- if (!allow_js_access->IsType(Value::TYPE_BOOLEAN) || |
- !allow_js_access->GetAsBoolean(&allow_background_js_access_)) { |
- *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess); |
+ if (!LoadThemeFeatures(error)) |
+ return false; |
+ |
+ if (HasMultipleUISurfaces()) { |
+ *error = ASCIIToUTF16(errors::kOneUISurfaceOnly); |
return false; |
} |
+ runtime_data_.SetActivePermissions(new PermissionSet( |
+ this, api_permissions, host_permissions)); |
+ required_permission_set_ = new PermissionSet( |
+ this, api_permissions, host_permissions); |
+ optional_permission_set_ = new PermissionSet( |
+ optional_api_permissions, optional_host_permissions, URLPatternSet()); |
+ |
return true; |
} |
-bool Extension::LoadWebIntentAction(const std::string& action_name, |
- const DictionaryValue& intent_service, |
- string16* error) { |
- DCHECK(error); |
- webkit_glue::WebIntentServiceData service; |
- std::string value; |
+bool Extension::LoadAppIsolation(const APIPermissionSet& api_permissions, |
+ string16* error) { |
+ // Platform apps always get isolated storage. |
+ if (is_platform_app()) { |
+ is_storage_isolated_ = true; |
+ return true; |
+ } |
- service.action = UTF8ToUTF16(action_name); |
+ // Other apps only get it if it is requested _and_ experimental APIs are |
+ // enabled. |
+ if (!api_permissions.count(APIPermission::kExperimental) || !is_app()) |
+ return true; |
- const ListValue* mime_types = NULL; |
- if (!intent_service.HasKey(keys::kIntentType) || |
- !intent_service.GetList(keys::kIntentType, &mime_types) || |
- mime_types->GetSize() == 0) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidIntentType, action_name); |
+ Value* tmp_isolation = NULL; |
+ if (!manifest_->Get(keys::kIsolation, &tmp_isolation)) |
+ return true; |
+ |
+ if (tmp_isolation->GetType() != Value::TYPE_LIST) { |
+ *error = ASCIIToUTF16(errors::kInvalidIsolation); |
return false; |
} |
- std::string href; |
- if (intent_service.HasKey(keys::kIntentPath)) { |
- if (!intent_service.GetString(keys::kIntentPath, &href)) { |
- *error = ASCIIToUTF16(errors::kInvalidIntentHref); |
+ ListValue* isolation_list = static_cast<ListValue*>(tmp_isolation); |
+ for (size_t i = 0; i < isolation_list->GetSize(); ++i) { |
+ std::string isolation_string; |
+ if (!isolation_list->GetString(i, &isolation_string)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidIsolationValue, |
+ base::UintToString(i)); |
return false; |
} |
- } |
- if (intent_service.HasKey(keys::kIntentHref)) { |
- if (!href.empty()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidIntentHrefOldAndNewKey, action_name, |
- keys::kIntentPath, keys::kIntentHref); |
- return false; |
- } |
- if (!intent_service.GetString(keys::kIntentHref, &href)) { |
- *error = ASCIIToUTF16(errors::kInvalidIntentHref); |
- return false; |
+ // Check for isolated storage. |
+ if (isolation_string == values::kIsolatedStorage) { |
+ is_storage_isolated_ = true; |
+ } else { |
+ DLOG(WARNING) << "Did not recognize isolation type: " << isolation_string; |
} |
} |
+ return true; |
+} |
- // For packaged/hosted apps, empty href implies the respective launch URLs. |
- if (href.empty()) { |
- if (is_hosted_app()) { |
- href = launch_web_url(); |
- } else if (is_legacy_packaged_app()) { |
- href = launch_local_path(); |
- } |
+bool Extension::LoadRequiredFeatures(string16* error) { |
+ if (!LoadName(error) || |
+ !LoadVersion(error)) |
+ return false; |
+ return true; |
+} |
+ |
+bool Extension::LoadName(string16* error) { |
+ string16 localized_name; |
+ if (!manifest_->GetString(keys::kName, &localized_name)) { |
+ *error = ASCIIToUTF16(errors::kInvalidName); |
+ return false; |
} |
+ non_localized_name_ = UTF16ToUTF8(localized_name); |
+ base::i18n::AdjustStringForLocaleDirection(&localized_name); |
+ name_ = UTF16ToUTF8(localized_name); |
+ return true; |
+} |
- // If there still is not an href, the manifest is malformed, unless this is a |
- // platform app in which case the href should not be present. |
- if (href.empty() && !is_platform_app()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidIntentHrefEmpty, action_name); |
+bool Extension::LoadVersion(string16* error) { |
+ std::string version_str; |
+ if (!manifest_->GetString(keys::kVersion, &version_str)) { |
+ *error = ASCIIToUTF16(errors::kInvalidVersion); |
return false; |
- } else if (!href.empty() && is_platform_app()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidIntentHrefInPlatformApp, action_name); |
+ } |
+ version_.reset(new Version(version_str)); |
+ if (!version_->IsValid() || version_->components().size() > 4) { |
+ *error = ASCIIToUTF16(errors::kInvalidVersion); |
return false; |
} |
+ return true; |
+} |
- GURL service_url(href); |
- if (is_hosted_app()) { |
- // Hosted apps require an absolute URL for intents. |
- if (!service_url.is_valid() || |
- !(web_extent().MatchesURL(service_url))) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidIntentPageInHostedApp, action_name); |
+bool Extension::LoadAppFeatures(string16* error) { |
+ if (!LoadExtent(keys::kWebURLs, &extent_, |
+ errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || |
+ !LoadLaunchURL(error) || |
+ !LoadLaunchContainer(error)) { |
+ return false; |
+ } |
+ if (manifest_->HasKey(keys::kDisplayInLauncher) && |
+ !manifest_->GetBoolean(keys::kDisplayInLauncher, &display_in_launcher_)) { |
+ *error = ASCIIToUTF16(errors::kInvalidDisplayInLauncher); |
+ return false; |
+ } |
+ if (manifest_->HasKey(keys::kDisplayInNewTabPage)) { |
+ if (!manifest_->GetBoolean(keys::kDisplayInNewTabPage, |
+ &display_in_new_tab_page_)) { |
+ *error = ASCIIToUTF16(errors::kInvalidDisplayInNewTabPage); |
return false; |
} |
- service.service_url = service_url; |
- } else if (is_platform_app()) { |
- service.service_url = GetBackgroundURL(); |
} else { |
- // We do not allow absolute intent URLs in non-hosted apps. |
- if (service_url.is_valid()) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kCannotAccessPage, href); |
- return false; |
- } |
- service.service_url = GetResourceURL(href); |
+ // Inherit default from display_in_launcher property. |
+ display_in_new_tab_page_ = display_in_launcher_; |
} |
+ return true; |
+} |
- if (intent_service.HasKey(keys::kIntentTitle) && |
- !intent_service.GetString(keys::kIntentTitle, &service.title)) { |
- *error = ASCIIToUTF16(errors::kInvalidIntentTitle); |
+bool Extension::LoadExtent(const char* key, |
+ URLPatternSet* extent, |
+ const char* list_error, |
+ const char* value_error, |
+ string16* error) { |
+ Value* temp_pattern_value = NULL; |
+ if (!manifest_->Get(key, &temp_pattern_value)) |
+ return true; |
+ |
+ if (temp_pattern_value->GetType() != Value::TYPE_LIST) { |
+ *error = ASCIIToUTF16(list_error); |
return false; |
} |
- if (intent_service.HasKey(keys::kIntentDisposition)) { |
- if (is_platform_app()) { |
+ ListValue* pattern_list = static_cast<ListValue*>(temp_pattern_value); |
+ for (size_t i = 0; i < pattern_list->GetSize(); ++i) { |
+ std::string pattern_string; |
+ if (!pattern_list->GetString(i, &pattern_string)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16(value_error, |
+ base::UintToString(i), |
+ errors::kExpectString); |
+ return false; |
+ } |
+ |
+ URLPattern pattern(kValidWebExtentSchemes); |
+ URLPattern::ParseResult parse_result = pattern.Parse(pattern_string); |
+ if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { |
+ pattern_string += "/"; |
+ parse_result = pattern.Parse(pattern_string); |
+ } |
+ |
+ if (parse_result != URLPattern::PARSE_SUCCESS) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidIntentDispositionInPlatformApp, action_name); |
+ value_error, |
+ base::UintToString(i), |
+ URLPattern::GetParseResultString(parse_result)); |
return false; |
} |
- if (!intent_service.GetString(keys::kIntentDisposition, &value) || |
- (value != values::kIntentDispositionWindow && |
- value != values::kIntentDispositionInline)) { |
- *error = ASCIIToUTF16(errors::kInvalidIntentDisposition); |
+ |
+ // Do not allow authors to claim "<all_urls>". |
+ if (pattern.match_all_urls()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ value_error, |
+ base::UintToString(i), |
+ errors::kCannotClaimAllURLsInExtent); |
return false; |
} |
- if (value == values::kIntentDispositionInline) { |
- service.disposition = |
- webkit_glue::WebIntentServiceData::DISPOSITION_INLINE; |
- } else { |
- service.disposition = |
- webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW; |
+ |
+ // Do not allow authors to claim "*" for host. |
+ if (pattern.host().empty()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ value_error, |
+ base::UintToString(i), |
+ errors::kCannotClaimAllHostsInExtent); |
+ return false; |
} |
- } |
- for (size_t i = 0; i < mime_types->GetSize(); ++i) { |
- if (!mime_types->GetString(i, &service.type)) { |
+ // We do not allow authors to put wildcards in their paths. Instead, we |
+ // imply one at the end. |
+ if (pattern.path().find('*') != std::string::npos) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidIntentTypeElement, action_name, |
- std::string(base::IntToString(i))); |
+ value_error, |
+ base::UintToString(i), |
+ errors::kNoWildCardsInPaths); |
return false; |
} |
- intents_services_.push_back(service); |
+ pattern.SetPath(pattern.path() + '*'); |
+ |
+ extent->AddPattern(pattern); |
} |
+ |
return true; |
} |
-bool Extension::LoadWebIntentServices(string16* error) { |
- DCHECK(error); |
- |
- if (!manifest_->HasKey(keys::kIntents)) |
+bool Extension::LoadLaunchContainer(string16* error) { |
+ Value* tmp_launcher_container = NULL; |
+ if (!manifest_->Get(keys::kLaunchContainer, &tmp_launcher_container)) |
return true; |
- DictionaryValue* all_services = NULL; |
- if (!manifest_->GetDictionary(keys::kIntents, &all_services)) { |
- *error = ASCIIToUTF16(errors::kInvalidIntents); |
+ std::string launch_container_string; |
+ if (!tmp_launcher_container->GetAsString(&launch_container_string)) { |
+ *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
return false; |
} |
- for (DictionaryValue::key_iterator iter(all_services->begin_keys()); |
- iter != all_services->end_keys(); ++iter) { |
- // Any entry in the intents dictionary can either have a list of |
- // dictionaries, or just a single dictionary attached to that. Try |
- // lists first, fall back to single dictionary. |
- ListValue* service_list = NULL; |
- DictionaryValue* one_service = NULL; |
- if (all_services->GetListWithoutPathExpansion(*iter, &service_list)) { |
- for (size_t i = 0; i < service_list->GetSize(); ++i) { |
- if (!service_list->GetDictionary(i, &one_service)) { |
- *error = ASCIIToUTF16(errors::kInvalidIntent); |
- return false; |
- } |
- if (!LoadWebIntentAction(*iter, *one_service, error)) |
- return false; |
- } |
- } else { |
- if (!all_services->GetDictionaryWithoutPathExpansion(*iter, |
- &one_service)) { |
- *error = ASCIIToUTF16(errors::kInvalidIntent); |
- return false; |
- } |
- if (!LoadWebIntentAction(*iter, *one_service, error)) |
- return false; |
- } |
+ if (launch_container_string == values::kLaunchContainerPanel) { |
+ launch_container_ = extension_misc::LAUNCH_PANEL; |
+ } else if (launch_container_string == values::kLaunchContainerTab) { |
+ launch_container_ = extension_misc::LAUNCH_TAB; |
+ } else { |
+ *error = ASCIIToUTF16(errors::kInvalidLaunchContainer); |
+ return false; |
} |
- return true; |
-} |
-bool Extension::LoadFileHandler(const std::string& handler_id, |
- const DictionaryValue& handler_info, |
- string16* error) { |
- DCHECK(error); |
- DCHECK(is_platform_app()); |
- FileHandlerInfo handler; |
- |
- handler.id = handler_id; |
- |
- const ListValue* mime_types = NULL; |
- // TODO(benwells): handle file extensions. |
- if (!handler_info.HasKey(keys::kFileHandlerTypes) || |
- !handler_info.GetList(keys::kFileHandlerTypes, &mime_types) || |
- mime_types->GetSize() == 0) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidFileHandlerType, handler_id); |
- return false; |
- } |
+ bool can_specify_initial_size = |
+ launch_container_ == extension_misc::LAUNCH_PANEL || |
+ launch_container_ == extension_misc::LAUNCH_WINDOW; |
- if (handler_info.HasKey(keys::kFileHandlerTitle) && |
- !handler_info.GetString(keys::kFileHandlerTitle, &handler.title)) { |
- *error = ASCIIToUTF16(errors::kInvalidFileHandlerTitle); |
- return false; |
+ // Validate the container width if present. |
+ if (!ReadLaunchDimension(manifest_.get(), |
+ keys::kLaunchWidth, |
+ &launch_width_, |
+ can_specify_initial_size, |
+ error)) { |
+ return false; |
} |
- std::string type; |
- for (size_t i = 0; i < mime_types->GetSize(); ++i) { |
- if (!mime_types->GetString(i, &type)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidFileHandlerTypeElement, handler_id, |
- std::string(base::IntToString(i))); |
+ // Validate container height if present. |
+ if (!ReadLaunchDimension(manifest_.get(), |
+ keys::kLaunchHeight, |
+ &launch_height_, |
+ can_specify_initial_size, |
+ error)) { |
return false; |
- } |
- handler.types.insert(type); |
} |
- file_handlers_.push_back(handler); |
return true; |
} |
-bool Extension::LoadFileHandlers(string16* error) { |
- DCHECK(error); |
- |
- if (!manifest_->HasKey(keys::kFileHandlers)) |
- return true; |
- |
- DictionaryValue* all_handlers = NULL; |
- if (!manifest_->GetDictionary(keys::kFileHandlers, &all_handlers)) { |
- *error = ASCIIToUTF16(errors::kInvalidFileHandlers); |
- return false; |
- } |
+bool Extension::LoadLaunchURL(string16* error) { |
+ Value* temp = NULL; |
- for (DictionaryValue::key_iterator iter(all_handlers->begin_keys()); |
- iter != all_handlers->end_keys(); ++iter) { |
- // A file handler entry is a title and a list of MIME types to handle. |
- DictionaryValue* handler = NULL; |
- if (all_handlers->GetDictionaryWithoutPathExpansion(*iter, &handler)) { |
- if (!LoadFileHandler(*iter, *handler, error)) |
- return false; |
- } else { |
- *error = ASCIIToUTF16(errors::kInvalidFileHandlers); |
+ // launch URL can be either local (to chrome-extension:// root) or an absolute |
+ // web URL. |
+ if (manifest_->Get(keys::kLaunchLocalPath, &temp)) { |
+ if (manifest_->Get(keys::kLaunchWebURL, NULL)) { |
+ *error = ASCIIToUTF16(errors::kLaunchPathAndURLAreExclusive); |
return false; |
} |
- } |
- return true; |
-} |
- |
-bool Extension::LoadExtensionFeatures(APIPermissionSet* api_permissions, |
- string16* error) { |
- if (manifest_->HasKey(keys::kConvertedFromUserScript)) |
- manifest_->GetBoolean(keys::kConvertedFromUserScript, |
- &converted_from_user_script_); |
- |
- if (!LoadDevToolsPage(error) || |
- !LoadInputComponents(*api_permissions, error) || |
- !LoadContentScripts(error) || |
- !LoadPageAction(error) || |
- !LoadBrowserAction(error) || |
- !LoadSystemIndicator(api_permissions, error) || |
- !LoadScriptBadge(error) || |
- !LoadFileBrowserHandlers(error) || |
- !LoadChromeURLOverrides(error) || |
- !LoadOmnibox(error) || |
- !LoadTextToSpeechVoices(error) || |
- !LoadIncognitoMode(error) || |
- !LoadFileHandlers(error) || |
- !LoadContentSecurityPolicy(error)) |
- return false; |
- |
- return true; |
-} |
- |
-bool Extension::LoadDevToolsPage(string16* error) { |
- if (!manifest_->HasKey(keys::kDevToolsPage)) |
- return true; |
- std::string devtools_str; |
- if (!manifest_->GetString(keys::kDevToolsPage, &devtools_str)) { |
- *error = ASCIIToUTF16(errors::kInvalidDevToolsPage); |
- return false; |
- } |
- devtools_url_ = GetResourceURL(devtools_str); |
- return true; |
-} |
- |
-bool Extension::LoadInputComponents(const APIPermissionSet& api_permissions, |
- string16* error) { |
- if (!manifest_->HasKey(keys::kInputComponents)) |
- return true; |
- ListValue* list_value = NULL; |
- if (!manifest_->GetList(keys::kInputComponents, &list_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidInputComponents); |
- return false; |
- } |
- |
- for (size_t i = 0; i < list_value->GetSize(); ++i) { |
- DictionaryValue* module_value = NULL; |
- std::string name_str; |
- InputComponentType type; |
- std::string id_str; |
- std::string description_str; |
- std::string language_str; |
- std::set<std::string> layouts; |
- std::string shortcut_keycode_str; |
- bool shortcut_alt = false; |
- bool shortcut_ctrl = false; |
- bool shortcut_shift = false; |
- if (!list_value->GetDictionary(i, &module_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidInputComponents); |
+ if (manifest_->Get(keys::kWebURLs, NULL)) { |
+ *error = ASCIIToUTF16(errors::kLaunchPathAndExtentAreExclusive); |
return false; |
} |
- // Get input_components[i].name. |
- if (!module_value->GetString(keys::kName, &name_str)) { |
+ std::string launch_path; |
+ if (!temp->GetAsString(&launch_path)) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidInputComponentName, base::IntToString(i)); |
+ errors::kInvalidLaunchValue, |
+ keys::kLaunchLocalPath); |
return false; |
} |
- // Get input_components[i].type. |
- std::string type_str; |
- if (module_value->GetString(keys::kType, &type_str)) { |
- if (type_str == "ime") { |
- type = INPUT_COMPONENT_TYPE_IME; |
- } else { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidInputComponentType, base::IntToString(i)); |
- return false; |
- } |
- } else { |
+ // Ensure the launch path is a valid relative URL. |
+ GURL resolved = url().Resolve(launch_path); |
+ if (!resolved.is_valid() || resolved.GetOrigin() != url()) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidInputComponentType, base::IntToString(i)); |
+ errors::kInvalidLaunchValue, |
+ keys::kLaunchLocalPath); |
return false; |
} |
- // Get input_components[i].id. |
- if (!module_value->GetString(keys::kId, &id_str)) { |
- id_str = ""; |
+ launch_local_path_ = launch_path; |
+ } else if (manifest_->Get(keys::kLaunchWebURL, &temp)) { |
+ std::string launch_url; |
+ if (!temp->GetAsString(&launch_url)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidLaunchValue, |
+ keys::kLaunchWebURL); |
+ return false; |
} |
- // Get input_components[i].description. |
- if (!module_value->GetString(keys::kDescription, &description_str)) { |
+ // Ensure the launch URL is a valid absolute URL and web extent scheme. |
+ GURL url(launch_url); |
+ URLPattern pattern(kValidWebExtentSchemes); |
+ if (!url.is_valid() || !pattern.SetScheme(url.scheme())) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidInputComponentDescription, base::IntToString(i)); |
+ errors::kInvalidLaunchValue, |
+ keys::kLaunchWebURL); |
return false; |
} |
- // Get input_components[i].language. |
- if (!module_value->GetString(keys::kLanguage, &language_str)) { |
- language_str = ""; |
- } |
- // Get input_components[i].layouts. |
- ListValue* layouts_value = NULL; |
- if (!module_value->GetList(keys::kLayouts, &layouts_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidInputComponentLayouts); |
+ launch_web_url_ = launch_url; |
+ } else if (is_legacy_packaged_app() || is_hosted_app()) { |
+ *error = ASCIIToUTF16(errors::kLaunchURLRequired); |
+ return false; |
+ } |
+ |
+ // If there is no extent, we default the extent based on the launch URL. |
+ if (web_extent().is_empty() && !launch_web_url().empty()) { |
+ GURL launch_url(launch_web_url()); |
+ URLPattern pattern(kValidWebExtentSchemes); |
+ if (!pattern.SetScheme("*")) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidLaunchValue, |
+ keys::kLaunchWebURL); |
return false; |
} |
+ pattern.SetHost(launch_url.host()); |
+ pattern.SetPath("/*"); |
+ extent_.AddPattern(pattern); |
+ } |
- for (size_t j = 0; j < layouts_value->GetSize(); ++j) { |
- std::string layout_name_str; |
- if (!layouts_value->GetString(j, &layout_name_str)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidInputComponentLayoutName, base::IntToString(i), |
- base::IntToString(j)); |
- return false; |
- } |
- layouts.insert(layout_name_str); |
+ // In order for the --apps-gallery-url switch to work with the gallery |
+ // process isolation, we must insert any provided value into the component |
+ // app's launch url and web extent. |
+ if (id() == extension_misc::kWebStoreAppId) { |
+ std::string gallery_url_str = CommandLine::ForCurrentProcess()-> |
+ GetSwitchValueASCII(switches::kAppsGalleryURL); |
+ |
+ // Empty string means option was not used. |
+ if (!gallery_url_str.empty()) { |
+ GURL gallery_url(gallery_url_str); |
+ OverrideLaunchUrl(gallery_url); |
+ } |
+ } else if (id() == extension_misc::kCloudPrintAppId) { |
+ // In order for the --cloud-print-service switch to work, we must update |
+ // the launch URL and web extent. |
+ // TODO(sanjeevr): Ideally we want to use CloudPrintURL here but that is |
+ // currently under chrome/browser. |
+ const CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
+ GURL cloud_print_service_url = GURL(command_line.GetSwitchValueASCII( |
+ switches::kCloudPrintServiceURL)); |
+ if (!cloud_print_service_url.is_empty()) { |
+ std::string path( |
+ cloud_print_service_url.path() + "/enable_chrome_connector"); |
+ GURL::Replacements replacements; |
+ replacements.SetPathStr(path); |
+ GURL cloud_print_enable_connector_url = |
+ cloud_print_service_url.ReplaceComponents(replacements); |
+ OverrideLaunchUrl(cloud_print_enable_connector_url); |
} |
+ } else if (id() == extension_misc::kChromeAppId) { |
+ // Override launch url to new tab. |
+ launch_web_url_ = chrome::kChromeUINewTabURL; |
+ extent_.ClearPatterns(); |
+ } |
- if (module_value->HasKey(keys::kShortcutKey)) { |
- DictionaryValue* shortcut_value = NULL; |
- if (!module_value->GetDictionary(keys::kShortcutKey, &shortcut_value)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidInputComponentShortcutKey, base::IntToString(i)); |
- return false; |
- } |
+ return true; |
+} |
- // Get input_components[i].shortcut_keycode. |
- if (!shortcut_value->GetString(keys::kKeycode, &shortcut_keycode_str)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidInputComponentShortcutKeycode, |
- base::IntToString(i)); |
- return false; |
- } |
- |
- // Get input_components[i].shortcut_alt. |
- if (!shortcut_value->GetBoolean(keys::kAltKey, &shortcut_alt)) { |
- shortcut_alt = false; |
- } |
- |
- // Get input_components[i].shortcut_ctrl. |
- if (!shortcut_value->GetBoolean(keys::kCtrlKey, &shortcut_ctrl)) { |
- shortcut_ctrl = false; |
- } |
- |
- // Get input_components[i].shortcut_shift. |
- if (!shortcut_value->GetBoolean(keys::kShiftKey, &shortcut_shift)) { |
- shortcut_shift = false; |
- } |
- } |
- |
- input_components_.push_back(InputComponentInfo()); |
- input_components_.back().name = name_str; |
- input_components_.back().type = type; |
- input_components_.back().id = id_str; |
- input_components_.back().description = description_str; |
- input_components_.back().language = language_str; |
- input_components_.back().layouts.insert(layouts.begin(), layouts.end()); |
- input_components_.back().shortcut_keycode = shortcut_keycode_str; |
- input_components_.back().shortcut_alt = shortcut_alt; |
- input_components_.back().shortcut_ctrl = shortcut_ctrl; |
- input_components_.back().shortcut_shift = shortcut_shift; |
- } |
+bool Extension::LoadSharedFeatures( |
+ const APIPermissionSet& api_permissions, |
+ string16* error) { |
+ if (!LoadDescription(error) || |
+ !LoadHomepageURL(error) || |
+ !LoadUpdateURL(error) || |
+ !LoadIcons(error) || |
+ !LoadCommands(error) || |
+ !LoadPlugins(error) || |
+ !LoadNaClModules(error) || |
+ !LoadWebAccessibleResources(error) || |
+ !LoadSandboxedPages(error) || |
+ !LoadRequirements(error) || |
+ !LoadDefaultLocale(error) || |
+ !LoadOfflineEnabled(error) || |
+ !LoadOptionsPage(error) || |
+ // LoadBackgroundScripts() must be called before LoadBackgroundPage(). |
+ !LoadBackgroundScripts(error) || |
+ !LoadBackgroundPage(api_permissions, error) || |
+ !LoadBackgroundPersistent(api_permissions, error) || |
+ !LoadBackgroundAllowJSAccess(api_permissions, error) || |
+ !LoadWebIntentServices(error) || |
+ !LoadOAuth2Info(error)) |
+ return false; |
return true; |
} |
-bool Extension::LoadContentScripts(string16* error) { |
- if (!manifest_->HasKey(keys::kContentScripts)) |
- return true; |
- ListValue* list_value; |
- if (!manifest_->GetList(keys::kContentScripts, &list_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); |
+bool Extension::LoadDescription(string16* error) { |
+ if (manifest_->HasKey(keys::kDescription) && |
+ !manifest_->GetString(keys::kDescription, &description_)) { |
+ *error = ASCIIToUTF16(errors::kInvalidDescription); |
return false; |
} |
- |
- for (size_t i = 0; i < list_value->GetSize(); ++i) { |
- DictionaryValue* content_script = NULL; |
- if (!list_value->GetDictionary(i, &content_script)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidContentScript, base::IntToString(i)); |
- return false; |
- } |
- |
- UserScript script; |
- if (!LoadUserScriptHelper(content_script, i, error, &script)) |
- return false; // Failed to parse script context definition. |
- script.set_extension_id(id()); |
- if (converted_from_user_script_) { |
- script.set_emulate_greasemonkey(true); |
- script.set_match_all_frames(true); // Greasemonkey matches all frames. |
- } |
- content_scripts_.push_back(script); |
- } |
return true; |
} |
-bool Extension::LoadPageAction(string16* error) { |
- DictionaryValue* page_action_value = NULL; |
- |
- if (manifest_->HasKey(keys::kPageActions)) { |
- ListValue* list_value = NULL; |
- if (!manifest_->GetList(keys::kPageActions, &list_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionsList); |
- return false; |
- } |
- |
- size_t list_value_length = list_value->GetSize(); |
- |
- if (list_value_length == 0u) { |
- // A list with zero items is allowed, and is equivalent to not having |
- // a page_actions key in the manifest. Don't set |page_action_value|. |
- } else if (list_value_length == 1u) { |
- if (!list_value->GetDictionary(0, &page_action_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageAction); |
- return false; |
- } |
- } else { // list_value_length > 1u. |
- *error = ASCIIToUTF16(errors::kInvalidPageActionsListSize); |
- return false; |
- } |
- } else if (manifest_->HasKey(keys::kPageAction)) { |
- if (!manifest_->GetDictionary(keys::kPageAction, &page_action_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageAction); |
+bool Extension::LoadManifestVersion(string16* error) { |
+ // Get the original value out of the dictionary so that we can validate it |
+ // more strictly. |
+ if (manifest_->value()->HasKey(keys::kManifestVersion)) { |
+ int manifest_version = 1; |
+ if (!manifest_->GetInteger(keys::kManifestVersion, &manifest_version) || |
+ manifest_version < 1) { |
+ *error = ASCIIToUTF16(errors::kInvalidManifestVersion); |
return false; |
} |
} |
- // If page_action_value is not NULL, then there was a valid page action. |
- if (page_action_value) { |
- page_action_info_ = LoadExtensionActionInfoHelper( |
- page_action_value, Extension::ActionInfo::TYPE_PAGE, error); |
- if (!page_action_info_.get()) |
- return false; // Failed to parse page action definition. |
+ manifest_version_ = manifest_->GetManifestVersion(); |
+ if (creation_flags_ & REQUIRE_MODERN_MANIFEST_VERSION && |
+ manifest_version_ < kModernManifestVersion && |
+ !CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kAllowLegacyExtensionManifests)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidManifestVersionOld, |
+ base::IntToString(kModernManifestVersion)); |
+ return false; |
} |
return true; |
} |
-bool Extension::LoadBrowserAction(string16* error) { |
- if (!manifest_->HasKey(keys::kBrowserAction)) |
+bool Extension::LoadHomepageURL(string16* error) { |
+ if (!manifest_->HasKey(keys::kHomepageURL)) |
return true; |
- DictionaryValue* browser_action_value = NULL; |
- if (!manifest_->GetDictionary(keys::kBrowserAction, &browser_action_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidBrowserAction); |
+ std::string tmp_homepage_url; |
+ if (!manifest_->GetString(keys::kHomepageURL, &tmp_homepage_url)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidHomepageURL, ""); |
+ return false; |
+ } |
+ homepage_url_ = GURL(tmp_homepage_url); |
+ if (!homepage_url_.is_valid() || |
+ (!homepage_url_.SchemeIs("http") && |
+ !homepage_url_.SchemeIs("https"))) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidHomepageURL, tmp_homepage_url); |
return false; |
} |
- |
- browser_action_info_ = LoadExtensionActionInfoHelper( |
- browser_action_value, Extension::ActionInfo::TYPE_BROWSER, error); |
- if (!browser_action_info_.get()) |
- return false; // Failed to parse browser action definition. |
return true; |
} |
-bool Extension::LoadSystemIndicator(APIPermissionSet* api_permissions, |
- string16* error) { |
- if (!manifest_->HasKey(keys::kSystemIndicator)) { |
- // There was no manifest entry for the system indicator. |
+bool Extension::LoadUpdateURL(string16* error) { |
+ if (!manifest_->HasKey(keys::kUpdateURL)) |
return true; |
+ std::string tmp_update_url; |
+ if (!manifest_->GetString(keys::kUpdateURL, &tmp_update_url)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidUpdateURL, ""); |
+ return false; |
} |
- |
- DictionaryValue* system_indicator_value = NULL; |
- if (!manifest_->GetDictionary(keys::kSystemIndicator, |
- &system_indicator_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidSystemIndicator); |
+ update_url_ = GURL(tmp_update_url); |
+ if (!update_url_.is_valid() || |
+ update_url_.has_ref()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidUpdateURL, tmp_update_url); |
return false; |
} |
+ return true; |
+} |
- system_indicator_info_ = LoadExtensionActionInfoHelper( |
- system_indicator_value, |
- Extension::ActionInfo::TYPE_SYSTEM_INDICATOR, |
- error); |
- |
- if (!system_indicator_info_.get()) { |
+bool Extension::LoadIcons(string16* error) { |
+ if (!manifest_->HasKey(keys::kIcons)) |
+ return true; |
+ DictionaryValue* icons_value = NULL; |
+ if (!manifest_->GetDictionary(keys::kIcons, &icons_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIcons); |
return false; |
} |
- // Because the manifest was successfully parsed, auto-grant the permission. |
- // TODO(dewittj) Add this for all extension action APIs. |
- api_permissions->insert(APIPermission::kSystemIndicator); |
- |
- return true; |
+ return LoadIconsFromDictionary(icons_value, |
+ extension_misc::kExtensionIconSizes, |
+ extension_misc::kNumExtensionIconSizes, |
+ &icons_, |
+ error); |
} |
-bool Extension::LoadScriptBadge(string16* error) { |
- if (manifest_->HasKey(keys::kScriptBadge)) { |
- if (!FeatureSwitch::script_badges()->IsEnabled()) { |
- // So as to not confuse developers if they specify a script badge section |
- // in the manifest, show a warning if the script badge declaration isn't |
- // going to have any effect. |
- install_warnings_.push_back( |
- InstallWarning(InstallWarning::FORMAT_TEXT, |
- errors::kScriptBadgeRequiresFlag)); |
+bool Extension::LoadCommands(string16* error) { |
+ if (manifest_->HasKey(keys::kCommands)) { |
+ DictionaryValue* commands = NULL; |
+ if (!manifest_->GetDictionary(keys::kCommands, &commands)) { |
+ *error = ASCIIToUTF16(errors::kInvalidCommandsKey); |
+ return false; |
} |
- DictionaryValue* script_badge_value = NULL; |
- if (!manifest_->GetDictionary(keys::kScriptBadge, &script_badge_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidScriptBadge); |
+ if (commands->size() > kMaxCommandsPerExtension) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidKeyBindingTooMany, |
+ base::IntToString(kMaxCommandsPerExtension)); |
return false; |
} |
- script_badge_info_ = LoadExtensionActionInfoHelper( |
- script_badge_value, Extension::ActionInfo::TYPE_SCRIPT_BADGE, error); |
- if (!script_badge_info_.get()) |
- return false; // Failed to parse script badge definition. |
- } else { |
- script_badge_info_.reset(new ActionInfo()); |
- } |
+ int command_index = 0; |
+ for (DictionaryValue::key_iterator iter = commands->begin_keys(); |
+ iter != commands->end_keys(); ++iter) { |
+ ++command_index; |
- // Script badges always use their extension's title and icon so users can rely |
- // on the visual appearance to know which extension is running. This isn't |
- // bulletproof since an malicious extension could use a different 16x16 icon |
- // that matches the icon of a trusted extension, and users wouldn't be warned |
- // during installation. |
+ DictionaryValue* command = NULL; |
+ if (!commands->GetDictionary(*iter, &command)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidKeyBindingDictionary, |
+ base::IntToString(command_index)); |
+ return false; |
+ } |
- if (!script_badge_info_->default_title.empty()) { |
- install_warnings_.push_back( |
- InstallWarning(InstallWarning::FORMAT_TEXT, |
- errors::kScriptBadgeTitleIgnored)); |
- } |
- script_badge_info_->default_title = name(); |
+ scoped_ptr<extensions::Command> binding(new extensions::Command()); |
+ if (!binding->Parse(command, *iter, command_index, error)) |
+ return false; // |error| already set. |
- if (!script_badge_info_->default_icon.empty()) { |
- install_warnings_.push_back( |
- InstallWarning(InstallWarning::FORMAT_TEXT, |
- errors::kScriptBadgeIconIgnored)); |
+ std::string command_name = binding->command_name(); |
+ if (command_name == values::kPageActionCommandEvent) { |
+ page_action_command_.reset(binding.release()); |
+ } else if (command_name == values::kBrowserActionCommandEvent) { |
+ browser_action_command_.reset(binding.release()); |
+ } else if (command_name == values::kScriptBadgeCommandEvent) { |
+ script_badge_command_.reset(binding.release()); |
+ } else { |
+ if (command_name[0] != '_') // All commands w/underscore are reserved. |
+ named_commands_[command_name] = *binding.get(); |
+ } |
+ } |
} |
- script_badge_info_->default_icon.Clear(); |
- for (size_t i = 0; i < extension_misc::kNumScriptBadgeIconSizes; i++) { |
- std::string path = icons().Get(extension_misc::kScriptBadgeIconSizes[i], |
- ExtensionIconSet::MATCH_BIGGER); |
- if (!path.empty()) |
- script_badge_info_->default_icon.Add( |
- extension_misc::kScriptBadgeIconSizes[i], path); |
+ if (manifest_->HasKey(keys::kBrowserAction) && |
+ !browser_action_command_.get()) { |
+ // If the extension defines a browser action, but no command for it, then |
+ // we synthesize a generic one, so the user can configure a shortcut for it. |
+ // No keyboard shortcut will be assigned to it, until the user selects one. |
+ browser_action_command_.reset( |
+ new extensions::Command( |
+ values::kBrowserActionCommandEvent, string16(), "")); |
} |
return true; |
} |
-bool Extension::LoadFileBrowserHandlers(string16* error) { |
- if (!manifest_->HasKey(keys::kFileBrowserHandlers)) |
+bool Extension::LoadPlugins(string16* error) { |
+ if (!manifest_->HasKey(keys::kPlugins)) |
return true; |
- ListValue* file_browser_handlers_value = NULL; |
- if (!manifest_->GetList(keys::kFileBrowserHandlers, |
- &file_browser_handlers_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); |
- return false; |
- } |
- file_browser_handlers_.reset( |
- LoadFileBrowserHandlersHelper(file_browser_handlers_value, error)); |
- if (!file_browser_handlers_.get()) |
- return false; // Failed to parse file browser actions definition. |
- return true; |
-} |
- |
-Extension::FileBrowserHandlerList* Extension::LoadFileBrowserHandlersHelper( |
- const ListValue* extension_actions, string16* error) { |
- scoped_ptr<FileBrowserHandlerList> result( |
- new FileBrowserHandlerList()); |
- for (ListValue::const_iterator iter = extension_actions->begin(); |
- iter != extension_actions->end(); |
- ++iter) { |
- if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) { |
- *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); |
- return NULL; |
- } |
- scoped_ptr<FileBrowserHandler> action( |
- LoadFileBrowserHandler( |
- reinterpret_cast<DictionaryValue*>(*iter), error)); |
- if (!action.get()) |
- return NULL; // Failed to parse file browser action definition. |
- result->push_back(linked_ptr<FileBrowserHandler>(action.release())); |
- } |
- return result.release(); |
-} |
- |
-FileBrowserHandler* Extension::LoadFileBrowserHandler( |
- const DictionaryValue* file_browser_handler, string16* error) { |
- scoped_ptr<FileBrowserHandler> result(new FileBrowserHandler()); |
- result->set_extension_id(id()); |
- |
- std::string id; |
- // Read the file action |id| (mandatory). |
- if (!file_browser_handler->HasKey(keys::kPageActionId) || |
- !file_browser_handler->GetString(keys::kPageActionId, &id)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionId); |
- return NULL; |
- } |
- result->set_id(id); |
- // Read the page action title from |default_title| (mandatory). |
- std::string title; |
- if (!file_browser_handler->HasKey(keys::kPageActionDefaultTitle) || |
- !file_browser_handler->GetString(keys::kPageActionDefaultTitle, &title)) { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); |
- return NULL; |
+ ListValue* list_value = NULL; |
+ if (!manifest_->GetList(keys::kPlugins, &list_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPlugins); |
+ return false; |
} |
- result->set_title(title); |
- // Initialize access permissions (optional). |
- const ListValue* access_list_value = NULL; |
- if (file_browser_handler->HasKey(keys::kFileAccessList)) { |
- if (!file_browser_handler->GetList(keys::kFileAccessList, |
- &access_list_value) || |
- access_list_value->empty()) { |
- *error = ASCIIToUTF16(errors::kInvalidFileAccessList); |
- return NULL; |
+ for (size_t i = 0; i < list_value->GetSize(); ++i) { |
+ DictionaryValue* plugin_value = NULL; |
+ if (!list_value->GetDictionary(i, &plugin_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPlugins); |
+ return false; |
} |
- for (size_t i = 0; i < access_list_value->GetSize(); ++i) { |
- std::string access; |
- if (!access_list_value->GetString(i, &access) || |
- result->AddFileAccessPermission(access)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidFileAccessValue, base::IntToString(i)); |
- return NULL; |
- } |
+ // Get plugins[i].path. |
+ std::string path_str; |
+ if (!plugin_value->GetString(keys::kPluginsPath, &path_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidPluginsPath, base::IntToString(i)); |
+ return false; |
} |
- } |
- if (!result->ValidateFileAccessPermissions()) { |
- *error = ASCIIToUTF16(errors::kInvalidFileAccessList); |
- return NULL; |
- } |
- // Initialize file filters (mandatory, unless "create" access is specified, |
- // in which case is ignored). |
- if (!result->HasCreateAccessPermission()) { |
- const ListValue* list_value = NULL; |
- if (!file_browser_handler->HasKey(keys::kFileFilters) || |
- !file_browser_handler->GetList(keys::kFileFilters, &list_value) || |
- list_value->empty()) { |
- *error = ASCIIToUTF16(errors::kInvalidFileFiltersList); |
- return NULL; |
- } |
- for (size_t i = 0; i < list_value->GetSize(); ++i) { |
- std::string filter; |
- if (!list_value->GetString(i, &filter)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidFileFilterValue, base::IntToString(i)); |
- return NULL; |
- } |
- StringToLowerASCII(&filter); |
- if (!StartsWithASCII(filter, |
- std::string(chrome::kFileSystemScheme) + ':', |
- true)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidURLPatternError, filter); |
- return NULL; |
- } |
- // The user inputs filesystem:*; we don't actually implement scheme |
- // wildcards in URLPattern, so transform to what will match correctly. |
- filter.replace(0, 11, "chrome-extension://*/"); |
- URLPattern pattern(URLPattern::SCHEME_EXTENSION); |
- if (pattern.Parse(filter) != URLPattern::PARSE_SUCCESS) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidURLPatternError, filter); |
- return NULL; |
- } |
- std::string path = pattern.path(); |
- bool allowed = path == "/*" || path == "/*.*" || |
- (path.compare(0, 3, "/*.") == 0 && |
- path.find_first_of('*', 3) == std::string::npos); |
- if (!allowed) { |
+ // Get plugins[i].content (optional). |
+ bool is_public = false; |
+ if (plugin_value->HasKey(keys::kPluginsPublic)) { |
+ if (!plugin_value->GetBoolean(keys::kPluginsPublic, &is_public)) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidURLPatternError, filter); |
- return NULL; |
+ errors::kInvalidPluginsPublic, base::IntToString(i)); |
+ return false; |
} |
- result->AddPattern(pattern); |
} |
- } |
- std::string default_icon; |
- // Read the file browser action |default_icon| (optional). |
- if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) { |
- if (!file_browser_handler->GetString( |
- keys::kPageActionDefaultIcon, &default_icon) || |
- default_icon.empty()) { |
- *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
- return NULL; |
+ // We don't allow extensions to load NPAPI plugins on Chrome OS, or under |
+ // Windows 8 Metro mode, but still parse the entries to display consistent |
+ // error messages. If the extension actually requires the plugins then |
+ // LoadRequirements will prevent it loading. |
+#if defined(OS_CHROMEOS) |
+ continue; |
+#elif defined(OS_WIN) |
+ if (base::win::IsMetroProcess()) { |
+ continue; |
} |
- result->set_icon_path(default_icon); |
+#endif // defined(OS_WIN). |
+ plugins_.push_back(PluginInfo()); |
+ plugins_.back().path = path().Append(FilePath::FromUTF8Unsafe(path_str)); |
+ plugins_.back().is_public = is_public; |
} |
- |
- return result.release(); |
+ return true; |
} |
-bool Extension::LoadChromeURLOverrides(string16* error) { |
- if (!manifest_->HasKey(keys::kChromeURLOverrides)) |
+bool Extension::LoadNaClModules(string16* error) { |
+ if (!manifest_->HasKey(keys::kNaClModules)) |
return true; |
- DictionaryValue* overrides = NULL; |
- if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) { |
- *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); |
+ ListValue* list_value = NULL; |
+ if (!manifest_->GetList(keys::kNaClModules, &list_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidNaClModules); |
return false; |
} |
- // Validate that the overrides are all strings |
- for (DictionaryValue::key_iterator iter = overrides->begin_keys(); |
- iter != overrides->end_keys(); ++iter) { |
- std::string page = *iter; |
- std::string val; |
- // Restrict override pages to a list of supported URLs. |
- bool is_override = (page != chrome::kChromeUINewTabHost && |
- page != chrome::kChromeUIBookmarksHost && |
- page != chrome::kChromeUIHistoryHost); |
-#if defined(OS_CHROMEOS) |
- is_override = (is_override && |
- page != chrome::kChromeUIActivationMessageHost); |
-#endif |
-#if defined(FILE_MANAGER_EXTENSION) |
- is_override = (is_override && |
- !(location() == COMPONENT && |
- page == chrome::kChromeUIFileManagerHost)); |
-#endif |
+ for (size_t i = 0; i < list_value->GetSize(); ++i) { |
+ DictionaryValue* module_value = NULL; |
+ if (!list_value->GetDictionary(i, &module_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidNaClModules); |
+ return false; |
+ } |
- if (is_override || !overrides->GetStringWithoutPathExpansion(*iter, &val)) { |
- *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); |
+ // Get nacl_modules[i].path. |
+ std::string path_str; |
+ if (!module_value->GetString(keys::kNaClModulesPath, &path_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidNaClModulesPath, base::IntToString(i)); |
return false; |
} |
- // Replace the entry with a fully qualified chrome-extension:// URL. |
- chrome_url_overrides_[page] = GetResourceURL(val); |
- // For component extensions, add override URL to extent patterns. |
- if (is_legacy_packaged_app() && location() == COMPONENT) { |
- URLPattern pattern(URLPattern::SCHEME_CHROMEUI); |
- std::string url = base::StringPrintf(kOverrideExtentUrlPatternFormat, |
- page.c_str()); |
- if (pattern.Parse(url) != URLPattern::PARSE_SUCCESS) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidURLPatternError, url); |
- return false; |
- } |
- extent_.AddPattern(pattern); |
+ // Get nacl_modules[i].mime_type. |
+ std::string mime_type; |
+ if (!module_value->GetString(keys::kNaClModulesMIMEType, &mime_type)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidNaClModulesMIMEType, base::IntToString(i)); |
+ return false; |
} |
+ |
+ nacl_modules_.push_back(NaClModuleInfo()); |
+ nacl_modules_.back().url = GetResourceURL(path_str); |
+ nacl_modules_.back().mime_type = mime_type; |
} |
- // An extension may override at most one page. |
- if (overrides->size() > 1) { |
- *error = ASCIIToUTF16(errors::kMultipleOverrides); |
+ return true; |
+} |
+ |
+bool Extension::LoadWebAccessibleResources(string16* error) { |
+ if (!manifest_->HasKey(keys::kWebAccessibleResources)) |
+ return true; |
+ ListValue* list_value = NULL; |
+ if (!manifest_->GetList(keys::kWebAccessibleResources, &list_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidWebAccessibleResourcesList); |
return false; |
} |
+ for (size_t i = 0; i < list_value->GetSize(); ++i) { |
+ std::string relative_path; |
+ if (!list_value->GetString(i, &relative_path)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidWebAccessibleResource, base::IntToString(i)); |
+ return false; |
+ } |
+ URLPattern pattern(URLPattern::SCHEME_EXTENSION); |
+ if (pattern.Parse(extension_url_.spec()) != URLPattern::PARSE_SUCCESS) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidURLPatternError, extension_url_.spec()); |
+ return false; |
+ } |
+ while (relative_path[0] == '/') |
+ relative_path = relative_path.substr(1, relative_path.length() - 1); |
+ pattern.SetPath(pattern.path() + relative_path); |
+ web_accessible_resources_.AddPattern(pattern); |
+ } |
return true; |
} |
-bool Extension::LoadOmnibox(string16* error) { |
- if (!manifest_->HasKey(keys::kOmnibox)) |
+bool Extension::LoadSandboxedPages(string16* error) { |
+ if (!manifest_->HasPath(keys::kSandboxedPages)) |
return true; |
- if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) || |
- omnibox_keyword_.empty()) { |
- *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword); |
+ |
+ ListValue* list_value = NULL; |
+ if (!manifest_->GetList(keys::kSandboxedPages, &list_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesList); |
return false; |
} |
+ for (size_t i = 0; i < list_value->GetSize(); ++i) { |
+ std::string relative_path; |
+ if (!list_value->GetString(i, &relative_path)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidSandboxedPage, base::IntToString(i)); |
+ return false; |
+ } |
+ URLPattern pattern(URLPattern::SCHEME_EXTENSION); |
+ if (pattern.Parse(extension_url_.spec()) != URLPattern::PARSE_SUCCESS) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidURLPatternError, extension_url_.spec()); |
+ return false; |
+ } |
+ while (relative_path[0] == '/') |
+ relative_path = relative_path.substr(1, relative_path.length() - 1); |
+ pattern.SetPath(pattern.path() + relative_path); |
+ sandboxed_pages_.AddPattern(pattern); |
+ } |
+ |
+ if (manifest_->HasPath(keys::kSandboxedPagesCSP)) { |
+ if (!manifest_->GetString( |
+ keys::kSandboxedPagesCSP, &sandboxed_pages_content_security_policy_)) { |
+ *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP); |
+ return false; |
+ } |
+ |
+ if (!ContentSecurityPolicyIsLegal( |
+ sandboxed_pages_content_security_policy_) || |
+ !ContentSecurityPolicyIsSandboxed( |
+ sandboxed_pages_content_security_policy_, GetType())) { |
+ *error = ASCIIToUTF16(errors::kInvalidSandboxedPagesCSP); |
+ return false; |
+ } |
+ } else { |
+ sandboxed_pages_content_security_policy_ = |
+ kDefaultSandboxedPageContentSecurityPolicy; |
+ CHECK(ContentSecurityPolicyIsSandboxed( |
+ sandboxed_pages_content_security_policy_, GetType())); |
+ } |
+ |
return true; |
} |
-bool Extension::LoadTextToSpeechVoices(string16* error) { |
- if (!manifest_->HasKey(keys::kTtsEngine)) |
+bool Extension::LoadRequirements(string16* error) { |
+ // Before parsing requirements from the manifest, automatically default the |
+ // NPAPI plugin requirement based on whether it includes NPAPI plugins. |
+ ListValue* list_value = NULL; |
+ requirements_.npapi = |
+ manifest_->GetList(keys::kPlugins, &list_value) && !list_value->empty(); |
+ |
+ if (!manifest_->HasKey(keys::kRequirements)) |
return true; |
- DictionaryValue* tts_dict = NULL; |
- if (!manifest_->GetDictionary(keys::kTtsEngine, &tts_dict)) { |
- *error = ASCIIToUTF16(errors::kInvalidTts); |
+ |
+ DictionaryValue* requirements_value = NULL; |
+ if (!manifest_->GetDictionary(keys::kRequirements, &requirements_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidRequirements); |
return false; |
} |
- if (tts_dict->HasKey(keys::kTtsVoices)) { |
- ListValue* tts_voices = NULL; |
- if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoices); |
+ for (DictionaryValue::key_iterator it = requirements_value->begin_keys(); |
+ it != requirements_value->end_keys(); ++it) { |
+ DictionaryValue* requirement_value; |
+ if (!requirements_value->GetDictionaryWithoutPathExpansion( |
+ *it, &requirement_value)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidRequirement, *it); |
return false; |
} |
- for (size_t i = 0; i < tts_voices->GetSize(); i++) { |
- DictionaryValue* one_tts_voice = NULL; |
- if (!tts_voices->GetDictionary(i, &one_tts_voice)) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoices); |
- return false; |
- } |
- |
- TtsVoice voice_data; |
- if (one_tts_voice->HasKey(keys::kTtsVoicesVoiceName)) { |
- if (!one_tts_voice->GetString( |
- keys::kTtsVoicesVoiceName, &voice_data.voice_name)) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoicesVoiceName); |
+ if (*it == "plugins") { |
+ for (DictionaryValue::key_iterator plugin_it = |
+ requirement_value->begin_keys(); |
+ plugin_it != requirement_value->end_keys(); ++plugin_it) { |
+ bool plugin_required = false; |
+ if (!requirement_value->GetBoolean(*plugin_it, &plugin_required)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidRequirement, *it); |
return false; |
} |
- } |
- if (one_tts_voice->HasKey(keys::kTtsVoicesLang)) { |
- if (!one_tts_voice->GetString( |
- keys::kTtsVoicesLang, &voice_data.lang) || |
- !l10n_util::IsValidLocaleSyntax(voice_data.lang)) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoicesLang); |
+ if (*plugin_it == "npapi") { |
+ requirements_.npapi = plugin_required; |
+ } else { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidRequirement, *it); |
return false; |
} |
} |
- if (one_tts_voice->HasKey(keys::kTtsVoicesGender)) { |
- if (!one_tts_voice->GetString( |
- keys::kTtsVoicesGender, &voice_data.gender) || |
- (voice_data.gender != keys::kTtsGenderMale && |
- voice_data.gender != keys::kTtsGenderFemale)) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoicesGender); |
- return false; |
- } |
+ } else if (*it == "3D") { |
+ ListValue* features = NULL; |
+ if (!requirement_value->GetListWithoutPathExpansion("features", |
+ &features) || |
+ !features) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidRequirement, *it); |
+ return false; |
} |
- if (one_tts_voice->HasKey(keys::kTtsVoicesEventTypes)) { |
- ListValue* event_types_list; |
- if (!one_tts_voice->GetList( |
- keys::kTtsVoicesEventTypes, &event_types_list)) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
- return false; |
- } |
- for (size_t i = 0; i < event_types_list->GetSize(); i++) { |
- std::string event_type; |
- if (!event_types_list->GetString(i, &event_type)) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
- return false; |
- } |
- if (event_type != keys::kTtsVoicesEventTypeEnd && |
- event_type != keys::kTtsVoicesEventTypeError && |
- event_type != keys::kTtsVoicesEventTypeMarker && |
- event_type != keys::kTtsVoicesEventTypeSentence && |
- event_type != keys::kTtsVoicesEventTypeStart && |
- event_type != keys::kTtsVoicesEventTypeWord) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
- return false; |
- } |
- if (voice_data.event_types.find(event_type) != |
- voice_data.event_types.end()) { |
- *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
+ |
+ for (base::ListValue::iterator feature_it = features->begin(); |
+ feature_it != features->end(); |
+ ++feature_it) { |
+ std::string feature; |
+ if ((*feature_it)->GetAsString(&feature)) { |
+ if (feature == "webgl") { |
+ requirements_.webgl = true; |
+ } else if (feature == "css3d") { |
+ requirements_.css3d = true; |
+ } else { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidRequirement, *it); |
return false; |
} |
- voice_data.event_types.insert(event_type); |
} |
} |
- |
- tts_voices_.push_back(voice_data); |
+ } else { |
+ *error = ASCIIToUTF16(errors::kInvalidRequirements); |
+ return false; |
} |
} |
return true; |
} |
-bool Extension::LoadIncognitoMode(string16* error) { |
- // Apps default to split mode, extensions default to spanning. |
- incognito_split_mode_ = is_app(); |
- if (!manifest_->HasKey(keys::kIncognito)) |
+bool Extension::LoadDefaultLocale(string16* error) { |
+ if (!manifest_->HasKey(keys::kDefaultLocale)) |
return true; |
- std::string value; |
- if (!manifest_->GetString(keys::kIncognito, &value)) { |
- *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); |
+ if (!manifest_->GetString(keys::kDefaultLocale, &default_locale_) || |
+ !l10n_util::IsValidLocaleSyntax(default_locale_)) { |
+ *error = ASCIIToUTF16(errors::kInvalidDefaultLocale); |
return false; |
} |
- if (value == values::kIncognitoSpanning) { |
- incognito_split_mode_ = false; |
- } else if (value == values::kIncognitoSplit) { |
- incognito_split_mode_ = true; |
- } else { |
- *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); |
+ return true; |
+} |
+ |
+bool Extension::LoadOfflineEnabled(string16* error) { |
+ // Defaults to false, except for platform apps which are offline by default. |
+ if (!manifest_->HasKey(keys::kOfflineEnabled)) { |
+ offline_enabled_ = is_platform_app(); |
+ return true; |
+ } |
+ if (!manifest_->GetBoolean(keys::kOfflineEnabled, &offline_enabled_)) { |
+ *error = ASCIIToUTF16(errors::kInvalidOfflineEnabled); |
return false; |
} |
return true; |
} |
-bool Extension::LoadContentSecurityPolicy(string16* error) { |
- const std::string& key = is_platform_app() ? |
- keys::kPlatformAppContentSecurityPolicy : keys::kContentSecurityPolicy; |
+bool Extension::LoadOptionsPage(string16* error) { |
+ if (!manifest_->HasKey(keys::kOptionsPage)) |
+ return true; |
+ std::string options_str; |
+ if (!manifest_->GetString(keys::kOptionsPage, &options_str)) { |
+ *error = ASCIIToUTF16(errors::kInvalidOptionsPage); |
+ return false; |
+ } |
- if (manifest_->HasPath(key)) { |
- std::string content_security_policy; |
- if (!manifest_->GetString(key, &content_security_policy)) { |
- *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); |
+ if (is_hosted_app()) { |
+ // hosted apps require an absolute URL. |
+ GURL options_url(options_str); |
+ if (!options_url.is_valid() || |
+ !(options_url.SchemeIs("http") || options_url.SchemeIs("https"))) { |
+ *error = ASCIIToUTF16(errors::kInvalidOptionsPageInHostedApp); |
return false; |
} |
- if (!ContentSecurityPolicyIsLegal(content_security_policy)) { |
- *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); |
+ options_url_ = options_url; |
+ } else { |
+ GURL absolute(options_str); |
+ if (absolute.is_valid()) { |
+ *error = ASCIIToUTF16(errors::kInvalidOptionsPageExpectUrlInPackage); |
return false; |
} |
- if (manifest_version_ >= 2 && |
- !ContentSecurityPolicyIsSecure(content_security_policy, GetType())) { |
- *error = ASCIIToUTF16(errors::kInsecureContentSecurityPolicy); |
+ options_url_ = GetResourceURL(options_str); |
+ if (!options_url_.is_valid()) { |
+ *error = ASCIIToUTF16(errors::kInvalidOptionsPage); |
return false; |
} |
- |
- content_security_policy_ = content_security_policy; |
- } else if (manifest_version_ >= 2) { |
- // Manifest version 2 introduced a default Content-Security-Policy. |
- // TODO(abarth): Should we continue to let extensions override the |
- // default Content-Security-Policy? |
- content_security_policy_ = is_platform_app() ? |
- kDefaultPlatformAppContentSecurityPolicy : |
- kDefaultContentSecurityPolicy; |
- CHECK(ContentSecurityPolicyIsSecure(content_security_policy_, GetType())); |
} |
+ |
return true; |
} |
-bool Extension::LoadAppIsolation(const APIPermissionSet& api_permissions, |
- string16* error) { |
- // Platform apps always get isolated storage. |
- if (is_platform_app()) { |
- is_storage_isolated_ = true; |
- return true; |
- } |
- |
- // Other apps only get it if it is requested _and_ experimental APIs are |
- // enabled. |
- if (!api_permissions.count(APIPermission::kExperimental) || !is_app()) |
- return true; |
+bool Extension::LoadBackgroundScripts(string16* error) { |
+ const std::string& key = is_platform_app() ? |
+ keys::kPlatformAppBackgroundScripts : keys::kBackgroundScripts; |
+ return LoadBackgroundScripts(key, error); |
+} |
- Value* tmp_isolation = NULL; |
- if (!manifest_->Get(keys::kIsolation, &tmp_isolation)) |
+bool Extension::LoadBackgroundScripts(const std::string& key, string16* error) { |
+ Value* background_scripts_value = NULL; |
+ if (!manifest_->Get(key, &background_scripts_value)) |
return true; |
- if (tmp_isolation->GetType() != Value::TYPE_LIST) { |
- *error = ASCIIToUTF16(errors::kInvalidIsolation); |
+ CHECK(background_scripts_value); |
+ if (background_scripts_value->GetType() != Value::TYPE_LIST) { |
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts); |
return false; |
} |
- ListValue* isolation_list = static_cast<ListValue*>(tmp_isolation); |
- for (size_t i = 0; i < isolation_list->GetSize(); ++i) { |
- std::string isolation_string; |
- if (!isolation_list->GetString(i, &isolation_string)) { |
+ ListValue* background_scripts = |
+ static_cast<ListValue*>(background_scripts_value); |
+ for (size_t i = 0; i < background_scripts->GetSize(); ++i) { |
+ std::string script; |
+ if (!background_scripts->GetString(i, &script)) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidIsolationValue, |
- base::UintToString(i)); |
+ errors::kInvalidBackgroundScript, base::IntToString(i)); |
return false; |
} |
+ background_scripts_.push_back(script); |
+ } |
- // Check for isolated storage. |
- if (isolation_string == values::kIsolatedStorage) { |
- is_storage_isolated_ = true; |
- } else { |
- DLOG(WARNING) << "Did not recognize isolation type: " << isolation_string; |
- } |
+ return true; |
+} |
+ |
+bool Extension::LoadBackgroundPage( |
+ const APIPermissionSet& api_permissions, |
+ string16* error) { |
+ if (is_platform_app()) { |
+ return LoadBackgroundPage( |
+ keys::kPlatformAppBackgroundPage, api_permissions, error); |
+ } |
+ |
+ if (!LoadBackgroundPage(keys::kBackgroundPage, api_permissions, error)) |
+ return false; |
+ if (background_url_.is_empty()) { |
+ return LoadBackgroundPage( |
+ keys::kBackgroundPageLegacy, api_permissions, error); |
} |
return true; |
} |
-bool Extension::LoadThemeFeatures(string16* error) { |
- if (!manifest_->HasKey(keys::kTheme)) |
+bool Extension::LoadBackgroundPage( |
+ const std::string& key, |
+ const APIPermissionSet& api_permissions, |
+ string16* error) { |
+ base::Value* background_page_value = NULL; |
+ if (!manifest_->Get(key, &background_page_value)) |
return true; |
- DictionaryValue* theme_value = NULL; |
- if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) { |
- *error = ASCIIToUTF16(errors::kInvalidTheme); |
+ |
+ if (!background_scripts_.empty()) { |
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination); |
return false; |
} |
- if (!LoadThemeImages(theme_value, error)) |
- return false; |
- if (!LoadThemeColors(theme_value, error)) |
- return false; |
- if (!LoadThemeTints(theme_value, error)) |
- return false; |
- if (!LoadThemeDisplayProperties(theme_value, error)) |
+ |
+ |
+ std::string background_str; |
+ if (!background_page_value->GetAsString(&background_str)) { |
+ *error = ASCIIToUTF16(errors::kInvalidBackground); |
return false; |
+ } |
- return true; |
-} |
+ if (is_hosted_app()) { |
+ background_url_ = GURL(background_str); |
-bool Extension::LoadThemeImages(const DictionaryValue* theme_value, |
- string16* error) { |
- const DictionaryValue* images_value = NULL; |
- if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { |
- // Validate that the images are all strings |
- for (DictionaryValue::key_iterator iter = images_value->begin_keys(); |
- iter != images_value->end_keys(); ++iter) { |
- std::string val; |
- if (!images_value->GetString(*iter, &val)) { |
- *error = ASCIIToUTF16(errors::kInvalidThemeImages); |
- return false; |
- } |
+ // Make sure "background" permission is set. |
+ if (!api_permissions.count(APIPermission::kBackground)) { |
+ *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded); |
+ return false; |
+ } |
+ // Hosted apps require an absolute URL. |
+ if (!background_url_.is_valid()) { |
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); |
+ return false; |
} |
- theme_images_.reset(images_value->DeepCopy()); |
- } |
- return true; |
-} |
-bool Extension::LoadThemeColors(const DictionaryValue* theme_value, |
- string16* error) { |
- const DictionaryValue* colors_value = NULL; |
- if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) { |
- // Validate that the colors are RGB or RGBA lists |
- for (DictionaryValue::key_iterator iter = colors_value->begin_keys(); |
- iter != colors_value->end_keys(); ++iter) { |
- const ListValue* color_list = NULL; |
- double alpha = 0.0; |
- int color = 0; |
- // The color must be a list |
- if (!colors_value->GetListWithoutPathExpansion(*iter, &color_list) || |
- // And either 3 items (RGB) or 4 (RGBA) |
- ((color_list->GetSize() != 3) && |
- ((color_list->GetSize() != 4) || |
- // For RGBA, the fourth item must be a real or int alpha value. |
- // Note that GetDouble() can get an integer value. |
- !color_list->GetDouble(3, &alpha))) || |
- // For both RGB and RGBA, the first three items must be ints (R,G,B) |
- !color_list->GetInteger(0, &color) || |
- !color_list->GetInteger(1, &color) || |
- !color_list->GetInteger(2, &color)) { |
- *error = ASCIIToUTF16(errors::kInvalidThemeColors); |
- return false; |
- } |
+ if (!(background_url_.SchemeIs("https") || |
+ (CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kAllowHTTPBackgroundPage) && |
+ background_url_.SchemeIs("http")))) { |
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp); |
+ return false; |
} |
- theme_colors_.reset(colors_value->DeepCopy()); |
+ } else { |
+ background_url_ = GetResourceURL(background_str); |
} |
+ |
return true; |
} |
-bool Extension::LoadThemeTints(const DictionaryValue* theme_value, |
- string16* error) { |
- const DictionaryValue* tints_value = NULL; |
- if (!theme_value->GetDictionary(keys::kThemeTints, &tints_value)) |
+bool Extension::LoadBackgroundPersistent( |
+ const APIPermissionSet& api_permissions, |
+ string16* error) { |
+ if (is_platform_app()) { |
+ background_page_is_persistent_ = false; |
return true; |
+ } |
- // Validate that the tints are all reals. |
- for (DictionaryValue::key_iterator iter = tints_value->begin_keys(); |
- iter != tints_value->end_keys(); ++iter) { |
- const ListValue* tint_list = NULL; |
- double v = 0.0; |
- if (!tints_value->GetListWithoutPathExpansion(*iter, &tint_list) || |
- tint_list->GetSize() != 3 || |
- !tint_list->GetDouble(0, &v) || |
- !tint_list->GetDouble(1, &v) || |
- !tint_list->GetDouble(2, &v)) { |
- *error = ASCIIToUTF16(errors::kInvalidThemeTints); |
- return false; |
- } |
+ Value* background_persistent = NULL; |
+ if (!manifest_->Get(keys::kBackgroundPersistent, &background_persistent)) |
+ return true; |
+ |
+ if (!background_persistent->GetAsBoolean(&background_page_is_persistent_)) { |
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent); |
+ return false; |
} |
- theme_tints_.reset(tints_value->DeepCopy()); |
+ |
+ if (!has_background_page()) { |
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage); |
+ return false; |
+ } |
+ |
return true; |
} |
-bool Extension::LoadThemeDisplayProperties(const DictionaryValue* theme_value, |
- string16* error) { |
- const DictionaryValue* display_properties_value = NULL; |
- if (theme_value->GetDictionary(keys::kThemeDisplayProperties, |
- &display_properties_value)) { |
- theme_display_properties_.reset( |
- display_properties_value->DeepCopy()); |
+bool Extension::LoadBackgroundAllowJSAccess( |
+ const APIPermissionSet& api_permissions, |
+ string16* error) { |
+ Value* allow_js_access = NULL; |
+ if (!manifest_->Get(keys::kBackgroundAllowJsAccess, &allow_js_access)) |
+ return true; |
+ |
+ if (!allow_js_access->IsType(Value::TYPE_BOOLEAN) || |
+ !allow_js_access->GetAsBoolean(&allow_background_js_access_)) { |
+ *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess); |
+ return false; |
} |
+ |
return true; |
} |
-// static |
-bool Extension::IsTrustedId(const std::string& id) { |
- // See http://b/4946060 for more details. |
- return id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); |
-} |
+bool Extension::LoadWebIntentAction(const std::string& action_name, |
+ const DictionaryValue& intent_service, |
+ string16* error) { |
+ DCHECK(error); |
+ webkit_glue::WebIntentServiceData service; |
+ std::string value; |
-Extension::Extension(const FilePath& path, |
- scoped_ptr<extensions::Manifest> manifest) |
- : manifest_version_(0), |
- incognito_split_mode_(false), |
- offline_enabled_(false), |
- converted_from_user_script_(false), |
- background_page_is_persistent_(true), |
- allow_background_js_access_(true), |
- manifest_(manifest.release()), |
- is_storage_isolated_(false), |
- launch_container_(extension_misc::LAUNCH_TAB), |
- launch_width_(0), |
- launch_height_(0), |
- display_in_launcher_(true), |
- display_in_new_tab_page_(true), |
- wants_file_access_(false), |
- creation_flags_(0) { |
- DCHECK(path.empty() || path.IsAbsolute()); |
- path_ = MaybeNormalizePath(path); |
-} |
+ service.action = UTF8ToUTF16(action_name); |
-Extension::~Extension() { |
-} |
+ const ListValue* mime_types = NULL; |
+ if (!intent_service.HasKey(keys::kIntentType) || |
+ !intent_service.GetList(keys::kIntentType, &mime_types) || |
+ mime_types->GetSize() == 0) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidIntentType, action_name); |
+ return false; |
+ } |
-ExtensionResource Extension::GetResource( |
- const std::string& relative_path) const { |
- std::string new_path = relative_path; |
- // We have some legacy data where resources have leading slashes. |
- // See: http://crbug.com/121164 |
- if (!new_path.empty() && new_path.at(0) == '/') |
- new_path.erase(0, 1); |
-#if defined(OS_POSIX) |
- FilePath relative_file_path(new_path); |
-#elif defined(OS_WIN) |
- FilePath relative_file_path(UTF8ToWide(new_path)); |
-#endif |
- ExtensionResource r(id(), path(), relative_file_path); |
- if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { |
- r.set_follow_symlinks_anywhere(); |
+ std::string href; |
+ if (intent_service.HasKey(keys::kIntentPath)) { |
+ if (!intent_service.GetString(keys::kIntentPath, &href)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIntentHref); |
+ return false; |
+ } |
} |
- return r; |
-} |
-ExtensionResource Extension::GetResource( |
- const FilePath& relative_file_path) const { |
- ExtensionResource r(id(), path(), relative_file_path); |
- if ((creation_flags() & Extension::FOLLOW_SYMLINKS_ANYWHERE)) { |
- r.set_follow_symlinks_anywhere(); |
+ if (intent_service.HasKey(keys::kIntentHref)) { |
+ if (!href.empty()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidIntentHrefOldAndNewKey, action_name, |
+ keys::kIntentPath, keys::kIntentHref); |
+ return false; |
+ } |
+ if (!intent_service.GetString(keys::kIntentHref, &href)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIntentHref); |
+ return false; |
+ } |
} |
- return r; |
-} |
-// TODO(rafaelw): Move ParsePEMKeyBytes, ProducePEM & FormatPEMForOutput to a |
-// util class in base: |
-// http://code.google.com/p/chromium/issues/detail?id=13572 |
-bool Extension::ParsePEMKeyBytes(const std::string& input, |
- std::string* output) { |
- DCHECK(output); |
- if (!output) |
+ // For packaged/hosted apps, empty href implies the respective launch URLs. |
+ if (href.empty()) { |
+ if (is_hosted_app()) { |
+ href = launch_web_url(); |
+ } else if (is_legacy_packaged_app()) { |
+ href = launch_local_path(); |
+ } |
+ } |
+ |
+ // If there still is not an href, the manifest is malformed, unless this is a |
+ // platform app in which case the href should not be present. |
+ if (href.empty() && !is_platform_app()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidIntentHrefEmpty, action_name); |
return false; |
- if (input.length() == 0) |
+ } else if (!href.empty() && is_platform_app()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidIntentHrefInPlatformApp, action_name); |
return false; |
+ } |
- std::string working = input; |
- if (StartsWithASCII(working, kKeyBeginHeaderMarker, true)) { |
- working = CollapseWhitespaceASCII(working, true); |
- size_t header_pos = working.find(kKeyInfoEndMarker, |
- sizeof(kKeyBeginHeaderMarker) - 1); |
- if (header_pos == std::string::npos) |
- return false; |
- size_t start_pos = header_pos + sizeof(kKeyInfoEndMarker) - 1; |
- size_t end_pos = working.rfind(kKeyBeginFooterMarker); |
- if (end_pos == std::string::npos) |
+ GURL service_url(href); |
+ if (is_hosted_app()) { |
+ // Hosted apps require an absolute URL for intents. |
+ if (!service_url.is_valid() || |
+ !(web_extent().MatchesURL(service_url))) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidIntentPageInHostedApp, action_name); |
return false; |
- if (start_pos >= end_pos) |
+ } |
+ service.service_url = service_url; |
+ } else if (is_platform_app()) { |
+ service.service_url = GetBackgroundURL(); |
+ } else { |
+ // We do not allow absolute intent URLs in non-hosted apps. |
+ if (service_url.is_valid()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kCannotAccessPage, href); |
return false; |
+ } |
+ service.service_url = GetResourceURL(href); |
+ } |
- working = working.substr(start_pos, end_pos - start_pos); |
- if (working.length() == 0) |
+ if (intent_service.HasKey(keys::kIntentTitle) && |
+ !intent_service.GetString(keys::kIntentTitle, &service.title)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIntentTitle); |
+ return false; |
+ } |
+ |
+ if (intent_service.HasKey(keys::kIntentDisposition)) { |
+ if (is_platform_app()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidIntentDispositionInPlatformApp, action_name); |
return false; |
+ } |
+ if (!intent_service.GetString(keys::kIntentDisposition, &value) || |
+ (value != values::kIntentDispositionWindow && |
+ value != values::kIntentDispositionInline)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIntentDisposition); |
+ return false; |
+ } |
+ if (value == values::kIntentDispositionInline) { |
+ service.disposition = |
+ webkit_glue::WebIntentServiceData::DISPOSITION_INLINE; |
+ } else { |
+ service.disposition = |
+ webkit_glue::WebIntentServiceData::DISPOSITION_WINDOW; |
+ } |
} |
- return base::Base64Decode(working, output); |
+ for (size_t i = 0; i < mime_types->GetSize(); ++i) { |
+ if (!mime_types->GetString(i, &service.type)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidIntentTypeElement, action_name, |
+ std::string(base::IntToString(i))); |
+ return false; |
+ } |
+ intents_services_.push_back(service); |
+ } |
+ return true; |
} |
-bool Extension::ProducePEM(const std::string& input, std::string* output) { |
- DCHECK(output); |
- return (input.length() == 0) ? false : base::Base64Encode(input, output); |
-} |
+bool Extension::LoadWebIntentServices(string16* error) { |
+ DCHECK(error); |
-bool Extension::FormatPEMForFileOutput(const std::string& input, |
- std::string* output, |
- bool is_public) { |
- DCHECK(output); |
- if (input.length() == 0) |
+ if (!manifest_->HasKey(keys::kIntents)) |
+ return true; |
+ |
+ DictionaryValue* all_services = NULL; |
+ if (!manifest_->GetDictionary(keys::kIntents, &all_services)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIntents); |
return false; |
- *output = ""; |
- output->append(kKeyBeginHeaderMarker); |
- output->append(" "); |
- output->append(is_public ? kPublic : kPrivate); |
- output->append(" "); |
- output->append(kKeyInfoEndMarker); |
- output->append("\n"); |
- for (size_t i = 0; i < input.length(); ) { |
- int slice = std::min<int>(input.length() - i, kPEMOutputColumns); |
- output->append(input.substr(i, slice)); |
- output->append("\n"); |
- i += slice; |
} |
- output->append(kKeyBeginFooterMarker); |
- output->append(" "); |
- output->append(is_public ? kPublic : kPrivate); |
- output->append(" "); |
- output->append(kKeyInfoEndMarker); |
- output->append("\n"); |
+ for (DictionaryValue::key_iterator iter(all_services->begin_keys()); |
+ iter != all_services->end_keys(); ++iter) { |
+ // Any entry in the intents dictionary can either have a list of |
+ // dictionaries, or just a single dictionary attached to that. Try |
+ // lists first, fall back to single dictionary. |
+ ListValue* service_list = NULL; |
+ DictionaryValue* one_service = NULL; |
+ if (all_services->GetListWithoutPathExpansion(*iter, &service_list)) { |
+ for (size_t i = 0; i < service_list->GetSize(); ++i) { |
+ if (!service_list->GetDictionary(i, &one_service)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIntent); |
+ return false; |
+ } |
+ if (!LoadWebIntentAction(*iter, *one_service, error)) |
+ return false; |
+ } |
+ } else { |
+ if (!all_services->GetDictionaryWithoutPathExpansion(*iter, |
+ &one_service)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIntent); |
+ return false; |
+ } |
+ if (!LoadWebIntentAction(*iter, *one_service, error)) |
+ return false; |
+ } |
+ } |
return true; |
} |
-// static |
-void Extension::DecodeIcon(const Extension* extension, |
- int preferred_icon_size, |
- ExtensionIconSet::MatchType match_type, |
- scoped_ptr<SkBitmap>* result) { |
- std::string path = extension->icons().Get(preferred_icon_size, match_type); |
- int size = extension->icons().GetIconSizeFromPath(path); |
- ExtensionResource icon_resource = extension->GetResource(path); |
- DecodeIconFromPath(icon_resource.GetFilePath(), size, result); |
-} |
- |
-// static |
-void Extension::DecodeIcon(const Extension* extension, |
- int icon_size, |
- scoped_ptr<SkBitmap>* result) { |
- DecodeIcon(extension, icon_size, ExtensionIconSet::MATCH_EXACTLY, result); |
-} |
+bool Extension::LoadFileHandler(const std::string& handler_id, |
+ const DictionaryValue& handler_info, |
+ string16* error) { |
+ DCHECK(error); |
+ DCHECK(is_platform_app()); |
+ FileHandlerInfo handler; |
-// static |
-void Extension::DecodeIconFromPath(const FilePath& icon_path, |
- int icon_size, |
- scoped_ptr<SkBitmap>* result) { |
- if (icon_path.empty()) |
- return; |
+ handler.id = handler_id; |
- std::string file_contents; |
- if (!file_util::ReadFileToString(icon_path, &file_contents)) { |
- DLOG(ERROR) << "Could not read icon file: " << icon_path.LossyDisplayName(); |
- return; |
+ const ListValue* mime_types = NULL; |
+ // TODO(benwells): handle file extensions. |
+ if (!handler_info.HasKey(keys::kFileHandlerTypes) || |
+ !handler_info.GetList(keys::kFileHandlerTypes, &mime_types) || |
+ mime_types->GetSize() == 0) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidFileHandlerType, handler_id); |
+ return false; |
+ } |
+ |
+ if (handler_info.HasKey(keys::kFileHandlerTitle) && |
+ !handler_info.GetString(keys::kFileHandlerTitle, &handler.title)) { |
+ *error = ASCIIToUTF16(errors::kInvalidFileHandlerTitle); |
+ return false; |
+ } |
+ |
+ std::string type; |
+ for (size_t i = 0; i < mime_types->GetSize(); ++i) { |
+ if (!mime_types->GetString(i, &type)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidFileHandlerTypeElement, handler_id, |
+ std::string(base::IntToString(i))); |
+ return false; |
+ } |
+ handler.types.insert(type); |
+ } |
+ |
+ file_handlers_.push_back(handler); |
+ return true; |
+} |
+ |
+bool Extension::LoadFileHandlers(string16* error) { |
+ DCHECK(error); |
+ |
+ if (!manifest_->HasKey(keys::kFileHandlers)) |
+ return true; |
+ |
+ DictionaryValue* all_handlers = NULL; |
+ if (!manifest_->GetDictionary(keys::kFileHandlers, &all_handlers)) { |
+ *error = ASCIIToUTF16(errors::kInvalidFileHandlers); |
+ return false; |
+ } |
+ |
+ for (DictionaryValue::key_iterator iter(all_handlers->begin_keys()); |
+ iter != all_handlers->end_keys(); ++iter) { |
+ // A file handler entry is a title and a list of MIME types to handle. |
+ DictionaryValue* handler = NULL; |
+ if (all_handlers->GetDictionaryWithoutPathExpansion(*iter, &handler)) { |
+ if (!LoadFileHandler(*iter, *handler, error)) |
+ return false; |
+ } else { |
+ *error = ASCIIToUTF16(errors::kInvalidFileHandlers); |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool Extension::LoadExtensionFeatures(APIPermissionSet* api_permissions, |
+ string16* error) { |
+ if (manifest_->HasKey(keys::kConvertedFromUserScript)) |
+ manifest_->GetBoolean(keys::kConvertedFromUserScript, |
+ &converted_from_user_script_); |
+ |
+ if (!LoadDevToolsPage(error) || |
+ !LoadInputComponents(*api_permissions, error) || |
+ !LoadContentScripts(error) || |
+ !LoadPageAction(error) || |
+ !LoadBrowserAction(error) || |
+ !LoadSystemIndicator(api_permissions, error) || |
+ !LoadScriptBadge(error) || |
+ !LoadFileBrowserHandlers(error) || |
+ !LoadChromeURLOverrides(error) || |
+ !LoadOmnibox(error) || |
+ !LoadTextToSpeechVoices(error) || |
+ !LoadIncognitoMode(error) || |
+ !LoadFileHandlers(error) || |
+ !LoadContentSecurityPolicy(error)) |
+ return false; |
+ |
+ return true; |
+} |
+ |
+bool Extension::LoadDevToolsPage(string16* error) { |
+ if (!manifest_->HasKey(keys::kDevToolsPage)) |
+ return true; |
+ std::string devtools_str; |
+ if (!manifest_->GetString(keys::kDevToolsPage, &devtools_str)) { |
+ *error = ASCIIToUTF16(errors::kInvalidDevToolsPage); |
+ return false; |
+ } |
+ devtools_url_ = GetResourceURL(devtools_str); |
+ return true; |
+} |
+ |
+bool Extension::LoadInputComponents(const APIPermissionSet& api_permissions, |
+ string16* error) { |
+ if (!manifest_->HasKey(keys::kInputComponents)) |
+ return true; |
+ ListValue* list_value = NULL; |
+ if (!manifest_->GetList(keys::kInputComponents, &list_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidInputComponents); |
+ return false; |
+ } |
+ |
+ for (size_t i = 0; i < list_value->GetSize(); ++i) { |
+ DictionaryValue* module_value = NULL; |
+ std::string name_str; |
+ InputComponentType type; |
+ std::string id_str; |
+ std::string description_str; |
+ std::string language_str; |
+ std::set<std::string> layouts; |
+ std::string shortcut_keycode_str; |
+ bool shortcut_alt = false; |
+ bool shortcut_ctrl = false; |
+ bool shortcut_shift = false; |
+ |
+ if (!list_value->GetDictionary(i, &module_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidInputComponents); |
+ return false; |
+ } |
+ |
+ // Get input_components[i].name. |
+ if (!module_value->GetString(keys::kName, &name_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidInputComponentName, base::IntToString(i)); |
+ return false; |
+ } |
+ |
+ // Get input_components[i].type. |
+ std::string type_str; |
+ if (module_value->GetString(keys::kType, &type_str)) { |
+ if (type_str == "ime") { |
+ type = INPUT_COMPONENT_TYPE_IME; |
+ } else { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidInputComponentType, base::IntToString(i)); |
+ return false; |
+ } |
+ } else { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidInputComponentType, base::IntToString(i)); |
+ return false; |
+ } |
+ |
+ // Get input_components[i].id. |
+ if (!module_value->GetString(keys::kId, &id_str)) { |
+ id_str = ""; |
+ } |
+ |
+ // Get input_components[i].description. |
+ if (!module_value->GetString(keys::kDescription, &description_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidInputComponentDescription, base::IntToString(i)); |
+ return false; |
+ } |
+ // Get input_components[i].language. |
+ if (!module_value->GetString(keys::kLanguage, &language_str)) { |
+ language_str = ""; |
+ } |
+ |
+ // Get input_components[i].layouts. |
+ ListValue* layouts_value = NULL; |
+ if (!module_value->GetList(keys::kLayouts, &layouts_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidInputComponentLayouts); |
+ return false; |
+ } |
+ |
+ for (size_t j = 0; j < layouts_value->GetSize(); ++j) { |
+ std::string layout_name_str; |
+ if (!layouts_value->GetString(j, &layout_name_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidInputComponentLayoutName, base::IntToString(i), |
+ base::IntToString(j)); |
+ return false; |
+ } |
+ layouts.insert(layout_name_str); |
+ } |
+ |
+ if (module_value->HasKey(keys::kShortcutKey)) { |
+ DictionaryValue* shortcut_value = NULL; |
+ if (!module_value->GetDictionary(keys::kShortcutKey, &shortcut_value)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidInputComponentShortcutKey, base::IntToString(i)); |
+ return false; |
+ } |
+ |
+ // Get input_components[i].shortcut_keycode. |
+ if (!shortcut_value->GetString(keys::kKeycode, &shortcut_keycode_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidInputComponentShortcutKeycode, |
+ base::IntToString(i)); |
+ return false; |
+ } |
+ |
+ // Get input_components[i].shortcut_alt. |
+ if (!shortcut_value->GetBoolean(keys::kAltKey, &shortcut_alt)) { |
+ shortcut_alt = false; |
+ } |
+ |
+ // Get input_components[i].shortcut_ctrl. |
+ if (!shortcut_value->GetBoolean(keys::kCtrlKey, &shortcut_ctrl)) { |
+ shortcut_ctrl = false; |
+ } |
+ |
+ // Get input_components[i].shortcut_shift. |
+ if (!shortcut_value->GetBoolean(keys::kShiftKey, &shortcut_shift)) { |
+ shortcut_shift = false; |
+ } |
+ } |
+ |
+ input_components_.push_back(InputComponentInfo()); |
+ input_components_.back().name = name_str; |
+ input_components_.back().type = type; |
+ input_components_.back().id = id_str; |
+ input_components_.back().description = description_str; |
+ input_components_.back().language = language_str; |
+ input_components_.back().layouts.insert(layouts.begin(), layouts.end()); |
+ input_components_.back().shortcut_keycode = shortcut_keycode_str; |
+ input_components_.back().shortcut_alt = shortcut_alt; |
+ input_components_.back().shortcut_ctrl = shortcut_ctrl; |
+ input_components_.back().shortcut_shift = shortcut_shift; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool Extension::LoadContentScripts(string16* error) { |
+ if (!manifest_->HasKey(keys::kContentScripts)) |
+ return true; |
+ ListValue* list_value; |
+ if (!manifest_->GetList(keys::kContentScripts, &list_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidContentScriptsList); |
+ return false; |
+ } |
+ |
+ for (size_t i = 0; i < list_value->GetSize(); ++i) { |
+ DictionaryValue* content_script = NULL; |
+ if (!list_value->GetDictionary(i, &content_script)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidContentScript, base::IntToString(i)); |
+ return false; |
+ } |
+ |
+ UserScript script; |
+ if (!LoadUserScriptHelper(content_script, i, error, &script)) |
+ return false; // Failed to parse script context definition. |
+ script.set_extension_id(id()); |
+ if (converted_from_user_script_) { |
+ script.set_emulate_greasemonkey(true); |
+ script.set_match_all_frames(true); // Greasemonkey matches all frames. |
+ } |
+ content_scripts_.push_back(script); |
+ } |
+ return true; |
+} |
+ |
+bool Extension::LoadPageAction(string16* error) { |
+ DictionaryValue* page_action_value = NULL; |
+ |
+ if (manifest_->HasKey(keys::kPageActions)) { |
+ ListValue* list_value = NULL; |
+ if (!manifest_->GetList(keys::kPageActions, &list_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionsList); |
+ return false; |
+ } |
+ |
+ size_t list_value_length = list_value->GetSize(); |
+ |
+ if (list_value_length == 0u) { |
+ // A list with zero items is allowed, and is equivalent to not having |
+ // a page_actions key in the manifest. Don't set |page_action_value|. |
+ } else if (list_value_length == 1u) { |
+ if (!list_value->GetDictionary(0, &page_action_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageAction); |
+ return false; |
+ } |
+ } else { // list_value_length > 1u. |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionsListSize); |
+ return false; |
+ } |
+ } else if (manifest_->HasKey(keys::kPageAction)) { |
+ if (!manifest_->GetDictionary(keys::kPageAction, &page_action_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageAction); |
+ return false; |
+ } |
+ } |
+ |
+ // If page_action_value is not NULL, then there was a valid page action. |
+ if (page_action_value) { |
+ page_action_info_ = LoadExtensionActionInfoHelper( |
+ page_action_value, Extension::ActionInfo::TYPE_PAGE, error); |
+ if (!page_action_info_.get()) |
+ return false; // Failed to parse page action definition. |
+ } |
+ |
+ return true; |
+} |
+ |
+bool Extension::LoadBrowserAction(string16* error) { |
+ if (!manifest_->HasKey(keys::kBrowserAction)) |
+ return true; |
+ DictionaryValue* browser_action_value = NULL; |
+ if (!manifest_->GetDictionary(keys::kBrowserAction, &browser_action_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidBrowserAction); |
+ return false; |
+ } |
+ |
+ browser_action_info_ = LoadExtensionActionInfoHelper( |
+ browser_action_value, Extension::ActionInfo::TYPE_BROWSER, error); |
+ if (!browser_action_info_.get()) |
+ return false; // Failed to parse browser action definition. |
+ return true; |
+} |
+ |
+bool Extension::LoadScriptBadge(string16* error) { |
+ if (manifest_->HasKey(keys::kScriptBadge)) { |
+ if (!FeatureSwitch::script_badges()->IsEnabled()) { |
+ // So as to not confuse developers if they specify a script badge section |
+ // in the manifest, show a warning if the script badge declaration isn't |
+ // going to have any effect. |
+ install_warnings_.push_back( |
+ InstallWarning(InstallWarning::FORMAT_TEXT, |
+ errors::kScriptBadgeRequiresFlag)); |
+ } |
+ |
+ DictionaryValue* script_badge_value = NULL; |
+ if (!manifest_->GetDictionary(keys::kScriptBadge, &script_badge_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidScriptBadge); |
+ return false; |
+ } |
+ |
+ script_badge_info_ = LoadExtensionActionInfoHelper( |
+ script_badge_value, Extension::ActionInfo::TYPE_SCRIPT_BADGE, error); |
+ if (!script_badge_info_.get()) |
+ return false; // Failed to parse script badge definition. |
+ } else { |
+ script_badge_info_.reset(new ActionInfo()); |
+ } |
+ |
+ // Script badges always use their extension's title and icon so users can rely |
+ // on the visual appearance to know which extension is running. This isn't |
+ // bulletproof since an malicious extension could use a different 16x16 icon |
+ // that matches the icon of a trusted extension, and users wouldn't be warned |
+ // during installation. |
+ |
+ if (!script_badge_info_->default_title.empty()) { |
+ install_warnings_.push_back( |
+ InstallWarning(InstallWarning::FORMAT_TEXT, |
+ errors::kScriptBadgeTitleIgnored)); |
+ } |
+ script_badge_info_->default_title = name(); |
+ |
+ if (!script_badge_info_->default_icon.empty()) { |
+ install_warnings_.push_back( |
+ InstallWarning(InstallWarning::FORMAT_TEXT, |
+ errors::kScriptBadgeIconIgnored)); |
+ } |
+ |
+ script_badge_info_->default_icon.Clear(); |
+ for (size_t i = 0; i < extension_misc::kNumScriptBadgeIconSizes; i++) { |
+ std::string path = icons().Get(extension_misc::kScriptBadgeIconSizes[i], |
+ ExtensionIconSet::MATCH_BIGGER); |
+ if (!path.empty()) |
+ script_badge_info_->default_icon.Add( |
+ extension_misc::kScriptBadgeIconSizes[i], path); |
+ } |
+ |
+ return true; |
+} |
+ |
+bool Extension::LoadSystemIndicator(APIPermissionSet* api_permissions, |
+ string16* error) { |
+ if (!manifest_->HasKey(keys::kSystemIndicator)) { |
+ // There was no manifest entry for the system indicator. |
+ return true; |
} |
- // Decode the image using WebKit's image decoder. |
- const unsigned char* data = |
- reinterpret_cast<const unsigned char*>(file_contents.data()); |
- webkit_glue::ImageDecoder decoder; |
- scoped_ptr<SkBitmap> decoded(new SkBitmap()); |
- *decoded = decoder.Decode(data, file_contents.length()); |
- if (decoded->empty()) { |
- DLOG(ERROR) << "Could not decode icon file: " |
- << icon_path.LossyDisplayName(); |
- return; |
+ DictionaryValue* system_indicator_value = NULL; |
+ if (!manifest_->GetDictionary(keys::kSystemIndicator, |
+ &system_indicator_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidSystemIndicator); |
+ return false; |
} |
- if (decoded->width() != icon_size || decoded->height() != icon_size) { |
- DLOG(ERROR) << "Icon file has unexpected size: " |
- << base::IntToString(decoded->width()) << "x" |
- << base::IntToString(decoded->height()); |
- return; |
+ system_indicator_info_ = LoadExtensionActionInfoHelper( |
+ system_indicator_value, |
+ Extension::ActionInfo::TYPE_SYSTEM_INDICATOR, |
+ error); |
+ |
+ if (!system_indicator_info_.get()) { |
+ return false; |
} |
- result->swap(decoded); |
+ // Because the manifest was successfully parsed, auto-grant the permission. |
+ // TODO(dewittj) Add this for all extension action APIs. |
+ api_permissions->insert(APIPermission::kSystemIndicator); |
+ |
+ return true; |
} |
-// static |
-const gfx::ImageSkia& Extension::GetDefaultIcon(bool is_app) { |
- int id = is_app ? IDR_APP_DEFAULT_ICON : IDR_EXTENSION_DEFAULT_ICON; |
- return *ResourceBundle::GetSharedInstance().GetImageSkiaNamed(id); |
+bool Extension::LoadFileBrowserHandlers(string16* error) { |
+ if (!manifest_->HasKey(keys::kFileBrowserHandlers)) |
+ return true; |
+ ListValue* file_browser_handlers_value = NULL; |
+ if (!manifest_->GetList(keys::kFileBrowserHandlers, |
+ &file_browser_handlers_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); |
+ return false; |
+ } |
+ file_browser_handlers_.reset( |
+ LoadFileBrowserHandlersHelper(file_browser_handlers_value, error)); |
+ if (!file_browser_handlers_.get()) |
+ return false; // Failed to parse file browser actions definition. |
+ return true; |
} |
-// static |
-GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { |
- return GURL(std::string(extensions::kExtensionScheme) + |
- content::kStandardSchemeSeparator + extension_id + "/"); |
+Extension::FileBrowserHandlerList* Extension::LoadFileBrowserHandlersHelper( |
+ const ListValue* extension_actions, string16* error) { |
+ scoped_ptr<FileBrowserHandlerList> result( |
+ new FileBrowserHandlerList()); |
+ for (ListValue::const_iterator iter = extension_actions->begin(); |
+ iter != extension_actions->end(); |
+ ++iter) { |
+ if (!(*iter)->IsType(Value::TYPE_DICTIONARY)) { |
+ *error = ASCIIToUTF16(errors::kInvalidFileBrowserHandler); |
+ return NULL; |
+ } |
+ scoped_ptr<FileBrowserHandler> action( |
+ LoadFileBrowserHandler( |
+ reinterpret_cast<DictionaryValue*>(*iter), error)); |
+ if (!action.get()) |
+ return NULL; // Failed to parse file browser action definition. |
+ result->push_back(linked_ptr<FileBrowserHandler>(action.release())); |
+ } |
+ return result.release(); |
} |
-bool Extension::InitFromValue(int flags, string16* error) { |
- DCHECK(error); |
+FileBrowserHandler* Extension::LoadFileBrowserHandler( |
+ const DictionaryValue* file_browser_handler, string16* error) { |
+ scoped_ptr<FileBrowserHandler> result(new FileBrowserHandler()); |
+ result->set_extension_id(id()); |
- base::AutoLock auto_lock(runtime_data_lock_); |
+ std::string id; |
+ // Read the file action |id| (mandatory). |
+ if (!file_browser_handler->HasKey(keys::kPageActionId) || |
+ !file_browser_handler->GetString(keys::kPageActionId, &id)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionId); |
+ return NULL; |
+ } |
+ result->set_id(id); |
- // Initialize permissions with an empty, default permission set. |
- runtime_data_.SetActivePermissions(new PermissionSet()); |
- optional_permission_set_ = new PermissionSet(); |
- required_permission_set_ = new PermissionSet(); |
+ // Read the page action title from |default_title| (mandatory). |
+ std::string title; |
+ if (!file_browser_handler->HasKey(keys::kPageActionDefaultTitle) || |
+ !file_browser_handler->GetString(keys::kPageActionDefaultTitle, &title)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); |
+ return NULL; |
+ } |
+ result->set_title(title); |
- creation_flags_ = flags; |
+ // Initialize access permissions (optional). |
+ const ListValue* access_list_value = NULL; |
+ if (file_browser_handler->HasKey(keys::kFileAccessList)) { |
+ if (!file_browser_handler->GetList(keys::kFileAccessList, |
+ &access_list_value) || |
+ access_list_value->empty()) { |
+ *error = ASCIIToUTF16(errors::kInvalidFileAccessList); |
+ return NULL; |
+ } |
+ for (size_t i = 0; i < access_list_value->GetSize(); ++i) { |
+ std::string access; |
+ if (!access_list_value->GetString(i, &access) || |
+ result->AddFileAccessPermission(access)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidFileAccessValue, base::IntToString(i)); |
+ return NULL; |
+ } |
+ } |
+ } |
+ if (!result->ValidateFileAccessPermissions()) { |
+ *error = ASCIIToUTF16(errors::kInvalidFileAccessList); |
+ return NULL; |
+ } |
- // Important to load manifest version first because many other features |
- // depend on its value. |
- if (!LoadManifestVersion(error)) |
- return false; |
+ // Initialize file filters (mandatory, unless "create" access is specified, |
+ // in which case is ignored). |
+ if (!result->HasCreateAccessPermission()) { |
+ const ListValue* list_value = NULL; |
+ if (!file_browser_handler->HasKey(keys::kFileFilters) || |
+ !file_browser_handler->GetList(keys::kFileFilters, &list_value) || |
+ list_value->empty()) { |
+ *error = ASCIIToUTF16(errors::kInvalidFileFiltersList); |
+ return NULL; |
+ } |
+ for (size_t i = 0; i < list_value->GetSize(); ++i) { |
+ std::string filter; |
+ if (!list_value->GetString(i, &filter)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidFileFilterValue, base::IntToString(i)); |
+ return NULL; |
+ } |
+ StringToLowerASCII(&filter); |
+ if (!StartsWithASCII(filter, |
+ std::string(chrome::kFileSystemScheme) + ':', |
+ true)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidURLPatternError, filter); |
+ return NULL; |
+ } |
+ // The user inputs filesystem:*; we don't actually implement scheme |
+ // wildcards in URLPattern, so transform to what will match correctly. |
+ filter.replace(0, 11, "chrome-extension://*/"); |
+ URLPattern pattern(URLPattern::SCHEME_EXTENSION); |
+ if (pattern.Parse(filter) != URLPattern::PARSE_SUCCESS) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidURLPatternError, filter); |
+ return NULL; |
+ } |
+ std::string path = pattern.path(); |
+ bool allowed = path == "/*" || path == "/*.*" || |
+ (path.compare(0, 3, "/*.") == 0 && |
+ path.find_first_of('*', 3) == std::string::npos); |
+ if (!allowed) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidURLPatternError, filter); |
+ return NULL; |
+ } |
+ result->AddPattern(pattern); |
+ } |
+ } |
- // Validate minimum Chrome version. We don't need to store this, since the |
- // extension is not valid if it is incorrect |
- if (!CheckMinimumChromeVersion(error)) |
- return false; |
+ std::string default_icon; |
+ // Read the file browser action |default_icon| (optional). |
+ if (file_browser_handler->HasKey(keys::kPageActionDefaultIcon)) { |
+ if (!file_browser_handler->GetString( |
+ keys::kPageActionDefaultIcon, &default_icon) || |
+ default_icon.empty()) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
+ return NULL; |
+ } |
+ result->set_icon_path(default_icon); |
+ } |
- if (!LoadRequiredFeatures(error)) |
- return false; |
+ return result.release(); |
+} |
- // We don't need to validate because InitExtensionID already did that. |
- manifest_->GetString(keys::kPublicKey, &public_key_); |
+bool Extension::LoadChromeURLOverrides(string16* error) { |
+ if (!manifest_->HasKey(keys::kChromeURLOverrides)) |
+ return true; |
+ DictionaryValue* overrides = NULL; |
+ if (!manifest_->GetDictionary(keys::kChromeURLOverrides, &overrides)) { |
+ *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); |
+ return false; |
+ } |
- extension_url_ = Extension::GetBaseURLFromExtensionId(id()); |
+ // Validate that the overrides are all strings |
+ for (DictionaryValue::key_iterator iter = overrides->begin_keys(); |
+ iter != overrides->end_keys(); ++iter) { |
+ std::string page = *iter; |
+ std::string val; |
+ // Restrict override pages to a list of supported URLs. |
+ bool is_override = (page != chrome::kChromeUINewTabHost && |
+ page != chrome::kChromeUIBookmarksHost && |
+ page != chrome::kChromeUIHistoryHost); |
+#if defined(OS_CHROMEOS) |
+ is_override = (is_override && |
+ page != chrome::kChromeUIActivationMessageHost); |
+#endif |
+#if defined(FILE_MANAGER_EXTENSION) |
+ is_override = (is_override && |
+ !(location() == COMPONENT && |
+ page == chrome::kChromeUIFileManagerHost)); |
+#endif |
- // Load App settings. LoadExtent at least has to be done before |
- // ParsePermissions(), because the valid permissions depend on what type of |
- // package this is. |
- if (is_app() && !LoadAppFeatures(error)) |
- return false; |
+ if (is_override || !overrides->GetStringWithoutPathExpansion(*iter, &val)) { |
+ *error = ASCIIToUTF16(errors::kInvalidChromeURLOverrides); |
+ return false; |
+ } |
+ // Replace the entry with a fully qualified chrome-extension:// URL. |
+ chrome_url_overrides_[page] = GetResourceURL(val); |
- APIPermissionSet api_permissions; |
- URLPatternSet host_permissions; |
- if (!ParsePermissions(keys::kPermissions, |
- error, |
- &api_permissions, |
- &host_permissions)) { |
- return false; |
+ // For component extensions, add override URL to extent patterns. |
+ if (is_legacy_packaged_app() && location() == COMPONENT) { |
+ URLPattern pattern(URLPattern::SCHEME_CHROMEUI); |
+ std::string url = base::StringPrintf(kOverrideExtentUrlPatternFormat, |
+ page.c_str()); |
+ if (pattern.Parse(url) != URLPattern::PARSE_SUCCESS) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidURLPatternError, url); |
+ return false; |
+ } |
+ extent_.AddPattern(pattern); |
+ } |
} |
- // TODO(jeremya/kalman) do this via the features system by exposing the |
- // app.window API to platform apps, with no dependency on any permissions. |
- // See http://crbug.com/120069. |
- if (is_platform_app()) { |
- api_permissions.insert(APIPermission::kAppCurrentWindowInternal); |
- api_permissions.insert(APIPermission::kAppRuntime); |
- api_permissions.insert(APIPermission::kAppWindow); |
+ // An extension may override at most one page. |
+ if (overrides->size() > 1) { |
+ *error = ASCIIToUTF16(errors::kMultipleOverrides); |
+ return false; |
} |
- if (from_webstore()) { |
- details_url_ = |
- GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()); |
- } |
+ return true; |
+} |
- APIPermissionSet optional_api_permissions; |
- URLPatternSet optional_host_permissions; |
- if (!ParsePermissions(keys::kOptionalPermissions, |
- error, |
- &optional_api_permissions, |
- &optional_host_permissions)) { |
+bool Extension::LoadOmnibox(string16* error) { |
+ if (!manifest_->HasKey(keys::kOmnibox)) |
+ return true; |
+ if (!manifest_->GetString(keys::kOmniboxKeyword, &omnibox_keyword_) || |
+ omnibox_keyword_.empty()) { |
+ *error = ASCIIToUTF16(errors::kInvalidOmniboxKeyword); |
return false; |
} |
+ return true; |
+} |
- if (!LoadAppIsolation(api_permissions, error)) |
+bool Extension::LoadTextToSpeechVoices(string16* error) { |
+ if (!manifest_->HasKey(keys::kTtsEngine)) |
+ return true; |
+ DictionaryValue* tts_dict = NULL; |
+ if (!manifest_->GetDictionary(keys::kTtsEngine, &tts_dict)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTts); |
return false; |
+ } |
- if (!LoadSharedFeatures(api_permissions, error)) |
- return false; |
+ if (tts_dict->HasKey(keys::kTtsVoices)) { |
+ ListValue* tts_voices = NULL; |
+ if (!tts_dict->GetList(keys::kTtsVoices, &tts_voices)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoices); |
+ return false; |
+ } |
- if (!LoadExtensionFeatures(&api_permissions, error)) |
- return false; |
+ for (size_t i = 0; i < tts_voices->GetSize(); i++) { |
+ DictionaryValue* one_tts_voice = NULL; |
+ if (!tts_voices->GetDictionary(i, &one_tts_voice)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoices); |
+ return false; |
+ } |
- if (!LoadThemeFeatures(error)) |
- return false; |
+ TtsVoice voice_data; |
+ if (one_tts_voice->HasKey(keys::kTtsVoicesVoiceName)) { |
+ if (!one_tts_voice->GetString( |
+ keys::kTtsVoicesVoiceName, &voice_data.voice_name)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoicesVoiceName); |
+ return false; |
+ } |
+ } |
+ if (one_tts_voice->HasKey(keys::kTtsVoicesLang)) { |
+ if (!one_tts_voice->GetString( |
+ keys::kTtsVoicesLang, &voice_data.lang) || |
+ !l10n_util::IsValidLocaleSyntax(voice_data.lang)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoicesLang); |
+ return false; |
+ } |
+ } |
+ if (one_tts_voice->HasKey(keys::kTtsVoicesGender)) { |
+ if (!one_tts_voice->GetString( |
+ keys::kTtsVoicesGender, &voice_data.gender) || |
+ (voice_data.gender != keys::kTtsGenderMale && |
+ voice_data.gender != keys::kTtsGenderFemale)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoicesGender); |
+ return false; |
+ } |
+ } |
+ if (one_tts_voice->HasKey(keys::kTtsVoicesEventTypes)) { |
+ ListValue* event_types_list; |
+ if (!one_tts_voice->GetList( |
+ keys::kTtsVoicesEventTypes, &event_types_list)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
+ return false; |
+ } |
+ for (size_t i = 0; i < event_types_list->GetSize(); i++) { |
+ std::string event_type; |
+ if (!event_types_list->GetString(i, &event_type)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
+ return false; |
+ } |
+ if (event_type != keys::kTtsVoicesEventTypeEnd && |
+ event_type != keys::kTtsVoicesEventTypeError && |
+ event_type != keys::kTtsVoicesEventTypeMarker && |
+ event_type != keys::kTtsVoicesEventTypeSentence && |
+ event_type != keys::kTtsVoicesEventTypeStart && |
+ event_type != keys::kTtsVoicesEventTypeWord) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
+ return false; |
+ } |
+ if (voice_data.event_types.find(event_type) != |
+ voice_data.event_types.end()) { |
+ *error = ASCIIToUTF16(errors::kInvalidTtsVoicesEventTypes); |
+ return false; |
+ } |
+ voice_data.event_types.insert(event_type); |
+ } |
+ } |
- if (HasMultipleUISurfaces()) { |
- *error = ASCIIToUTF16(errors::kOneUISurfaceOnly); |
+ tts_voices_.push_back(voice_data); |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool Extension::LoadIncognitoMode(string16* error) { |
+ // Apps default to split mode, extensions default to spanning. |
+ incognito_split_mode_ = is_app(); |
+ if (!manifest_->HasKey(keys::kIncognito)) |
+ return true; |
+ std::string value; |
+ if (!manifest_->GetString(keys::kIncognito, &value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); |
+ return false; |
+ } |
+ if (value == values::kIncognitoSpanning) { |
+ incognito_split_mode_ = false; |
+ } else if (value == values::kIncognitoSplit) { |
+ incognito_split_mode_ = true; |
+ } else { |
+ *error = ASCIIToUTF16(errors::kInvalidIncognitoBehavior); |
return false; |
} |
+ return true; |
+} |
- runtime_data_.SetActivePermissions(new PermissionSet( |
- this, api_permissions, host_permissions)); |
- required_permission_set_ = new PermissionSet( |
- this, api_permissions, host_permissions); |
- optional_permission_set_ = new PermissionSet( |
- optional_api_permissions, optional_host_permissions, URLPatternSet()); |
+bool Extension::LoadContentSecurityPolicy(string16* error) { |
+ const std::string& key = is_platform_app() ? |
+ keys::kPlatformAppContentSecurityPolicy : keys::kContentSecurityPolicy; |
+ |
+ if (manifest_->HasPath(key)) { |
+ std::string content_security_policy; |
+ if (!manifest_->GetString(key, &content_security_policy)) { |
+ *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); |
+ return false; |
+ } |
+ if (!ContentSecurityPolicyIsLegal(content_security_policy)) { |
+ *error = ASCIIToUTF16(errors::kInvalidContentSecurityPolicy); |
+ return false; |
+ } |
+ if (manifest_version_ >= 2 && |
+ !ContentSecurityPolicyIsSecure(content_security_policy, GetType())) { |
+ *error = ASCIIToUTF16(errors::kInsecureContentSecurityPolicy); |
+ return false; |
+ } |
+ content_security_policy_ = content_security_policy; |
+ } else if (manifest_version_ >= 2) { |
+ // Manifest version 2 introduced a default Content-Security-Policy. |
+ // TODO(abarth): Should we continue to let extensions override the |
+ // default Content-Security-Policy? |
+ content_security_policy_ = is_platform_app() ? |
+ kDefaultPlatformAppContentSecurityPolicy : |
+ kDefaultContentSecurityPolicy; |
+ CHECK(ContentSecurityPolicyIsSecure(content_security_policy_, GetType())); |
+ } |
return true; |
} |
-GURL Extension::GetHomepageURL() const { |
- if (homepage_url_.is_valid()) |
- return homepage_url_; |
+bool Extension::LoadThemeFeatures(string16* error) { |
+ if (!manifest_->HasKey(keys::kTheme)) |
+ return true; |
+ DictionaryValue* theme_value = NULL; |
+ if (!manifest_->GetDictionary(keys::kTheme, &theme_value)) { |
+ *error = ASCIIToUTF16(errors::kInvalidTheme); |
+ return false; |
+ } |
+ if (!LoadThemeImages(theme_value, error)) |
+ return false; |
+ if (!LoadThemeColors(theme_value, error)) |
+ return false; |
+ if (!LoadThemeTints(theme_value, error)) |
+ return false; |
+ if (!LoadThemeDisplayProperties(theme_value, error)) |
+ return false; |
- return UpdatesFromGallery() ? |
- GURL(extension_urls::GetWebstoreItemDetailURLPrefix() + id()) : GURL(); |
+ return true; |
} |
-std::set<FilePath> Extension::GetBrowserImages() const { |
- std::set<FilePath> image_paths; |
- // TODO(viettrungluu): These |FilePath::FromWStringHack(UTF8ToWide())| |
- // indicate that we're doing something wrong. |
- |
- // Extension icons. |
- for (ExtensionIconSet::IconMap::const_iterator iter = icons().map().begin(); |
- iter != icons().map().end(); ++iter) { |
- image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
+bool Extension::LoadThemeImages(const DictionaryValue* theme_value, |
+ string16* error) { |
+ const DictionaryValue* images_value = NULL; |
+ if (theme_value->GetDictionary(keys::kThemeImages, &images_value)) { |
+ // Validate that the images are all strings |
+ for (DictionaryValue::key_iterator iter = images_value->begin_keys(); |
+ iter != images_value->end_keys(); ++iter) { |
+ std::string val; |
+ if (!images_value->GetString(*iter, &val)) { |
+ *error = ASCIIToUTF16(errors::kInvalidThemeImages); |
+ return false; |
+ } |
+ } |
+ theme_images_.reset(images_value->DeepCopy()); |
} |
+ return true; |
+} |
- // Theme images. |
- DictionaryValue* theme_images = GetThemeImages(); |
- if (theme_images) { |
- for (DictionaryValue::key_iterator it = theme_images->begin_keys(); |
- it != theme_images->end_keys(); ++it) { |
- std::string val; |
- if (theme_images->GetStringWithoutPathExpansion(*it, &val)) |
- image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(val))); |
+bool Extension::LoadThemeColors(const DictionaryValue* theme_value, |
+ string16* error) { |
+ const DictionaryValue* colors_value = NULL; |
+ if (theme_value->GetDictionary(keys::kThemeColors, &colors_value)) { |
+ // Validate that the colors are RGB or RGBA lists |
+ for (DictionaryValue::key_iterator iter = colors_value->begin_keys(); |
+ iter != colors_value->end_keys(); ++iter) { |
+ const ListValue* color_list = NULL; |
+ double alpha = 0.0; |
+ int color = 0; |
+ // The color must be a list |
+ if (!colors_value->GetListWithoutPathExpansion(*iter, &color_list) || |
+ // And either 3 items (RGB) or 4 (RGBA) |
+ ((color_list->GetSize() != 3) && |
+ ((color_list->GetSize() != 4) || |
+ // For RGBA, the fourth item must be a real or int alpha value. |
+ // Note that GetDouble() can get an integer value. |
+ !color_list->GetDouble(3, &alpha))) || |
+ // For both RGB and RGBA, the first three items must be ints (R,G,B) |
+ !color_list->GetInteger(0, &color) || |
+ !color_list->GetInteger(1, &color) || |
+ !color_list->GetInteger(2, &color)) { |
+ *error = ASCIIToUTF16(errors::kInvalidThemeColors); |
+ return false; |
+ } |
} |
+ theme_colors_.reset(colors_value->DeepCopy()); |
} |
+ return true; |
+} |
- if (page_action_info() && !page_action_info()->default_icon.empty()) { |
- for (ExtensionIconSet::IconMap::const_iterator iter = |
- page_action_info()->default_icon.map().begin(); |
- iter != page_action_info()->default_icon.map().end(); |
- ++iter) { |
- image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
- } |
- } |
+bool Extension::LoadThemeTints(const DictionaryValue* theme_value, |
+ string16* error) { |
+ const DictionaryValue* tints_value = NULL; |
+ if (!theme_value->GetDictionary(keys::kThemeTints, &tints_value)) |
+ return true; |
- if (browser_action_info() && !browser_action_info()->default_icon.empty()) { |
- for (ExtensionIconSet::IconMap::const_iterator iter = |
- browser_action_info()->default_icon.map().begin(); |
- iter != browser_action_info()->default_icon.map().end(); |
- ++iter) { |
- image_paths.insert(FilePath::FromWStringHack(UTF8ToWide(iter->second))); |
+ // Validate that the tints are all reals. |
+ for (DictionaryValue::key_iterator iter = tints_value->begin_keys(); |
+ iter != tints_value->end_keys(); ++iter) { |
+ const ListValue* tint_list = NULL; |
+ double v = 0.0; |
+ if (!tints_value->GetListWithoutPathExpansion(*iter, &tint_list) || |
+ tint_list->GetSize() != 3 || |
+ !tint_list->GetDouble(0, &v) || |
+ !tint_list->GetDouble(1, &v) || |
+ !tint_list->GetDouble(2, &v)) { |
+ *error = ASCIIToUTF16(errors::kInvalidThemeTints); |
+ return false; |
} |
} |
- |
- return image_paths; |
-} |
- |
-GURL Extension::GetFullLaunchURL() const { |
- return launch_local_path().empty() ? GURL(launch_web_url()) : |
- url().Resolve(launch_local_path()); |
-} |
- |
-static std::string SizeToString(const gfx::Size& max_size) { |
- return base::IntToString(max_size.width()) + "x" + |
- base::IntToString(max_size.height()); |
+ theme_tints_.reset(tints_value->DeepCopy()); |
+ return true; |
} |
-// static |
-void Extension::SetScriptingWhitelist( |
- const Extension::ScriptingWhitelist& whitelist) { |
- ScriptingWhitelist* current_whitelist = |
- ExtensionConfig::GetInstance()->whitelist(); |
- current_whitelist->clear(); |
- for (ScriptingWhitelist::const_iterator it = whitelist.begin(); |
- it != whitelist.end(); ++it) { |
- current_whitelist->push_back(*it); |
+bool Extension::LoadThemeDisplayProperties(const DictionaryValue* theme_value, |
+ string16* error) { |
+ const DictionaryValue* display_properties_value = NULL; |
+ if (theme_value->GetDictionary(keys::kThemeDisplayProperties, |
+ &display_properties_value)) { |
+ theme_display_properties_.reset( |
+ display_properties_value->DeepCopy()); |
} |
+ return true; |
} |
- |
-// static |
-const Extension::ScriptingWhitelist* Extension::GetScriptingWhitelist() { |
- return ExtensionConfig::GetInstance()->whitelist(); |
-} |
- |
-void Extension::SetCachedImage(const ExtensionResource& source, |
- const SkBitmap& image, |
- const gfx::Size& original_size) const { |
- DCHECK(source.extension_root() == path()); // The resource must come from |
- // this extension. |
- const FilePath& path = source.relative_path(); |
- gfx::Size actual_size(image.width(), image.height()); |
- std::string location; |
- if (actual_size != original_size) |
- location = SizeToString(actual_size); |
- image_cache_[ImageCacheKey(path, location)] = image; |
-} |
- |
-bool Extension::HasCachedImage(const ExtensionResource& source, |
- const gfx::Size& max_size) const { |
- DCHECK(source.extension_root() == path()); // The resource must come from |
- // this extension. |
- return GetCachedImageImpl(source, max_size) != NULL; |
-} |
- |
-SkBitmap Extension::GetCachedImage(const ExtensionResource& source, |
- const gfx::Size& max_size) const { |
- DCHECK(source.extension_root() == path()); // The resource must come from |
- // this extension. |
- SkBitmap* image = GetCachedImageImpl(source, max_size); |
- return image ? *image : SkBitmap(); |
-} |
- |
SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, |
const gfx::Size& max_size) const { |
const FilePath& path = source.relative_path(); |
@@ -3447,568 +3546,487 @@ SkBitmap* Extension::GetCachedImageImpl(const ExtensionResource& source, |
return NULL; |
} |
-ExtensionResource Extension::GetIconResource( |
- int size, ExtensionIconSet::MatchType match_type) const { |
- std::string path = icons().Get(size, match_type); |
- return path.empty() ? ExtensionResource() : GetResource(path); |
-} |
- |
-GURL Extension::GetIconURL(int size, |
- ExtensionIconSet::MatchType match_type) const { |
- std::string path = icons().Get(size, match_type); |
- return path.empty() ? GURL() : GetResourceURL(path); |
-} |
- |
-bool Extension::ParsePermissions(const char* key, |
- string16* error, |
- APIPermissionSet* api_permissions, |
- URLPatternSet* host_permissions) { |
- if (manifest_->HasKey(key)) { |
- ListValue* permissions = NULL; |
- if (!manifest_->GetList(key, &permissions)) { |
+// Helper method that loads a UserScript object from a dictionary in the |
+// content_script list of the manifest. |
+bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, |
+ int definition_index, |
+ string16* error, |
+ UserScript* result) { |
+ // run_at |
+ if (content_script->HasKey(keys::kRunAt)) { |
+ std::string run_location; |
+ if (!content_script->GetString(keys::kRunAt, &run_location)) { |
*error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidPermissions, ""); |
- return false; |
- } |
- |
- // NOTE: We need to get the APIPermission before we check if features |
- // associated with them are available because the feature system does not |
- // know about aliases. |
- |
- std::vector<std::string> host_data; |
- if (!APIPermissionSet::ParseFromJSON(permissions, api_permissions, |
- error, &host_data)) |
+ errors::kInvalidRunAt, |
+ base::IntToString(definition_index)); |
return false; |
- |
- // Verify feature availability of permissions. |
- std::vector<APIPermission::ID> to_remove; |
- SimpleFeatureProvider* permission_features = |
- SimpleFeatureProvider::GetPermissionFeatures(); |
- for (APIPermissionSet::const_iterator it = api_permissions->begin(); |
- it != api_permissions->end(); ++it) { |
- extensions::Feature* feature = |
- permission_features->GetFeature(it->name()); |
- |
- // The feature should exist since we just got an APIPermission |
- // for it. The two systems should be updated together whenever a |
- // permission is added. |
- CHECK(feature); |
- |
- Feature::Availability availability = |
- feature->IsAvailableToManifest( |
- id(), |
- GetType(), |
- Feature::ConvertLocation(location()), |
- manifest_version()); |
- if (!availability.is_available()) { |
- // Don't fail, but warn the developer that the manifest contains |
- // unrecognized permissions. This may happen legitimately if the |
- // extensions requests platform- or channel-specific permissions. |
- install_warnings_.push_back(InstallWarning(InstallWarning::FORMAT_TEXT, |
- availability.message())); |
- to_remove.push_back(it->id()); |
- continue; |
- } |
- |
- if (it->id() == APIPermission::kExperimental) { |
- if (!CanSpecifyExperimentalPermission()) { |
- *error = ASCIIToUTF16(errors::kExperimentalFlagRequired); |
- return false; |
- } |
- } |
- } |
- |
- // Remove permissions that are not available to this extension. |
- for (std::vector<APIPermission::ID>::const_iterator it = to_remove.begin(); |
- it != to_remove.end(); ++it) { |
- api_permissions->erase(*it); |
- } |
- |
- // Parse host pattern permissions. |
- const int kAllowedSchemes = CanExecuteScriptEverywhere() ? |
- URLPattern::SCHEME_ALL : kValidHostPermissionSchemes; |
- |
- for (std::vector<std::string>::const_iterator it = host_data.begin(); |
- it != host_data.end(); ++it) { |
- const std::string& permission_str = *it; |
- |
- // Check if it's a host pattern permission. |
- URLPattern pattern = URLPattern(kAllowedSchemes); |
- URLPattern::ParseResult parse_result = pattern.Parse(permission_str); |
- if (parse_result == URLPattern::PARSE_SUCCESS) { |
- if (!CanSpecifyHostPermission(pattern, *api_permissions)) { |
- *error = ErrorUtils::FormatErrorMessageUTF16( |
- errors::kInvalidPermissionScheme, permission_str); |
- return false; |
- } |
- |
- // The path component is not used for host permissions, so we force it |
- // to match all paths. |
- pattern.SetPath("/*"); |
- |
- if (pattern.MatchesScheme(chrome::kFileScheme) && |
- !CanExecuteScriptEverywhere()) { |
- wants_file_access_ = true; |
- if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { |
- pattern.SetValidSchemes( |
- pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); |
- } |
- } |
- |
- host_permissions->AddPattern(pattern); |
- continue; |
- } |
- |
- // It's probably an unknown API permission. Do not throw an error so |
- // extensions can retain backwards compatability (http://crbug.com/42742). |
- install_warnings_.push_back(InstallWarning( |
- InstallWarning::FORMAT_TEXT, |
- base::StringPrintf( |
- "Permission '%s' is unknown or URL pattern is malformed.", |
- permission_str.c_str()))); |
} |
- } |
- return true; |
-} |
- |
-bool Extension::CanSilentlyIncreasePermissions() const { |
- return location() != INTERNAL; |
-} |
- |
-bool Extension::CanSpecifyHostPermission(const URLPattern& pattern, |
- const APIPermissionSet& permissions) const { |
- if (!pattern.match_all_urls() && |
- pattern.MatchesScheme(chrome::kChromeUIScheme)) { |
- // Regular extensions are only allowed access to chrome://favicon. |
- if (pattern.host() == chrome::kChromeUIFaviconHost) |
- return true; |
- // Experimental extensions are also allowed chrome://thumb. |
- if (pattern.host() == chrome::kChromeUIThumbnailHost) { |
- return permissions.find(APIPermission::kExperimental) != |
- permissions.end(); |
+ if (run_location == values::kRunAtDocumentStart) { |
+ result->set_run_location(UserScript::DOCUMENT_START); |
+ } else if (run_location == values::kRunAtDocumentEnd) { |
+ result->set_run_location(UserScript::DOCUMENT_END); |
+ } else if (run_location == values::kRunAtDocumentIdle) { |
+ result->set_run_location(UserScript::DOCUMENT_IDLE); |
+ } else { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidRunAt, |
+ base::IntToString(definition_index)); |
+ return false; |
} |
+ } |
- // Component extensions can have access to all of chrome://*. |
- if (CanExecuteScriptEverywhere()) |
- return true; |
+ // all frames |
+ if (content_script->HasKey(keys::kAllFrames)) { |
+ bool all_frames = false; |
+ if (!content_script->GetBoolean(keys::kAllFrames, &all_frames)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidAllFrames, base::IntToString(definition_index)); |
+ return false; |
+ } |
+ result->set_match_all_frames(all_frames); |
+ } |
+ // matches (required) |
+ const ListValue* matches = NULL; |
+ if (!content_script->GetList(keys::kMatches, &matches)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidMatches, |
+ base::IntToString(definition_index)); |
return false; |
} |
- // Otherwise, the valid schemes were handled by URLPattern. |
- return true; |
-} |
- |
-bool Extension::HasAPIPermission(APIPermission::ID permission) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetActivePermissions()->HasAPIPermission(permission); |
-} |
- |
-bool Extension::HasAPIPermission(const std::string& function_name) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetActivePermissions()-> |
- HasAccessToFunction(function_name); |
-} |
- |
-bool Extension::HasAPIPermissionForTab(int tab_id, |
- APIPermission::ID permission) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- if (runtime_data_.GetActivePermissions()->HasAPIPermission(permission)) |
- return true; |
- scoped_refptr<const PermissionSet> tab_specific_permissions = |
- runtime_data_.GetTabSpecificPermissions(tab_id); |
- return tab_specific_permissions.get() && |
- tab_specific_permissions->HasAPIPermission(permission); |
-} |
- |
-bool Extension::CheckAPIPermissionWithParam(APIPermission::ID permission, |
- const APIPermission::CheckParam* param) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetActivePermissions()-> |
- CheckAPIPermissionWithParam(permission, param); |
-} |
- |
-const URLPatternSet& Extension::GetEffectiveHostPermissions() const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetActivePermissions()->effective_hosts(); |
-} |
- |
-bool Extension::HasHostPermission(const GURL& url) const { |
- if (url.SchemeIs(chrome::kChromeUIScheme) && |
- url.host() != chrome::kChromeUIFaviconHost && |
- url.host() != chrome::kChromeUIThumbnailHost && |
- location() != Extension::COMPONENT) { |
+ if (matches->GetSize() == 0) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidMatchCount, |
+ base::IntToString(definition_index)); |
return false; |
} |
+ for (size_t j = 0; j < matches->GetSize(); ++j) { |
+ std::string match_str; |
+ if (!matches->GetString(j, &match_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidMatch, |
+ base::IntToString(definition_index), |
+ base::IntToString(j), |
+ errors::kExpectString); |
+ return false; |
+ } |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetActivePermissions()-> |
- HasExplicitAccessToOrigin(url); |
-} |
+ URLPattern pattern(UserScript::kValidUserScriptSchemes); |
+ if (CanExecuteScriptEverywhere()) |
+ pattern.SetValidSchemes(URLPattern::SCHEME_ALL); |
-bool Extension::HasEffectiveAccessToAllHosts() const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetActivePermissions()->HasEffectiveAccessToAllHosts(); |
-} |
+ URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
+ if (parse_result != URLPattern::PARSE_SUCCESS) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidMatch, |
+ base::IntToString(definition_index), |
+ base::IntToString(j), |
+ URLPattern::GetParseResultString(parse_result)); |
+ return false; |
+ } |
-bool Extension::HasFullPermissions() const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetActivePermissions()->HasEffectiveFullAccess(); |
-} |
+ if (pattern.MatchesScheme(chrome::kFileScheme) && |
+ !CanExecuteScriptEverywhere()) { |
+ wants_file_access_ = true; |
+ if (!(creation_flags_ & ALLOW_FILE_ACCESS)) { |
+ pattern.SetValidSchemes( |
+ pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); |
+ } |
+ } |
-PermissionMessages Extension::GetPermissionMessages() const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- if (IsTrustedId(id())) { |
- return PermissionMessages(); |
- } else { |
- return runtime_data_.GetActivePermissions()->GetPermissionMessages( |
- GetType()); |
+ result->add_url_pattern(pattern); |
} |
-} |
- |
-std::vector<string16> Extension::GetPermissionMessageStrings() const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- if (IsTrustedId(id())) |
- return std::vector<string16>(); |
- else |
- return runtime_data_.GetActivePermissions()->GetWarningMessages(GetType()); |
-} |
-bool Extension::ShouldSkipPermissionWarnings() const { |
- return IsTrustedId(id()); |
-} |
- |
-void Extension::SetActivePermissions( |
- const PermissionSet* permissions) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- runtime_data_.SetActivePermissions(permissions); |
-} |
- |
-scoped_refptr<const PermissionSet> |
- Extension::GetActivePermissions() const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetActivePermissions(); |
-} |
+ // exclude_matches |
+ if (content_script->HasKey(keys::kExcludeMatches)) { // optional |
+ const ListValue* exclude_matches = NULL; |
+ if (!content_script->GetList(keys::kExcludeMatches, &exclude_matches)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidExcludeMatches, |
+ base::IntToString(definition_index)); |
+ return false; |
+ } |
-bool Extension::HasMultipleUISurfaces() const { |
- int num_surfaces = 0; |
+ for (size_t j = 0; j < exclude_matches->GetSize(); ++j) { |
+ std::string match_str; |
+ if (!exclude_matches->GetString(j, &match_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidExcludeMatch, |
+ base::IntToString(definition_index), |
+ base::IntToString(j), |
+ errors::kExpectString); |
+ return false; |
+ } |
- if (page_action_info()) |
- ++num_surfaces; |
+ URLPattern pattern(UserScript::kValidUserScriptSchemes); |
+ if (CanExecuteScriptEverywhere()) |
+ pattern.SetValidSchemes(URLPattern::SCHEME_ALL); |
+ URLPattern::ParseResult parse_result = pattern.Parse(match_str); |
+ if (parse_result != URLPattern::PARSE_SUCCESS) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidExcludeMatch, |
+ base::IntToString(definition_index), base::IntToString(j), |
+ URLPattern::GetParseResultString(parse_result)); |
+ return false; |
+ } |
- if (browser_action_info()) |
- ++num_surfaces; |
+ result->add_exclude_url_pattern(pattern); |
+ } |
+ } |
- if (is_app()) |
- ++num_surfaces; |
+ // include/exclude globs (mostly for Greasemonkey compatibility) |
+ if (!LoadGlobsHelper(content_script, definition_index, keys::kIncludeGlobs, |
+ error, &UserScript::add_glob, result)) { |
+ return false; |
+ } |
- return num_surfaces > 1; |
-} |
+ if (!LoadGlobsHelper(content_script, definition_index, keys::kExcludeGlobs, |
+ error, &UserScript::add_exclude_glob, result)) { |
+ return false; |
+ } |
-bool Extension::CanExecuteScriptOnPage(const GURL& document_url, |
- const GURL& top_frame_url, |
- int tab_id, |
- const UserScript* script, |
- std::string* error) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- // The gallery is special-cased as a restricted URL for scripting to prevent |
- // access to special JS bindings we expose to the gallery (and avoid things |
- // like extensions removing the "report abuse" link). |
- // TODO(erikkay): This seems like the wrong test. Shouldn't we we testing |
- // against the store app extent? |
- GURL store_url(extension_urls::GetWebstoreLaunchURL()); |
- if ((document_url.host() == store_url.host()) && |
- !CanExecuteScriptEverywhere() && |
- !CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kAllowScriptingGallery)) { |
- if (error) |
- *error = errors::kCannotScriptGallery; |
+ // js and css keys |
+ const ListValue* js = NULL; |
+ if (content_script->HasKey(keys::kJs) && |
+ !content_script->GetList(keys::kJs, &js)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidJsList, |
+ base::IntToString(definition_index)); |
return false; |
} |
- if (document_url.SchemeIs(chrome::kChromeUIScheme) && |
- !CanExecuteScriptEverywhere()) { |
+ const ListValue* css = NULL; |
+ if (content_script->HasKey(keys::kCss) && |
+ !content_script->GetList(keys::kCss, &css)) { |
+ *error = ErrorUtils:: |
+ FormatErrorMessageUTF16(errors::kInvalidCssList, |
+ base::IntToString(definition_index)); |
return false; |
} |
- if (top_frame_url.SchemeIs(extensions::kExtensionScheme) && |
- top_frame_url.GetOrigin() != |
- GetBaseURLFromExtensionId(id()).GetOrigin() && |
- !CanExecuteScriptEverywhere()) { |
+ // The manifest needs to have at least one js or css user script definition. |
+ if (((js ? js->GetSize() : 0) + (css ? css->GetSize() : 0)) == 0) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kMissingFile, |
+ base::IntToString(definition_index)); |
return false; |
} |
- // If a tab ID is specified, try the tab-specific permissions. |
- if (tab_id >= 0) { |
- scoped_refptr<const PermissionSet> tab_permissions = |
- runtime_data_.GetTabSpecificPermissions(tab_id); |
- if (tab_permissions.get() && |
- tab_permissions->explicit_hosts().MatchesSecurityOrigin(document_url)) { |
- return true; |
+ if (js) { |
+ for (size_t script_index = 0; script_index < js->GetSize(); |
+ ++script_index) { |
+ const Value* value; |
+ std::string relative; |
+ if (!js->Get(script_index, &value) || !value->GetAsString(&relative)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidJs, |
+ base::IntToString(definition_index), |
+ base::IntToString(script_index)); |
+ return false; |
+ } |
+ GURL url = GetResourceURL(relative); |
+ ExtensionResource resource = GetResource(relative); |
+ result->js_scripts().push_back(UserScript::File( |
+ resource.extension_root(), resource.relative_path(), url)); |
} |
} |
- // If a script is specified, use its matches. |
- if (script) |
- return script->MatchesURL(document_url); |
- |
- // Otherwise, see if this extension has permission to execute script |
- // programmatically on pages. |
- if (runtime_data_.GetActivePermissions()->HasExplicitAccessToOrigin( |
- document_url)) { |
- return true; |
- } |
- |
- if (error) { |
- *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, |
- document_url.spec()); |
+ if (css) { |
+ for (size_t script_index = 0; script_index < css->GetSize(); |
+ ++script_index) { |
+ const Value* value; |
+ std::string relative; |
+ if (!css->Get(script_index, &value) || !value->GetAsString(&relative)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidCss, |
+ base::IntToString(definition_index), |
+ base::IntToString(script_index)); |
+ return false; |
+ } |
+ GURL url = GetResourceURL(relative); |
+ ExtensionResource resource = GetResource(relative); |
+ result->css_scripts().push_back(UserScript::File( |
+ resource.extension_root(), resource.relative_path(), url)); |
+ } |
} |
- return false; |
-} |
- |
-bool Extension::ShowConfigureContextMenus() const { |
- // Don't show context menu for component extensions. We might want to show |
- // options for component extension button but now there is no component |
- // extension with options. All other menu items like uninstall have |
- // no sense for component extensions. |
- return location() != Extension::COMPONENT; |
+ return true; |
} |
-bool Extension::CanSpecifyExperimentalPermission() const { |
- if (location() == Extension::COMPONENT) |
- return true; |
+bool Extension::LoadGlobsHelper( |
+ const DictionaryValue* content_script, |
+ int content_script_index, |
+ const char* globs_property_name, |
+ string16* error, |
+ void(UserScript::*add_method)(const std::string& glob), |
+ UserScript* instance) { |
+ if (!content_script->HasKey(globs_property_name)) |
+ return true; // they are optional |
- if (CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kEnableExperimentalExtensionApis)) { |
- return true; |
+ const ListValue* list = NULL; |
+ if (!content_script->GetList(globs_property_name, &list)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidGlobList, |
+ base::IntToString(content_script_index), |
+ globs_property_name); |
+ return false; |
} |
- // We rely on the webstore to check access to experimental. This way we can |
- // whitelist extensions to have access to experimental in just the store, and |
- // not have to push a new version of the client. |
- if (from_webstore()) |
- return true; |
+ for (size_t i = 0; i < list->GetSize(); ++i) { |
+ std::string glob; |
+ if (!list->GetString(i, &glob)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidGlob, |
+ base::IntToString(content_script_index), |
+ globs_property_name, |
+ base::IntToString(i)); |
+ return false; |
+ } |
- return false; |
+ (instance->*add_method)(glob); |
+ } |
+ |
+ return true; |
} |
-bool Extension::CanExecuteScriptEverywhere() const { |
- if (location() == Extension::COMPONENT) |
- return true; |
+scoped_ptr<Extension::ActionInfo> Extension::LoadExtensionActionInfoHelper( |
+ const DictionaryValue* extension_action, |
+ ActionInfo::Type action_type, |
+ string16* error) { |
+ scoped_ptr<ActionInfo> result(new ActionInfo()); |
- ScriptingWhitelist* whitelist = ExtensionConfig::GetInstance()->whitelist(); |
+ if (manifest_version_ == 1) { |
+ // kPageActionIcons is obsolete, and used by very few extensions. Continue |
+ // loading it, but only take the first icon as the default_icon path. |
+ const ListValue* icons = NULL; |
+ if (extension_action->HasKey(keys::kPageActionIcons) && |
+ extension_action->GetList(keys::kPageActionIcons, &icons)) { |
+ for (ListValue::const_iterator iter = icons->begin(); |
+ iter != icons->end(); ++iter) { |
+ std::string path; |
+ if (!(*iter)->GetAsString(&path) || !NormalizeAndValidatePath(&path)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
- for (ScriptingWhitelist::const_iterator it = whitelist->begin(); |
- it != whitelist->end(); ++it) { |
- if (id() == *it) { |
- return true; |
+ result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, path); |
+ break; |
+ } |
} |
- } |
- |
- return false; |
-} |
-bool Extension::CanCaptureVisiblePage(const GURL& page_url, |
- int tab_id, |
- std::string* error) const { |
- if (tab_id >= 0) { |
- scoped_refptr<const PermissionSet> tab_permissions = |
- GetTabSpecificPermissions(tab_id); |
- if (tab_permissions.get() && |
- tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) { |
- return true; |
+ std::string id; |
+ if (extension_action->HasKey(keys::kPageActionId)) { |
+ if (!extension_action->GetString(keys::kPageActionId, &id)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionId); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
+ result->id = id; |
} |
} |
- if (HasHostPermission(page_url) || page_url.GetOrigin() == url()) |
- return true; |
- |
- if (error) { |
- *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, |
- page_url.spec()); |
+ // Read the page action |default_icon| (optional). |
+ // The |default_icon| value can be either dictionary {icon size -> icon path} |
+ // or non empty string value. |
+ if (extension_action->HasKey(keys::kPageActionDefaultIcon)) { |
+ const DictionaryValue* icons_value = NULL; |
+ std::string default_icon; |
+ if (extension_action->GetDictionary(keys::kPageActionDefaultIcon, |
+ &icons_value)) { |
+ if (!LoadIconsFromDictionary(icons_value, |
+ extension_misc::kExtensionActionIconSizes, |
+ extension_misc::kNumExtensionActionIconSizes, |
+ &result->default_icon, |
+ error)) { |
+ return scoped_ptr<ActionInfo>(); |
+ } |
+ } else if (extension_action->GetString(keys::kPageActionDefaultIcon, |
+ &default_icon) && |
+ NormalizeAndValidatePath(&default_icon)) { |
+ result->default_icon.Add(extension_misc::EXTENSION_ICON_ACTION, |
+ default_icon); |
+ } else { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionIconPath); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
} |
- return false; |
-} |
- |
-bool Extension::UpdatesFromGallery() const { |
- return extension_urls::IsWebstoreUpdateUrl(update_url()); |
-} |
- |
-bool Extension::OverlapsWithOrigin(const GURL& origin) const { |
- if (url() == origin) |
- return true; |
- if (web_extent().is_empty()) |
- return false; |
- |
- // Note: patterns and extents ignore port numbers. |
- URLPattern origin_only_pattern(kValidWebExtentSchemes); |
- if (!origin_only_pattern.SetScheme(origin.scheme())) |
- return false; |
- origin_only_pattern.SetHost(origin.host()); |
- origin_only_pattern.SetPath("/*"); |
- |
- URLPatternSet origin_only_pattern_list; |
- origin_only_pattern_list.AddPattern(origin_only_pattern); |
+ // Read the page action title from |default_title| if present, |name| if not |
+ // (both optional). |
+ if (extension_action->HasKey(keys::kPageActionDefaultTitle)) { |
+ if (!extension_action->GetString(keys::kPageActionDefaultTitle, |
+ &result->default_title)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionDefaultTitle); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
+ } else if (manifest_version_ == 1 && extension_action->HasKey(keys::kName)) { |
+ if (!extension_action->GetString(keys::kName, &result->default_title)) { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionName); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
+ } |
- return web_extent().OverlapsWith(origin_only_pattern_list); |
-} |
+ // Read the action's |popup| (optional). |
+ const char* popup_key = NULL; |
+ if (extension_action->HasKey(keys::kPageActionDefaultPopup)) |
+ popup_key = keys::kPageActionDefaultPopup; |
-Extension::SyncType Extension::GetSyncType() const { |
- if (!IsSyncable()) { |
- // We have a non-standard location. |
- return SYNC_TYPE_NONE; |
+ if (manifest_version_ == 1 && |
+ extension_action->HasKey(keys::kPageActionPopup)) { |
+ if (popup_key) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidPageActionOldAndNewKeys, |
+ keys::kPageActionDefaultPopup, |
+ keys::kPageActionPopup); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
+ popup_key = keys::kPageActionPopup; |
} |
- // Disallow extensions with non-gallery auto-update URLs for now. |
- // |
- // TODO(akalin): Relax this restriction once we've put in UI to |
- // approve synced extensions. |
- if (!update_url().is_empty() && !UpdatesFromGallery()) |
- return SYNC_TYPE_NONE; |
+ if (popup_key) { |
+ const DictionaryValue* popup = NULL; |
+ std::string url_str; |
- // Disallow extensions with native code plugins. |
- // |
- // TODO(akalin): Relax this restriction once we've put in UI to |
- // approve synced extensions. |
- if (!plugins().empty()) { |
- return SYNC_TYPE_NONE; |
+ if (extension_action->GetString(popup_key, &url_str)) { |
+ // On success, |url_str| is set. Nothing else to do. |
+ } else if (manifest_version_ == 1 && |
+ extension_action->GetDictionary(popup_key, &popup)) { |
+ if (!popup->GetString(keys::kPageActionPopupPath, &url_str)) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidPageActionPopupPath, "<missing>"); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
+ } else { |
+ *error = ASCIIToUTF16(errors::kInvalidPageActionPopup); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
+ |
+ if (!url_str.empty()) { |
+ // An empty string is treated as having no popup. |
+ result->default_popup_url = GetResourceURL(url_str); |
+ if (!result->default_popup_url.is_valid()) { |
+ *error = ErrorUtils::FormatErrorMessageUTF16( |
+ errors::kInvalidPageActionPopupPath, url_str); |
+ return scoped_ptr<ActionInfo>(); |
+ } |
+ } else { |
+ DCHECK(result->default_popup_url.is_empty()) |
+ << "Shouldn't be possible for the popup to be set."; |
+ } |
} |
- switch (GetType()) { |
- case Extension::TYPE_EXTENSION: |
- return SYNC_TYPE_EXTENSION; |
+ return result.Pass(); |
+} |
- case Extension::TYPE_USER_SCRIPT: |
- // We only want to sync user scripts with gallery update URLs. |
- if (UpdatesFromGallery()) |
- return SYNC_TYPE_EXTENSION; |
- else |
- return SYNC_TYPE_NONE; |
+bool Extension::LoadOAuth2Info(string16* error) { |
+ if (!manifest_->HasKey(keys::kOAuth2)) |
+ return true; |
- case Extension::TYPE_HOSTED_APP: |
- case Extension::TYPE_LEGACY_PACKAGED_APP: |
- case Extension::TYPE_PLATFORM_APP: |
- return SYNC_TYPE_APP; |
+ if (!manifest_->GetString(keys::kOAuth2ClientId, &oauth2_info_.client_id) || |
+ oauth2_info_.client_id.empty()) { |
+ *error = ASCIIToUTF16(errors::kInvalidOAuth2ClientId); |
+ return false; |
+ } |
- default: |
- return SYNC_TYPE_NONE; |
+ ListValue* list = NULL; |
+ if (!manifest_->GetList(keys::kOAuth2Scopes, &list)) { |
+ *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes); |
+ return false; |
} |
-} |
-bool Extension::IsSyncable() const { |
- // TODO(akalin): Figure out if we need to allow some other types. |
+ for (size_t i = 0; i < list->GetSize(); ++i) { |
+ std::string scope; |
+ if (!list->GetString(i, &scope)) { |
+ *error = ASCIIToUTF16(errors::kInvalidOAuth2Scopes); |
+ return false; |
+ } |
+ oauth2_info_.scopes.push_back(scope); |
+ } |
- // Default apps are not synced because otherwise they will pollute profiles |
- // that don't already have them. Specially, if a user doesn't have default |
- // apps, creates a new profile (which get default apps) and then enables sync |
- // for it, then their profile everywhere gets the default apps. |
- bool is_syncable = (location() == Extension::INTERNAL && |
- !was_installed_by_default()); |
- // Sync the chrome web store to maintain its position on the new tab page. |
- is_syncable |= (id() == extension_misc::kWebStoreAppId); |
- return is_syncable; |
+ return true; |
} |
-bool Extension::RequiresSortOrdinal() const { |
- return is_app() && (display_in_launcher_ || display_in_new_tab_page_); |
-} |
+bool Extension::HasMultipleUISurfaces() const { |
+ int num_surfaces = 0; |
-bool Extension::ShouldDisplayInAppLauncher() const { |
- // Only apps should be displayed in the launcher. |
- return is_app() && display_in_launcher_; |
-} |
+ if (page_action_info()) |
+ ++num_surfaces; |
-bool Extension::ShouldDisplayInNewTabPage() const { |
- // Only apps should be displayed on the NTP. |
- return is_app() && display_in_new_tab_page_; |
-} |
+ if (browser_action_info()) |
+ ++num_surfaces; |
-bool Extension::InstallWarning::operator==(const InstallWarning& other) const { |
- return format == other.format && message == other.message; |
-} |
+ if (is_app()) |
+ ++num_surfaces; |
-void PrintTo(const Extension::InstallWarning& warning, ::std::ostream* os) { |
- *os << "InstallWarning("; |
- switch (warning.format) { |
- case Extension::InstallWarning::FORMAT_TEXT: |
- *os << "FORMAT_TEXT, \""; |
- break; |
- case Extension::InstallWarning::FORMAT_HTML: |
- *os << "FORMAT_HTML, \""; |
- break; |
- } |
- // This is just for test error messages, so no need to escape '"' |
- // characters inside the message. |
- *os << warning.message << "\")"; |
+ return num_surfaces > 1; |
} |
-ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, |
- const std::string& id, |
- const FilePath& path, |
- Extension::Location location) |
- : extension_id(id), |
- extension_path(path), |
- extension_location(location) { |
- if (manifest) |
- extension_manifest.reset(manifest->DeepCopy()); |
-} |
+void Extension::OverrideLaunchUrl(const GURL& override_url) { |
+ GURL new_url(override_url); |
+ if (!new_url.is_valid()) { |
+ DLOG(WARNING) << "Invalid override url given for " << name(); |
+ } else { |
+ if (new_url.has_port()) { |
+ DLOG(WARNING) << "Override URL passed for " << name() |
+ << " should not contain a port. Removing it."; |
-bool Extension::ShouldDisplayInExtensionSettings() const { |
- // Don't show for themes since the settings UI isn't really useful for them. |
- if (is_theme()) |
- return false; |
+ GURL::Replacements remove_port; |
+ remove_port.ClearPort(); |
+ new_url = new_url.ReplaceComponents(remove_port); |
+ } |
- // Don't show component extensions because they are only extensions as an |
- // implementation detail of Chrome. |
- if (location() == Extension::COMPONENT && |
- !CommandLine::ForCurrentProcess()->HasSwitch( |
- switches::kShowComponentExtensionOptions)) { |
- return false; |
+ launch_web_url_ = new_url.spec(); |
+ |
+ URLPattern pattern(kValidWebExtentSchemes); |
+ URLPattern::ParseResult result = pattern.Parse(new_url.spec()); |
+ DCHECK_EQ(result, URLPattern::PARSE_SUCCESS); |
+ pattern.SetPath(pattern.path() + '*'); |
+ extent_.AddPattern(pattern); |
} |
+} |
- // Always show unpacked extensions and apps. |
- if (location() == Extension::LOAD) |
+bool Extension::CanSpecifyExperimentalPermission() const { |
+ if (location() == Extension::COMPONENT) |
return true; |
- // Unless they are unpacked, never show hosted apps. Note: We intentionally |
- // show packaged apps and platform apps because there are some pieces of |
- // functionality that are only available in chrome://extensions/ but which |
- // are needed for packaged and platform apps. For example, inspecting |
- // background pages. See http://crbug.com/116134. |
- if (is_hosted_app()) |
- return false; |
+ if (CommandLine::ForCurrentProcess()->HasSwitch( |
+ switches::kEnableExperimentalExtensionApis)) { |
+ return true; |
+ } |
- return true; |
-} |
+ // We rely on the webstore to check access to experimental. This way we can |
+ // whitelist extensions to have access to experimental in just the store, and |
+ // not have to push a new version of the client. |
+ if (from_webstore()) |
+ return true; |
-bool Extension::HasContentScriptAtURL(const GURL& url) const { |
- for (UserScriptList::const_iterator it = content_scripts_.begin(); |
- it != content_scripts_.end(); ++it) { |
- if (it->MatchesURL(url)) |
- return true; |
- } |
return false; |
} |
-scoped_refptr<const PermissionSet> Extension::GetTabSpecificPermissions( |
- int tab_id) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- return runtime_data_.GetTabSpecificPermissions(tab_id); |
-} |
+bool Extension::CanSpecifyHostPermission(const URLPattern& pattern, |
+ const APIPermissionSet& permissions) const { |
+ if (!pattern.match_all_urls() && |
+ pattern.MatchesScheme(chrome::kChromeUIScheme)) { |
+ // Regular extensions are only allowed access to chrome://favicon. |
+ if (pattern.host() == chrome::kChromeUIFaviconHost) |
+ return true; |
-void Extension::UpdateTabSpecificPermissions( |
- int tab_id, |
- scoped_refptr<const PermissionSet> permissions) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- runtime_data_.UpdateTabSpecificPermissions(tab_id, permissions); |
-} |
+ // Experimental extensions are also allowed chrome://thumb. |
+ if (pattern.host() == chrome::kChromeUIThumbnailHost) { |
+ return permissions.find(APIPermission::kExperimental) != |
+ permissions.end(); |
+ } |
-void Extension::ClearTabSpecificPermissions(int tab_id) const { |
- base::AutoLock auto_lock(runtime_data_lock_); |
- runtime_data_.ClearTabSpecificPermissions(tab_id); |
+ // Component extensions can have access to all of chrome://*. |
+ if (CanExecuteScriptEverywhere()) |
+ return true; |
+ |
+ return false; |
+ } |
+ |
+ // Otherwise, the valid schemes were handled by URLPattern. |
+ return true; |
} |
bool Extension::CheckMinimumChromeVersion(string16* error) const { |
@@ -4076,48 +4094,34 @@ bool Extension::CheckConflictingFeatures(std::string* utf8_error) const { |
return true; |
} |
-ExtensionInfo::~ExtensionInfo() {} |
- |
-Extension::RuntimeData::RuntimeData() {} |
-Extension::RuntimeData::RuntimeData(const PermissionSet* active) |
- : active_permissions_(active) {} |
-Extension::RuntimeData::~RuntimeData() {} |
- |
-scoped_refptr<const PermissionSet> |
- Extension::RuntimeData::GetActivePermissions() const { |
- return active_permissions_; |
-} |
- |
-void Extension::RuntimeData::SetActivePermissions( |
- const PermissionSet* active) { |
- active_permissions_ = active; |
-} |
- |
-scoped_refptr<const PermissionSet> |
- Extension::RuntimeData::GetTabSpecificPermissions(int tab_id) const { |
- CHECK_GE(tab_id, 0); |
- TabPermissionsMap::const_iterator it = tab_specific_permissions_.find(tab_id); |
- return (it != tab_specific_permissions_.end()) ? it->second : NULL; |
-} |
- |
-void Extension::RuntimeData::UpdateTabSpecificPermissions( |
- int tab_id, |
- scoped_refptr<const PermissionSet> permissions) { |
- CHECK_GE(tab_id, 0); |
- if (tab_specific_permissions_.count(tab_id)) { |
- tab_specific_permissions_[tab_id] = PermissionSet::CreateUnion( |
- tab_specific_permissions_[tab_id], |
- permissions.get()); |
- } else { |
- tab_specific_permissions_[tab_id] = permissions; |
+void PrintTo(const Extension::InstallWarning& warning, ::std::ostream* os) { |
+ *os << "InstallWarning("; |
+ switch (warning.format) { |
+ case Extension::InstallWarning::FORMAT_TEXT: |
+ *os << "FORMAT_TEXT, \""; |
+ break; |
+ case Extension::InstallWarning::FORMAT_HTML: |
+ *os << "FORMAT_HTML, \""; |
+ break; |
} |
+ // This is just for test error messages, so no need to escape '"' |
+ // characters inside the message. |
+ *os << warning.message << "\")"; |
} |
-void Extension::RuntimeData::ClearTabSpecificPermissions(int tab_id) { |
- CHECK_GE(tab_id, 0); |
- tab_specific_permissions_.erase(tab_id); |
+ExtensionInfo::ExtensionInfo(const DictionaryValue* manifest, |
+ const std::string& id, |
+ const FilePath& path, |
+ Extension::Location location) |
+ : extension_id(id), |
+ extension_path(path), |
+ extension_location(location) { |
+ if (manifest) |
+ extension_manifest.reset(manifest->DeepCopy()); |
} |
+ExtensionInfo::~ExtensionInfo() {} |
+ |
UnloadedExtensionInfo::UnloadedExtensionInfo( |
const Extension* extension, |
extension_misc::UnloadedExtensionReason reason) |