Commit dcbfc072 authored by Finnur Thorarinsson's avatar Finnur Thorarinsson Committed by Commit Bot

Add webapp manifest parsing code for three new fields.

New fields: 'description', 'categories' and 'screenshots'.

Bug: 1146450
Change-Id: I8d104ed2ccc38e67cc84c873026b6f24a82a75b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2523178
Commit-Queue: Finnur Thorarinsson <finnur@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarRayan Kanso <rayankans@chromium.org>
Cr-Commit-Position: refs/heads/master@{#826234}
parent 1263b2d4
...@@ -55,6 +55,13 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read( ...@@ -55,6 +55,13 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read(
return false; return false;
out->short_name = std::move(string.string); out->short_name = std::move(string.string);
if (!data.ReadDescription(&string))
return false;
out->description = std::move(string.string);
if (!data.ReadCategories(&out->categories))
return false;
if (!data.ReadGcmSenderId(&string)) if (!data.ReadGcmSenderId(&string))
return false; return false;
out->gcm_sender_id = std::move(string.string); out->gcm_sender_id = std::move(string.string);
...@@ -65,6 +72,9 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read( ...@@ -65,6 +72,9 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read(
if (!data.ReadIcons(&out->icons)) if (!data.ReadIcons(&out->icons))
return false; return false;
if (!data.ReadScreenshots(&out->screenshots))
return false;
if (!data.ReadShortcuts(&out->shortcuts)) if (!data.ReadShortcuts(&out->shortcuts))
return false; return false;
......
...@@ -154,6 +154,13 @@ struct BLINK_COMMON_EXPORT Manifest { ...@@ -154,6 +154,13 @@ struct BLINK_COMMON_EXPORT Manifest {
// Null if the parsing failed or the field was not present. // Null if the parsing failed or the field was not present.
base::Optional<base::string16> short_name; base::Optional<base::string16> short_name;
// Null if the parsing failed or the field was not present.
base::Optional<base::string16> description;
// Empty if the parsing failed, the field was not present, or all the
// values inside the JSON array were invalid.
std::vector<base::string16> categories;
// Empty if the parsing failed or the field was not present. // Empty if the parsing failed or the field was not present.
GURL start_url; GURL start_url;
...@@ -174,6 +181,10 @@ struct BLINK_COMMON_EXPORT Manifest { ...@@ -174,6 +181,10 @@ struct BLINK_COMMON_EXPORT Manifest {
// icons inside the JSON array were invalid. // icons inside the JSON array were invalid.
std::vector<ImageResource> icons; std::vector<ImageResource> icons;
// Empty if the parsing failed, the field was not present, or all the
// screenshots inside the JSON array were invalid.
std::vector<ImageResource> screenshots;
// Empty if the parsing failed, the field was not present, or all the // Empty if the parsing failed, the field was not present, or all the
// icons inside the JSON array were invalid. // icons inside the JSON array were invalid.
std::vector<ShortcutItem> shortcuts; std::vector<ShortcutItem> shortcuts;
......
...@@ -55,6 +55,16 @@ struct BLINK_COMMON_EXPORT ...@@ -55,6 +55,16 @@ struct BLINK_COMMON_EXPORT
return internal::TruncateOptionalString16(manifest.short_name); return internal::TruncateOptionalString16(manifest.short_name);
} }
static base::Optional<base::StringPiece16> description(
const ::blink::Manifest& manifest) {
return internal::TruncateOptionalString16(manifest.description);
}
static const std::vector<base::string16>& categories(
const ::blink::Manifest& manifest) {
return manifest.categories;
}
static base::Optional<base::StringPiece16> gcm_sender_id( static base::Optional<base::StringPiece16> gcm_sender_id(
const ::blink::Manifest& manifest) { const ::blink::Manifest& manifest) {
return internal::TruncateOptionalString16(manifest.gcm_sender_id); return internal::TruncateOptionalString16(manifest.gcm_sender_id);
...@@ -103,6 +113,11 @@ struct BLINK_COMMON_EXPORT ...@@ -103,6 +113,11 @@ struct BLINK_COMMON_EXPORT
return manifest.icons; return manifest.icons;
} }
static const std::vector<::blink::Manifest::ImageResource>& screenshots(
const ::blink::Manifest& manifest) {
return manifest.screenshots;
}
static const std::vector<::blink::Manifest::ShortcutItem>& shortcuts( static const std::vector<::blink::Manifest::ShortcutItem>& shortcuts(
const ::blink::Manifest& manifest) { const ::blink::Manifest& manifest) {
return manifest.shortcuts; return manifest.shortcuts;
......
...@@ -21,6 +21,10 @@ struct Manifest { ...@@ -21,6 +21,10 @@ struct Manifest {
mojo_base.mojom.String16? short_name; mojo_base.mojom.String16? short_name;
mojo_base.mojom.String16? description;
array<mojo_base.mojom.String16> categories;
url.mojom.Url start_url; url.mojom.Url start_url;
DisplayMode display; DisplayMode display;
...@@ -31,6 +35,8 @@ struct Manifest { ...@@ -31,6 +35,8 @@ struct Manifest {
array<ManifestImageResource> icons; array<ManifestImageResource> icons;
array<ManifestImageResource> screenshots;
array<ManifestShortcutItem> shortcuts; array<ManifestShortcutItem> shortcuts;
ManifestShareTarget? share_target; ManifestShareTarget? share_target;
......
...@@ -82,12 +82,15 @@ void ManifestParser::Parse() { ...@@ -82,12 +82,15 @@ void ManifestParser::Parse() {
manifest_->name = ParseName(root_object.get()); manifest_->name = ParseName(root_object.get());
manifest_->short_name = ParseShortName(root_object.get()); manifest_->short_name = ParseShortName(root_object.get());
manifest_->description = ParseDescription(root_object.get());
manifest_->categories = ParseCategories(root_object.get());
manifest_->start_url = ParseStartURL(root_object.get()); manifest_->start_url = ParseStartURL(root_object.get());
manifest_->scope = ParseScope(root_object.get(), manifest_->start_url); manifest_->scope = ParseScope(root_object.get(), manifest_->start_url);
manifest_->display = ParseDisplay(root_object.get()); manifest_->display = ParseDisplay(root_object.get());
manifest_->display_override = ParseDisplayOverride(root_object.get()); manifest_->display_override = ParseDisplayOverride(root_object.get());
manifest_->orientation = ParseOrientation(root_object.get()); manifest_->orientation = ParseOrientation(root_object.get());
manifest_->icons = ParseIcons(root_object.get()); manifest_->icons = ParseIcons(root_object.get());
manifest_->screenshots = ParseScreenshots(root_object.get());
auto share_target = ParseShareTarget(root_object.get()); auto share_target = ParseShareTarget(root_object.get());
if (share_target.has_value()) if (share_target.has_value())
...@@ -267,6 +270,33 @@ String ManifestParser::ParseShortName(const JSONObject* object) { ...@@ -267,6 +270,33 @@ String ManifestParser::ParseShortName(const JSONObject* object) {
return short_name.has_value() ? *short_name : String(); return short_name.has_value() ? *short_name : String();
} }
String ManifestParser::ParseDescription(const JSONObject* object) {
base::Optional<String> description = ParseString(object, "description", Trim);
return description.has_value() ? *description : String();
}
Vector<String> ManifestParser::ParseCategories(const JSONObject* object) {
Vector<String> categories;
JSONValue* json_value = object->Get("categories");
if (!json_value)
return categories;
JSONArray* categories_list = object->GetArray("categories");
if (!categories_list) {
AddErrorInfo("property 'categories' ignored, type array expected.");
return categories;
}
for (wtf_size_t i = 0; i < categories_list->size(); ++i) {
String category_string;
categories_list->at(i)->AsString(&category_string);
categories.push_back(category_string.StripWhiteSpace().LowerASCII());
}
return categories;
}
KURL ManifestParser::ParseStartURL(const JSONObject* object) { KURL ManifestParser::ParseStartURL(const JSONObject* object) {
return ParseURL(object, "start_url", manifest_url_, return ParseURL(object, "start_url", manifest_url_,
ParseURLRestrictions::kSameOriginOnly); ParseURLRestrictions::kSameOriginOnly);
...@@ -457,14 +487,25 @@ ManifestParser::ParseIconPurpose(const JSONObject* icon) { ...@@ -457,14 +487,25 @@ ManifestParser::ParseIconPurpose(const JSONObject* icon) {
Vector<mojom::blink::ManifestImageResourcePtr> ManifestParser::ParseIcons( Vector<mojom::blink::ManifestImageResourcePtr> ManifestParser::ParseIcons(
const JSONObject* object) { const JSONObject* object) {
return ParseImageResource("icons", object);
}
Vector<mojom::blink::ManifestImageResourcePtr> ManifestParser::ParseScreenshots(
const JSONObject* object) {
return ParseImageResource("screenshots", object);
}
Vector<mojom::blink::ManifestImageResourcePtr>
ManifestParser::ParseImageResource(const String& key,
const JSONObject* object) {
Vector<mojom::blink::ManifestImageResourcePtr> icons; Vector<mojom::blink::ManifestImageResourcePtr> icons;
JSONValue* json_value = object->Get("icons"); JSONValue* json_value = object->Get(key);
if (!json_value) if (!json_value)
return icons; return icons;
JSONArray* icons_list = object->GetArray("icons"); JSONArray* icons_list = object->GetArray(key);
if (!icons_list) { if (!icons_list) {
AddErrorInfo("property 'icons' ignored, type array expected."); AddErrorInfo("property '" + key + "' ignored, type array expected.");
return icons; return icons;
} }
......
...@@ -114,6 +114,17 @@ class MODULES_EXPORT ManifestParser { ...@@ -114,6 +114,17 @@ class MODULES_EXPORT ManifestParser {
// Returns the parsed string if any, a null string if the parsing failed. // Returns the parsed string if any, a null string if the parsing failed.
String ParseShortName(const JSONObject* object); String ParseShortName(const JSONObject* object);
// Parses the 'description' field of the manifest, as defined in:
// https://w3c.github.io/manifest/#description-member-0
// Returns the parsed string if any, a null string if the parsing failed.
String ParseDescription(const JSONObject* object);
// Parses the 'categories' field of the manifest, as defined in:
// https://w3c.github.io/manifest/#dfn-processing-the-categories-member
// Returns a vector of the parsed strings if any, or empty if the parsing
// failed.
Vector<String> ParseCategories(const JSONObject* object);
// Parses the 'scope' field of the manifest, as defined in: // Parses the 'scope' field of the manifest, as defined in:
// https://w3c.github.io/manifest/#scope-member. Returns the parsed KURL if // https://w3c.github.io/manifest/#scope-member. Returns the parsed KURL if
// any, or start URL (falling back to document URL) without filename, path, // any, or start URL (falling back to document URL) without filename, path,
...@@ -177,6 +188,18 @@ class MODULES_EXPORT ManifestParser { ...@@ -177,6 +188,18 @@ class MODULES_EXPORT ManifestParser {
Vector<mojom::blink::ManifestImageResourcePtr> ParseIcons( Vector<mojom::blink::ManifestImageResourcePtr> ParseIcons(
const JSONObject* object); const JSONObject* object);
// Parses the 'screenshots' field of a Manifest, as defined in:
// https://w3c.github.io/manifest/#screenshots-member
// Returns a vector of ManifestImageResourcePtr with the successfully parsed
// screenshots, if any. An empty vector if the field was not present or empty.
Vector<mojom::blink::ManifestImageResourcePtr> ParseScreenshots(
const JSONObject* object);
// A helper function for parsing ImageResources under |key| in the manifest.
Vector<mojom::blink::ManifestImageResourcePtr> ParseImageResource(
const String& key,
const JSONObject* object);
// Parses the 'name' field of a shortcut, as defined in: // Parses the 'name' field of a shortcut, as defined in:
// https://w3c.github.io/manifest/#shortcuts-member // https://w3c.github.io/manifest/#shortcuts-member
// Returns the parsed string if any, a null string if the parsing failed. // Returns the parsed string if any, a null string if the parsing failed.
......
...@@ -171,6 +171,89 @@ TEST_F(ManifestParserTest, NameParseRules) { ...@@ -171,6 +171,89 @@ TEST_F(ManifestParserTest, NameParseRules) {
} }
} }
TEST_F(ManifestParserTest, DescriptionParseRules) {
// Smoke test.
{
auto& manifest =
ParseManifest(R"({ "description": "foo is the new black" })");
ASSERT_EQ(manifest->description, "foo is the new black");
ASSERT_FALSE(IsManifestEmpty(manifest));
EXPECT_EQ(0u, GetErrorCount());
}
// Trim whitespaces.
{
auto& manifest = ParseManifest(R"({ "description": " foo " })");
ASSERT_EQ(manifest->description, "foo");
EXPECT_EQ(0u, GetErrorCount());
}
// Don't parse if description isn't a string.
{
auto& manifest = ParseManifest(R"({ "description": {} })");
ASSERT_TRUE(manifest->description.IsNull());
ASSERT_EQ(1u, GetErrorCount());
EXPECT_EQ("property 'description' ignored, type string expected.",
errors()[0]);
}
// Don't parse if description isn't a string.
{
auto& manifest = ParseManifest(R"({ "description": 42 })");
ASSERT_TRUE(manifest->description.IsNull());
ASSERT_EQ(1u, GetErrorCount());
EXPECT_EQ("property 'description' ignored, type string expected.",
errors()[0]);
}
}
TEST_F(ManifestParserTest, CategoriesParseRules) {
// Smoke test.
{
auto& manifest = ParseManifest(R"({ "categories": ["cats", "memes"] })");
ASSERT_EQ(2u, manifest->categories.size());
ASSERT_EQ(manifest->categories[0], "cats");
ASSERT_EQ(manifest->categories[1], "memes");
ASSERT_FALSE(IsManifestEmpty(manifest));
EXPECT_EQ(0u, GetErrorCount());
}
// Trim whitespaces.
{
auto& manifest =
ParseManifest(R"({ "categories": [" cats ", " memes "] })");
ASSERT_EQ(2u, manifest->categories.size());
ASSERT_EQ(manifest->categories[0], "cats");
ASSERT_EQ(manifest->categories[1], "memes");
EXPECT_EQ(0u, GetErrorCount());
}
// Categories should be lower-cased.
{
auto& manifest = ParseManifest(R"({ "categories": ["CaTs", "Memes"] })");
ASSERT_EQ(2u, manifest->categories.size());
ASSERT_EQ(manifest->categories[0], "cats");
ASSERT_EQ(manifest->categories[1], "memes");
EXPECT_EQ(0u, GetErrorCount());
}
// Empty array.
{
auto& manifest = ParseManifest(R"({ "categories": [] })");
ASSERT_EQ(0u, manifest->categories.size());
EXPECT_EQ(0u, GetErrorCount());
}
// Detect error if categories isn't an array.
{
auto& manifest = ParseManifest(R"({ "categories": {} })");
ASSERT_EQ(0u, manifest->categories.size());
ASSERT_EQ(1u, GetErrorCount());
EXPECT_EQ("property 'categories' ignored, type array expected.",
errors()[0]);
}
}
TEST_F(ManifestParserTest, ShortNameParseRules) { TEST_F(ManifestParserTest, ShortNameParseRules) {
// Smoke test. // Smoke test.
{ {
...@@ -877,6 +960,55 @@ TEST_F(ManifestParserTest, IconsParseRules) { ...@@ -877,6 +960,55 @@ TEST_F(ManifestParserTest, IconsParseRules) {
} }
} }
TEST_F(ManifestParserTest, ScreenshotsParseRules) {
// Smoke test: if no screenshot, no value.
{
auto& manifest = ParseManifest(R"({ "screenshots": [] })");
EXPECT_TRUE(manifest->screenshots.IsEmpty());
EXPECT_EQ(0u, GetErrorCount());
}
// Smoke test: if empty screenshot, no value.
{
auto& manifest = ParseManifest(R"({ "screenshots": [ {} ] })");
EXPECT_TRUE(manifest->screenshots.IsEmpty());
EXPECT_EQ(0u, GetErrorCount());
}
// Smoke test: screenshot with invalid src, no value.
{
auto& manifest =
ParseManifest(R"({ "screenshots": [ { "screenshots": [] } ] })");
EXPECT_TRUE(manifest->screenshots.IsEmpty());
EXPECT_EQ(0u, GetErrorCount());
}
// Smoke test: if screenshot with empty src, it will be present in the list.
{
auto& manifest = ParseManifest(R"({ "screenshots": [ { "src": "" } ] })");
EXPECT_FALSE(manifest->screenshots.IsEmpty());
auto& screenshots = manifest->screenshots;
EXPECT_EQ(screenshots.size(), 1u);
EXPECT_EQ(screenshots[0]->src.GetString(), "http://foo.com/manifest.json");
EXPECT_FALSE(IsManifestEmpty(manifest));
EXPECT_EQ(0u, GetErrorCount());
}
// Smoke test: if one icons has valid src, it will be present in the list.
{
auto& manifest =
ParseManifest(R"({ "screenshots": [{ "src": "foo.jpg" }] })");
EXPECT_FALSE(manifest->screenshots.IsEmpty());
auto& screenshots = manifest->screenshots;
EXPECT_EQ(screenshots.size(), 1u);
EXPECT_EQ(screenshots[0]->src.GetString(), "http://foo.com/foo.jpg");
EXPECT_FALSE(IsManifestEmpty(manifest));
EXPECT_EQ(0u, GetErrorCount());
}
}
TEST_F(ManifestParserTest, IconSrcParseRules) { TEST_F(ManifestParserTest, IconSrcParseRules) {
// Smoke test. // Smoke test.
{ {
...@@ -1679,6 +1811,7 @@ TEST_F(ManifestParserTest, ShortcutIconsParseRules) { ...@@ -1679,6 +1811,7 @@ TEST_F(ManifestParserTest, ShortcutIconsParseRules) {
EXPECT_EQ(0u, GetErrorCount()); EXPECT_EQ(0u, GetErrorCount());
} }
} }
TEST_F(ManifestParserTest, FileHandlerParseRules) { TEST_F(ManifestParserTest, FileHandlerParseRules) {
// Does not contain file_handlers field. // Does not contain file_handlers field.
{ {
......
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