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}
diff --git a/content/common/manifest_manager_messages.h b/content/common/manifest_manager_messages.h
index a447de9..be80405 100644
--- a/content/common/manifest_manager_messages.h
+++ b/content/common/manifest_manager_messages.h
@@ -21,6 +21,7 @@
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)
diff --git a/content/public/common/manifest.h b/content/public/common/manifest.h
index 949c610..957f007 100644
--- a/content/public/common/manifest.h
+++ b/content/public/common/manifest.h
@@ -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 @@
// 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;
};
diff --git a/content/renderer/manifest/manifest_parser.cc b/content/renderer/manifest/manifest_parser.cc
index 3f2f9893..dc1624a 100644
--- a/content/renderer/manifest/manifest_parser.cc
+++ b/content/renderer/manifest/manifest_parser.cc
@@ -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 @@
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 @@
continue;
icon.type = ParseIconType(*icon_dictionary);
icon.density = ParseIconDensity(*icon_dictionary);
- // TODO(mlamouri): icon.sizes
+ icon.sizes = ParseIconSizes(*icon_dictionary);
icons.push_back(icon);
}
diff --git a/content/renderer/manifest/manifest_parser_unittest.cc b/content/renderer/manifest/manifest_parser_unittest.cc
index c2ce8da7..32730f7 100644
--- a/content/renderer/manifest/manifest_parser_unittest.cc
+++ b/content/renderer/manifest/manifest_parser_unittest.cc
@@ -492,4 +492,92 @@
}
}
+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