Commit b847169d authored by mlamouri's avatar mlamouri Committed by Commit bot

Add support for Manifest.icons.sizes

This is re-implementing the HTML icon sizes parsing algorithm
given that it is missing a re-usable place for it. There is
a follow-up bug opened at http://crbug.com/416477

BUG=366145

Review URL: https://codereview.chromium.org/591073002

Cr-Commit-Position: refs/heads/master@{#296386}
parent 564697be
......@@ -21,6 +21,7 @@ IPC_STRUCT_TRAITS_BEGIN(content::Manifest::Icon)
IPC_STRUCT_TRAITS_MEMBER(src)
IPC_STRUCT_TRAITS_MEMBER(type)
IPC_STRUCT_TRAITS_MEMBER(density)
IPC_STRUCT_TRAITS_MEMBER(sizes)
IPC_STRUCT_TRAITS_END()
IPC_STRUCT_TRAITS_BEGIN(content::Manifest)
......
......@@ -10,6 +10,7 @@
#include "base/strings/nullable_string16.h"
#include "content/common/content_export.h"
#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h"
#include "ui/gfx/geometry/size.h"
#include "url/gurl.h"
namespace content {
......@@ -45,6 +46,10 @@ struct CONTENT_EXPORT Manifest {
// Default value is 1.0 if the value is missing or invalid.
double density;
// Empty if the parsing failed, the field was not present or empty.
// The special value "any" is represented by gfx::Size(0, 0).
std::vector<gfx::Size> sizes;
// Default density. Set to 1.0.
static const double kDefaultDensity;
};
......
......@@ -6,10 +6,13 @@
#include "base/json/json_reader.h"
#include "base/strings/nullable_string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "content/public/common/manifest.h"
#include "ui/gfx/geometry/size.h"
namespace content {
......@@ -165,6 +168,80 @@ double ParseIconDensity(const base::DictionaryValue& icon) {
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;
}
// 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;
}
// 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> icons;
......@@ -190,7 +267,7 @@ std::vector<Manifest::Icon> ParseIcons(const base::DictionaryValue& dictionary,
continue;
icon.type = ParseIconType(*icon_dictionary);
icon.density = ParseIconDensity(*icon_dictionary);
// TODO(mlamouri): icon.sizes
icon.sizes = ParseIconSizes(*icon_dictionary);
icons.push_back(icon);
}
......
......@@ -492,4 +492,92 @@ TEST_F(ManifestParserTest, IconDensityParseRules) {
}
}
TEST_F(ManifestParserTest, IconSizesParseRules) {
// Smoke test.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \"42x42\" } ] }");
EXPECT_EQ(manifest.icons[0].sizes.size(), 1u);
}
// Trim whitespaces.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \" 42x42 \" } ] }");
EXPECT_EQ(manifest.icons[0].sizes.size(), 1u);
}
// Don't parse if name isn't a string.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": {} } ] }");
EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
}
// Don't parse if name isn't a string.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": 42 } ] }");
EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
}
// Smoke test: value correctly parsed.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \"42x42 48x48\" } ] }");
EXPECT_EQ(manifest.icons[0].sizes[0], gfx::Size(42, 42));
EXPECT_EQ(manifest.icons[0].sizes[1], gfx::Size(48, 48));
}
// <WIDTH>'x'<HEIGHT> and <WIDTH>'X'<HEIGHT> are equivalent.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \"42X42 48X48\" } ] }");
EXPECT_EQ(manifest.icons[0].sizes[0], gfx::Size(42, 42));
EXPECT_EQ(manifest.icons[0].sizes[1], gfx::Size(48, 48));
}
// Twice the same value is parsed twice.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \"42X42 42x42\" } ] }");
EXPECT_EQ(manifest.icons[0].sizes[0], gfx::Size(42, 42));
EXPECT_EQ(manifest.icons[0].sizes[1], gfx::Size(42, 42));
}
// Width or height can't start with 0.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \"004X007 042x00\" } ] }");
EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
}
// Width and height MUST contain digits.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \"e4X1.0 55ax1e10\" } ] }");
EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
}
// 'any' is correctly parsed and transformed to gfx::Size(0,0).
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \"any AnY ANY aNy\" } ] }");
gfx::Size any = gfx::Size(0, 0);
EXPECT_EQ(manifest.icons[0].sizes.size(), 4u);
EXPECT_EQ(manifest.icons[0].sizes[0], any);
EXPECT_EQ(manifest.icons[0].sizes[1], any);
EXPECT_EQ(manifest.icons[0].sizes[2], any);
EXPECT_EQ(manifest.icons[0].sizes[3], any);
}
// Some invalid width/height combinations.
{
Manifest manifest = ParseManifest("{ \"icons\": [ {\"src\": \"\","
"\"sizes\": \"x 40xx 1x2x3 x42 42xx42\" } ] }");
gfx::Size any = gfx::Size(0, 0);
EXPECT_EQ(manifest.icons[0].sizes.size(), 0u);
}
}
} // namespace content
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment