| Index: content/renderer/manifest/manifest_parser.cc
|
| diff --git a/content/renderer/manifest/manifest_parser.cc b/content/renderer/manifest/manifest_parser.cc
|
| index bab85cd75f1b1acc9aad4161f213dfe65b155f6e..961ab3742cd32e27340a779b734701429778156b 100644
|
| --- a/content/renderer/manifest/manifest_parser.cc
|
| +++ b/content/renderer/manifest/manifest_parser.cc
|
| @@ -19,21 +19,142 @@ namespace content {
|
|
|
| namespace {
|
|
|
| -enum TrimType {
|
| - Trim,
|
| - NoTrim
|
| -};
|
| -
|
| -base::NullableString16 ParseString(const base::DictionaryValue& dictionary,
|
| - const std::string& key,
|
| - TrimType trim) {
|
| +// Helper function that returns whether the given |str| is a valid width or
|
| +// height value for an icon sizes per:
|
| +// https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes
|
| +bool IsValidIconWidthOrHeight(const std::string& str) {
|
| + if (str.empty() || str[0] == '0')
|
| + return false;
|
| + for (size_t i = 0; i < str.size(); ++i)
|
| + if (!IsAsciiDigit(str[i]))
|
| + return false;
|
| + return true;
|
| +}
|
| +
|
| +// Parses the 'sizes' attribute of an icon as described in the HTML spec:
|
| +// https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes
|
| +// Return a vector of gfx::Size that contains the valid sizes found. "Any" is
|
| +// represented by gfx::Size(0, 0).
|
| +// TODO(mlamouri): this is implemented as a separate function because it should
|
| +// be refactored with the other icon sizes parsing implementations, see
|
| +// http://crbug.com/416477
|
| +std::vector<gfx::Size> ParseIconSizesHTML(const base::string16& sizes_str16) {
|
| + if (!base::IsStringASCII(sizes_str16))
|
| + return std::vector<gfx::Size>();
|
| +
|
| + std::vector<gfx::Size> sizes;
|
| + std::string sizes_str =
|
| + base::StringToLowerASCII(base::UTF16ToUTF8(sizes_str16));
|
| + std::vector<std::string> sizes_str_list;
|
| + base::SplitStringAlongWhitespace(sizes_str, &sizes_str_list);
|
| +
|
| + for (size_t i = 0; i < sizes_str_list.size(); ++i) {
|
| + std::string& size_str = sizes_str_list[i];
|
| + if (size_str == "any") {
|
| + sizes.push_back(gfx::Size(0, 0));
|
| + continue;
|
| + }
|
| +
|
| + // It is expected that [0] => width and [1] => height after the split.
|
| + std::vector<std::string> size_list;
|
| + base::SplitStringDontTrim(size_str, L'x', &size_list);
|
| + if (size_list.size() != 2)
|
| + continue;
|
| + if (!IsValidIconWidthOrHeight(size_list[0]) ||
|
| + !IsValidIconWidthOrHeight(size_list[1])) {
|
| + continue;
|
| + }
|
| +
|
| + int width, height;
|
| + if (!base::StringToInt(size_list[0], &width) ||
|
| + !base::StringToInt(size_list[1], &height)) {
|
| + continue;
|
| + }
|
| +
|
| + sizes.push_back(gfx::Size(width, height));
|
| + }
|
| +
|
| + return sizes;
|
| +}
|
| +
|
| +const std::string& GetErrorPrefix() {
|
| + CR_DEFINE_STATIC_LOCAL(std::string, error_prefix,
|
| + ("Manifest parsing error: "));
|
| + return error_prefix;
|
| +}
|
| +
|
| +} // anonymous namespace
|
| +
|
| +
|
| +ManifestParser::ManifestParser(const base::StringPiece& data,
|
| + const GURL& manifest_url,
|
| + const GURL& document_url)
|
| + : data_(data),
|
| + manifest_url_(manifest_url),
|
| + document_url_(document_url),
|
| + failed_(false) {
|
| +}
|
| +
|
| +ManifestParser::~ManifestParser() {
|
| +}
|
| +
|
| +void ManifestParser::Parse() {
|
| + std::string parse_error;
|
| + scoped_ptr<base::Value> value(
|
| + base::JSONReader::ReadAndReturnError(data_, base::JSON_PARSE_RFC,
|
| + nullptr, &parse_error));
|
| +
|
| + if (!value) {
|
| + errors_.push_back(GetErrorPrefix() + parse_error);
|
| + ManifestUmaUtil::ParseFailed();
|
| + failed_ = true;
|
| + return;
|
| + }
|
| +
|
| + base::DictionaryValue* dictionary = nullptr;
|
| + if (!value->GetAsDictionary(&dictionary)) {
|
| + errors_.push_back(GetErrorPrefix() +
|
| + "root element must be a valid JSON object.");
|
| + ManifestUmaUtil::ParseFailed();
|
| + failed_ = true;
|
| + return;
|
| + }
|
| + DCHECK(dictionary);
|
| +
|
| + manifest_.name = ParseName(*dictionary);
|
| + manifest_.short_name = ParseShortName(*dictionary);
|
| + manifest_.start_url = ParseStartURL(*dictionary);
|
| + manifest_.display = ParseDisplay(*dictionary);
|
| + manifest_.orientation = ParseOrientation(*dictionary);
|
| + manifest_.icons = ParseIcons(*dictionary);
|
| + manifest_.gcm_sender_id = ParseGCMSenderID(*dictionary);
|
| +
|
| + ManifestUmaUtil::ParseSucceeded(manifest_);
|
| +}
|
| +
|
| +const Manifest& ManifestParser::manifest() const {
|
| + return manifest_;
|
| +}
|
| +
|
| +const std::vector<std::string>& ManifestParser::errors() const {
|
| + return errors_;
|
| +}
|
| +
|
| +bool ManifestParser::failed() const {
|
| + return failed_;
|
| +}
|
| +
|
| +base::NullableString16 ManifestParser::ParseString(
|
| + const base::DictionaryValue& dictionary,
|
| + const std::string& key,
|
| + TrimType trim) {
|
| if (!dictionary.HasKey(key))
|
| return base::NullableString16();
|
|
|
| base::string16 value;
|
| if (!dictionary.GetString(key, &value)) {
|
| - // TODO(mlamouri): provide a custom message to the developer console about
|
| - // the property being incorrectly set.
|
| + errors_.push_back(GetErrorPrefix() +
|
| + "property '" + key + "' ignored, type string expected.");
|
| return base::NullableString16();
|
| }
|
|
|
| @@ -42,13 +163,9 @@ base::NullableString16 ParseString(const base::DictionaryValue& dictionary,
|
| return base::NullableString16(value, false);
|
| }
|
|
|
| -// Helper function to parse URLs present on a given |dictionary| in a given
|
| -// field identified by its |key|. The URL is first parsed as a string then
|
| -// resolved using |base_url|.
|
| -// Returns a GURL. If the parsing failed, the GURL will not be valid.
|
| -GURL ParseURL(const base::DictionaryValue& dictionary,
|
| - const std::string& key,
|
| - const GURL& base_url) {
|
| +GURL ManifestParser::ParseURL(const base::DictionaryValue& dictionary,
|
| + const std::string& key,
|
| + const GURL& base_url) {
|
| base::NullableString16 url_str = ParseString(dictionary, key, NoTrim);
|
| if (url_str.is_null())
|
| return GURL();
|
| @@ -56,44 +173,32 @@ GURL ParseURL(const base::DictionaryValue& dictionary,
|
| return base_url.Resolve(url_str.string());
|
| }
|
|
|
| -// Parses the 'name' field of the manifest, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-the-name-member
|
| -// Returns the parsed string if any, a null string if the parsing failed.
|
| -base::NullableString16 ParseName(const base::DictionaryValue& dictionary) {
|
| +base::NullableString16 ManifestParser::ParseName(
|
| + const base::DictionaryValue& dictionary) {
|
| return ParseString(dictionary, "name", Trim);
|
| }
|
|
|
| -// Parses the 'short_name' field of the manifest, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-the-short-name-member
|
| -// Returns the parsed string if any, a null string if the parsing failed.
|
| -base::NullableString16 ParseShortName(
|
| +base::NullableString16 ManifestParser::ParseShortName(
|
| const base::DictionaryValue& dictionary) {
|
| return ParseString(dictionary, "short_name", Trim);
|
| }
|
|
|
| -// Parses the 'start_url' field of the manifest, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-the-start_url-member
|
| -// Returns the parsed GURL if any, an empty GURL if the parsing failed.
|
| -GURL ParseStartURL(const base::DictionaryValue& dictionary,
|
| - const GURL& manifest_url,
|
| - const GURL& document_url) {
|
| - GURL start_url = ParseURL(dictionary, "start_url", manifest_url);
|
| +GURL ManifestParser::ParseStartURL(const base::DictionaryValue& dictionary) {
|
| + GURL start_url = ParseURL(dictionary, "start_url", manifest_url_);
|
| if (!start_url.is_valid())
|
| return GURL();
|
|
|
| - if (start_url.GetOrigin() != document_url.GetOrigin()) {
|
| - // TODO(mlamouri): provide a custom message to the developer console.
|
| + if (start_url.GetOrigin() != document_url_.GetOrigin()) {
|
| + errors_.push_back(GetErrorPrefix() + "property 'start_url' ignored, should "
|
| + "be same origin as document.");
|
| return GURL();
|
| }
|
|
|
| return start_url;
|
| }
|
|
|
| -// Parses the 'display' field of the manifest, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-the-display-member
|
| -// Returns the parsed DisplayMode if any, DISPLAY_MODE_UNSPECIFIED if the
|
| -// parsing failed.
|
| -Manifest::DisplayMode ParseDisplay(const base::DictionaryValue& dictionary) {
|
| +Manifest::DisplayMode ManifestParser::ParseDisplay(
|
| + const base::DictionaryValue& dictionary) {
|
| base::NullableString16 display = ParseString(dictionary, "display", Trim);
|
| if (display.is_null())
|
| return Manifest::DISPLAY_MODE_UNSPECIFIED;
|
| @@ -106,15 +211,13 @@ Manifest::DisplayMode ParseDisplay(const base::DictionaryValue& dictionary) {
|
| return Manifest::DISPLAY_MODE_MINIMAL_UI;
|
| else if (LowerCaseEqualsASCII(display.string(), "browser"))
|
| return Manifest::DISPLAY_MODE_BROWSER;
|
| - else
|
| + else {
|
| + errors_.push_back(GetErrorPrefix() + "unknown 'display' value ignored.");
|
| return Manifest::DISPLAY_MODE_UNSPECIFIED;
|
| + }
|
| }
|
|
|
| -// Parses the 'orientation' field of the manifest, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-the-orientation-member
|
| -// Returns the parsed WebScreenOrientationLockType if any,
|
| -// WebScreenOrientationLockDefault if the parsing failed.
|
| -blink::WebScreenOrientationLockType ParseOrientation(
|
| +blink::WebScreenOrientationLockType ManifestParser::ParseOrientation(
|
| const base::DictionaryValue& dictionary) {
|
| base::NullableString16 orientation =
|
| ParseString(dictionary, "orientation", Trim);
|
| @@ -138,130 +241,69 @@ blink::WebScreenOrientationLockType ParseOrientation(
|
| return blink::WebScreenOrientationLockPortraitPrimary;
|
| else if (LowerCaseEqualsASCII(orientation.string(), "portrait-secondary"))
|
| return blink::WebScreenOrientationLockPortraitSecondary;
|
| - else
|
| + else {
|
| + errors_.push_back(GetErrorPrefix() +
|
| + "unknown 'orientation' value ignored.");
|
| return blink::WebScreenOrientationLockDefault;
|
| + }
|
| }
|
|
|
| -// Parses the 'src' field of an icon, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-the-src-member-of-an-icon
|
| -// Returns the parsed GURL if any, an empty GURL if the parsing failed.
|
| -GURL ParseIconSrc(const base::DictionaryValue& icon,
|
| - const GURL& manifest_url) {
|
| - return ParseURL(icon, "src", manifest_url);
|
| +GURL ManifestParser::ParseIconSrc(const base::DictionaryValue& icon) {
|
| + return ParseURL(icon, "src", manifest_url_);
|
| }
|
|
|
| -// Parses the 'type' field of an icon, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-the-type-member-of-an-icon
|
| -// Returns the parsed string if any, a null string if the parsing failed.
|
| -base::NullableString16 ParseIconType(const base::DictionaryValue& icon) {
|
| - return ParseString(icon, "type", Trim);
|
| +base::NullableString16 ManifestParser::ParseIconType(
|
| + const base::DictionaryValue& icon) {
|
| + return ParseString(icon, "type", Trim);
|
| }
|
|
|
| -// Parses the 'density' field of an icon, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-a-density-member-of-an-icon
|
| -// Returns the parsed double if any, Manifest::Icon::kDefaultDensity if the
|
| -// parsing failed.
|
| -double ParseIconDensity(const base::DictionaryValue& icon) {
|
| +double ManifestParser::ParseIconDensity(const base::DictionaryValue& icon) {
|
| double density;
|
| - if (!icon.GetDouble("density", &density) || density <= 0)
|
| + if (!icon.HasKey("density"))
|
| return Manifest::Icon::kDefaultDensity;
|
| +
|
| + if (!icon.GetDouble("density", &density) || density <= 0) {
|
| + errors_.push_back(GetErrorPrefix() +
|
| + "icon 'density' ignored, must be float greater than 0.");
|
| + return Manifest::Icon::kDefaultDensity;
|
| + }
|
| return density;
|
| }
|
|
|
| -// Helper function that returns whether the given |str| is a valid width or
|
| -// height value for an icon sizes per:
|
| -// https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes
|
| -bool IsValidIconWidthOrHeight(const std::string& str) {
|
| - if (str.empty() || str[0] == '0')
|
| - return false;
|
| - for (size_t i = 0; i < str.size(); ++i)
|
| - if (!IsAsciiDigit(str[i]))
|
| - return false;
|
| - return true;
|
| -}
|
| +std::vector<gfx::Size> ManifestParser::ParseIconSizes(
|
| + const base::DictionaryValue& icon) {
|
| + base::NullableString16 sizes_str = ParseString(icon, "sizes", NoTrim);
|
|
|
| -// Parses the 'sizes' attribute of an icon as described in the HTML spec:
|
| -// https://html.spec.whatwg.org/multipage/semantics.html#attr-link-sizes
|
| -// Return a vector of gfx::Size that contains the valid sizes found. "Any" is
|
| -// represented by gfx::Size(0, 0).
|
| -// TODO(mlamouri): this is implemented as a separate function because it should
|
| -// be refactored with the other icon sizes parsing implementations, see
|
| -// http://crbug.com/416477
|
| -std::vector<gfx::Size> ParseIconSizesHTML(const base::string16& sizes_str16) {
|
| - if (!base::IsStringASCII(sizes_str16))
|
| + if (sizes_str.is_null())
|
| return std::vector<gfx::Size>();
|
|
|
| - std::vector<gfx::Size> sizes;
|
| - std::string sizes_str =
|
| - base::StringToLowerASCII(base::UTF16ToUTF8(sizes_str16));
|
| - std::vector<std::string> sizes_str_list;
|
| - base::SplitStringAlongWhitespace(sizes_str, &sizes_str_list);
|
| -
|
| - for (size_t i = 0; i < sizes_str_list.size(); ++i) {
|
| - std::string& size_str = sizes_str_list[i];
|
| - if (size_str == "any") {
|
| - sizes.push_back(gfx::Size(0, 0));
|
| - continue;
|
| - }
|
| -
|
| - // It is expected that [0] => width and [1] => height after the split.
|
| - std::vector<std::string> size_list;
|
| - base::SplitStringDontTrim(size_str, L'x', &size_list);
|
| - if (size_list.size() != 2)
|
| - continue;
|
| - if (!IsValidIconWidthOrHeight(size_list[0]) ||
|
| - !IsValidIconWidthOrHeight(size_list[1])) {
|
| - continue;
|
| - }
|
| -
|
| - int width, height;
|
| - if (!base::StringToInt(size_list[0], &width) ||
|
| - !base::StringToInt(size_list[1], &height)) {
|
| - continue;
|
| - }
|
| -
|
| - sizes.push_back(gfx::Size(width, height));
|
| + std::vector<gfx::Size> sizes = ParseIconSizesHTML(sizes_str.string());
|
| + if (sizes.empty()) {
|
| + errors_.push_back(GetErrorPrefix() + "found icon with no valid size.");
|
| }
|
| -
|
| return sizes;
|
| }
|
|
|
| -// Parses the 'sizes' field of an icon, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-a-sizes-member-of-an-icon
|
| -// Returns a vector of gfx::Size with the successfully parsed sizes, if any. An
|
| -// empty vector if the field was not present or empty. "Any" is represented by
|
| -// gfx::Size(0, 0).
|
| -std::vector<gfx::Size> ParseIconSizes(const base::DictionaryValue& icon) {
|
| - base::NullableString16 sizes_str = ParseString(icon, "sizes", NoTrim);
|
| -
|
| - return sizes_str.is_null() ? std::vector<gfx::Size>()
|
| - : ParseIconSizesHTML(sizes_str.string());
|
| -}
|
| -
|
| -// Parses the 'icons' field of a Manifest, as defined in:
|
| -// http://w3c.github.io/manifest/#dfn-steps-for-processing-the-icons-member
|
| -// Returns a vector of Manifest::Icon with the successfully parsed icons, if
|
| -// any. An empty vector if the field was not present or empty.
|
| -std::vector<Manifest::Icon> ParseIcons(const base::DictionaryValue& dictionary,
|
| - const GURL& manifest_url) {
|
| +std::vector<Manifest::Icon> ManifestParser::ParseIcons(
|
| + const base::DictionaryValue& dictionary) {
|
| std::vector<Manifest::Icon> icons;
|
| if (!dictionary.HasKey("icons"))
|
| return icons;
|
|
|
| - const base::ListValue* icons_list = 0;
|
| + const base::ListValue* icons_list = nullptr;
|
| if (!dictionary.GetList("icons", &icons_list)) {
|
| - // TODO(mlamouri): provide a custom message to the developer console about
|
| - // the property being incorrectly set.
|
| + errors_.push_back(GetErrorPrefix() +
|
| + "property 'icons' ignored, type array expected.");
|
| return icons;
|
| }
|
|
|
| for (size_t i = 0; i < icons_list->GetSize(); ++i) {
|
| - const base::DictionaryValue* icon_dictionary = 0;
|
| + const base::DictionaryValue* icon_dictionary = nullptr;
|
| if (!icons_list->GetDictionary(i, &icon_dictionary))
|
| continue;
|
|
|
| Manifest::Icon icon;
|
| - icon.src = ParseIconSrc(*icon_dictionary, manifest_url);
|
| + icon.src = ParseIconSrc(*icon_dictionary);
|
| // An icon MUST have a valid src. If it does not, it MUST be ignored.
|
| if (!icon.src.is_valid())
|
| continue;
|
| @@ -275,54 +317,9 @@ std::vector<Manifest::Icon> ParseIcons(const base::DictionaryValue& dictionary,
|
| return icons;
|
| }
|
|
|
| -// Parses the 'gcm_sender_id' field of the manifest.
|
| -// This is a proprietary extension of the Web Manifest specification.
|
| -// Returns the parsed string if any, a null string if the parsing failed.
|
| -base::NullableString16 ParseGCMSenderID(
|
| +base::NullableString16 ManifestParser::ParseGCMSenderID(
|
| const base::DictionaryValue& dictionary) {
|
| return ParseString(dictionary, "gcm_sender_id", Trim);
|
| }
|
|
|
| -} // anonymous namespace
|
| -
|
| -Manifest ManifestParser::Parse(const base::StringPiece& json,
|
| - const GURL& manifest_url,
|
| - const GURL& document_url) {
|
| - scoped_ptr<base::Value> value(base::JSONReader::Read(json));
|
| - if (!value) {
|
| - // TODO(mlamouri): get the JSON parsing error and report it to the developer
|
| - // console.
|
| - ManifestUmaUtil::ParseFailed();
|
| - return Manifest();
|
| - }
|
| -
|
| - if (value->GetType() != base::Value::TYPE_DICTIONARY) {
|
| - // TODO(mlamouri): provide a custom message to the developer console.
|
| - ManifestUmaUtil::ParseFailed();
|
| - return Manifest();
|
| - }
|
| -
|
| - base::DictionaryValue* dictionary = 0;
|
| - value->GetAsDictionary(&dictionary);
|
| - if (!dictionary) {
|
| - // TODO(mlamouri): provide a custom message to the developer console.
|
| - ManifestUmaUtil::ParseFailed();
|
| - return Manifest();
|
| - }
|
| -
|
| - Manifest manifest;
|
| -
|
| - manifest.name = ParseName(*dictionary);
|
| - manifest.short_name = ParseShortName(*dictionary);
|
| - manifest.start_url = ParseStartURL(*dictionary, manifest_url, document_url);
|
| - manifest.display = ParseDisplay(*dictionary);
|
| - manifest.orientation = ParseOrientation(*dictionary);
|
| - manifest.icons = ParseIcons(*dictionary, manifest_url);
|
| - manifest.gcm_sender_id = ParseGCMSenderID(*dictionary);
|
| -
|
| - ManifestUmaUtil::ParseSucceeded(manifest);
|
| -
|
| - return manifest;
|
| -}
|
| -
|
| } // namespace content
|
|
|