| 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)
|
|
|