Commit 5168c222 authored by Ethan Xu's avatar Ethan Xu Committed by Commit Bot

[WebShareTargetV2] Added support for new share target fields: method, enctype, and fields.

This is the first CL of web share target V2.

The goal of this CL is to add support for three new fields: method, enctype, and fields.
This is in reference to the new web share target spec.
https://pr-preview.s3.amazonaws.com/ewilligers/web-share-target/pull/53.html#share_target-member

The purposes of each field are as follows:
method:  the HTTP method used for launching the web share target. Can be either GET or POST.
enctype: the encoding method used for launching a POST request web share target. Can be
         either application/x-www-form-urlencoded or multipart/form-data.
files:   a sequence of files each containing a name and an accepted sequence of mime types.
         Note that this can only be populated if the method is POST and the enctype is
         multipart/form-data.

Logic checks are performed in manifest_parser.cc to determine if the web share target
has a valid combination of fields.

Bug: 885308
Change-Id: I277fef7e1402620385cb28909a078eb09676c1fc
Reviewed-on: https://chromium-review.googlesource.com/c/1240867
Commit-Queue: Ethan Xu <xuethan@google.com>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarMatt Giuca <mgiuca@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarEric Willigers <ericwilligers@chromium.org>
Reviewed-by: default avatarGlenn Hartmann <hartmanng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609511}
parent 568ef6c3
......@@ -15,6 +15,7 @@
#include "base/values.h"
#include "content/public/common/manifest_util.h"
#include "content/renderer/manifest/manifest_uma_util.h"
#include "net/base/mime_util.h"
#include "third_party/blink/public/platform/web_icon_sizes_parser.h"
#include "third_party/blink/public/platform/web_size.h"
#include "third_party/blink/public/platform/web_string.h"
......@@ -22,6 +23,28 @@
#include "third_party/skia/include/core/SkColor.h"
#include "ui/gfx/geometry/size.h"
namespace {
bool IsValidMimeType(const std::string& mime_type) {
if (mime_type.length() > 0 && mime_type.at(0) == '.')
return true;
return net::ParseMimeTypeWithoutParameter(mime_type, nullptr, nullptr);
}
bool VerifyFiles(const std::vector<blink::Manifest::ShareTargetFile>& files) {
for (const blink::Manifest::ShareTargetFile& file : files) {
for (const base::string16& utf_accept : file.accept) {
std::string accept_type =
base::ToLowerASCII(base::UTF16ToASCII(utf_accept));
if (!IsValidMimeType(accept_type))
return false;
}
}
return true;
}
} // anonymous namespace
namespace content {
ManifestParser::ManifestParser(const base::StringPiece& data,
......@@ -342,6 +365,122 @@ std::vector<blink::Manifest::ImageResource> ManifestParser::ParseIcons(
return icons;
}
base::string16 ManifestParser::ParseShareTargetFileName(
const base::DictionaryValue& file) {
if (!file.HasKey("name")) {
AddErrorInfo("property 'name' missing.");
return base::string16();
}
base::string16 value;
if (!file.GetString("name", &value)) {
AddErrorInfo("property 'name' ignored, type string expected.");
return base::string16();
}
return value;
}
std::vector<base::string16> ManifestParser::ParseShareTargetFileAccept(
const base::DictionaryValue& dictionary) {
std::vector<base::string16> accept_types;
if (!dictionary.HasKey("accept")) {
return accept_types;
}
const base::ListValue* accept_list = nullptr;
if (!dictionary.GetList("accept", &accept_list)) {
AddErrorInfo("property 'accept' ignored, type array expected.");
accept_types.push_back(base::ASCIIToUTF16("invalid mimetype"));
return accept_types;
}
for (const base::Value& accept_value : accept_list->GetList())
accept_types.push_back(base::ASCIIToUTF16(accept_value.GetString()));
return accept_types;
}
std::vector<blink::Manifest::ShareTargetFile>
ManifestParser::ParseShareTargetFiles(
const base::DictionaryValue& share_target_params) {
std::vector<blink::Manifest::ShareTargetFile> files;
if (!share_target_params.HasKey("files"))
return files;
const base::ListValue* file_list = nullptr;
if (!share_target_params.GetList("files", &file_list)) {
AddErrorInfo("property 'files' ignored, type array expected.");
return files;
}
for (const base::Value& file_value : file_list->GetList()) {
const base::DictionaryValue* file_dictionary = nullptr;
if (!file_value.GetAsDictionary(&file_dictionary)) {
AddErrorInfo("files must be a sequence of non-empty file entires.");
continue;
}
blink::Manifest::ShareTargetFile file;
file.name = ParseShareTargetFileName(*file_dictionary);
file.accept = ParseShareTargetFileAccept(*file_dictionary);
files.push_back(file);
}
return files;
}
base::Optional<blink::Manifest::ShareTarget::Method>
ManifestParser::ParseShareTargetMethod(
const base::DictionaryValue& share_target_dict) {
if (!share_target_dict.HasKey("method")) {
AddErrorInfo(
"Method should be set to either GET or POST. It currently defaults to "
"GET.");
return base::Optional<blink::Manifest::ShareTarget::Method>(
blink::Manifest::ShareTarget::Method::kGet);
}
base::string16 value;
if (!share_target_dict.GetString("method", &value))
return base::nullopt;
std::string method = base::ToUpperASCII(base::UTF16ToASCII(value));
if (method == "GET")
return blink::Manifest::ShareTarget::Method::kGet;
if (method == "POST")
return blink::Manifest::ShareTarget::Method::kPost;
return base::nullopt;
}
base::Optional<blink::Manifest::ShareTarget::Enctype>
ManifestParser::ParseShareTargetEnctype(
const base::DictionaryValue& share_target_dict) {
if (!share_target_dict.HasKey("enctype")) {
AddErrorInfo(
"Enctype should be set to either application/x-www-form-urlencoded or "
"multipart/form-data. It currently defaults to "
"application/x-www-form-urlencoded");
return base::Optional<blink::Manifest::ShareTarget::Enctype>(
blink::Manifest::ShareTarget::Enctype::kApplication);
}
base::string16 value;
if (!share_target_dict.GetString("enctype", &value)) {
return base::nullopt;
}
std::string enctype = base::ToLowerASCII(base::UTF16ToASCII(value));
if (enctype == "application/x-www-form-urlencoded")
return base::Optional<blink::Manifest::ShareTarget::Enctype>(
blink::Manifest::ShareTarget::Enctype::kApplication);
if (enctype == "multipart/form-data")
return base::Optional<blink::Manifest::ShareTarget::Enctype>(
blink::Manifest::ShareTarget::Enctype::kMultipart);
return base::nullopt;
}
blink::Manifest::ShareTargetParams ManifestParser::ParseShareTargetParams(
const base::DictionaryValue& share_target_params) {
blink::Manifest::ShareTargetParams params;
......@@ -350,6 +489,7 @@ blink::Manifest::ShareTargetParams ManifestParser::ParseShareTargetParams(
params.text = ParseString(share_target_params, "text", Trim);
params.title = ParseString(share_target_params, "title", Trim);
params.url = ParseString(share_target_params, "url", Trim);
params.files = ParseShareTargetFiles(share_target_params);
return params;
}
......@@ -370,6 +510,11 @@ base::Optional<blink::Manifest::ShareTarget> ManifestParser::ParseShareTarget(
return base::nullopt;
}
base::Optional<blink::Manifest::ShareTarget::Method> method =
ParseShareTargetMethod(*share_target_dict);
base::Optional<blink::Manifest::ShareTarget::Enctype> enctype =
ParseShareTargetEnctype(*share_target_dict);
const base::DictionaryValue* share_target_params_dict = nullptr;
if (!share_target_dict->GetDictionary("params", &share_target_params_dict)) {
AddErrorInfo(
......@@ -380,6 +525,58 @@ base::Optional<blink::Manifest::ShareTarget> ManifestParser::ParseShareTarget(
share_target.params = ParseShareTargetParams(*share_target_params_dict);
if (method == base::nullopt) {
AddErrorInfo(
"invalid method. Allowed methods are:"
"GET and POST.");
return base::nullopt;
}
if (enctype == base::nullopt) {
AddErrorInfo(
"invalid enctype. Allowed enctypes are:"
"application/x-www-form-urlencoded and multipart/form-data.");
return base::nullopt;
}
if (method == base::Optional<blink::Manifest::ShareTarget::Method>(
blink::Manifest::ShareTarget::Method::kGet)) {
share_target.method = blink::Manifest::ShareTarget::Method::kGet;
} else {
share_target.method = blink::Manifest::ShareTarget::Method::kPost;
}
if (enctype == base::Optional<blink::Manifest::ShareTarget::Enctype>(
blink::Manifest::ShareTarget::Enctype::kMultipart)) {
share_target.enctype = blink::Manifest::ShareTarget::Enctype::kMultipart;
} else {
share_target.enctype = blink::Manifest::ShareTarget::Enctype::kApplication;
}
if (share_target.method == blink::Manifest::ShareTarget::Method::kGet) {
if (share_target.enctype ==
blink::Manifest::ShareTarget::Enctype::kMultipart) {
AddErrorInfo(
"invalid enctype for GET method. Only "
"application/x-www-form-urlencoded is allowed.");
return base::nullopt;
}
}
if (share_target.params.files.size() > 0) {
if (share_target.method != blink::Manifest::ShareTarget::Method::kPost ||
share_target.enctype !=
blink::Manifest::ShareTarget::Enctype::kMultipart) {
AddErrorInfo("files are only supported with multipart/form-data POST.");
return base::nullopt;
}
}
if (!VerifyFiles(share_target.params.files)) {
AddErrorInfo("invalid mime type inside files.");
return base::nullopt;
}
return base::Optional<blink::Manifest::ShareTarget>(share_target);
}
......
......@@ -156,6 +156,35 @@ class CONTENT_EXPORT ManifestParser {
std::vector<blink::Manifest::ImageResource> ParseIcons(
const base::DictionaryValue& dictionary);
// Parses the name field of a share target file, as defined in:
// https://github.com/WICG/web-share-target/blob/master/docs/interface.md
// Returns the parsed string if any, an empty string if the parsing failed.
base::string16 ParseShareTargetFileName(const base::DictionaryValue& file);
// Parses the accept field of a share target file, as defined in:
// https://github.com/WICG/web-share-target/blob/master/docs/interface.md
// Returns the parsed string if any, an empty string if the parsing failed.
std::vector<base::string16> ParseShareTargetFileAccept(
const base::DictionaryValue& file);
// Parses the 'files' field of a Share Target param, as defined in:
// https://github.com/WICG/web-share-target/blob/master/docs/interface.md
// Returns a parsed vector of share target files.
std::vector<blink::Manifest::ShareTargetFile> ParseShareTargetFiles(
const base::DictionaryValue& share_target_params);
// Parses the method field of a Share Target, as defined in:
// https://github.com/WICG/web-share-target/blob/master/docs/interface.md
// Returns an optional share target method enum object..
base::Optional<blink::Manifest::ShareTarget::Method> ParseShareTargetMethod(
const base::DictionaryValue& share_target_dict);
// Parses the enctype field of a Share Target, as defined in:
// https://github.com/WICG/web-share-target/blob/master/docs/interface.md
// Returns an optional share target enctype enum object.
base::Optional<blink::Manifest::ShareTarget::Enctype> ParseShareTargetEnctype(
const base::DictionaryValue& share_target_dict);
// Parses the 'params' field of a Share Target, as defined in:
// https://github.com/WICG/web-share-target/blob/master/docs/interface.md
// Returns a parsed Manifest::ShareTargetParams, not all fields need to be
......
......@@ -137,6 +137,25 @@ bool StructTraits<blink::mojom::ManifestRelatedApplicationDataView,
return !(out->url.is_empty() && out->id.is_null());
}
bool StructTraits<blink::mojom::ManifestShareTargetFileDataView,
::blink::Manifest::ShareTargetFile>::
Read(blink::mojom::ManifestShareTargetFileDataView data,
::blink::Manifest::ShareTargetFile* out) {
TruncatedString16 name;
if (!data.ReadName(&name))
return false;
if (!name.string)
return false;
out->name = *std::move(name.string);
if (!data.ReadAccept(&out->accept))
return false;
return true;
}
bool StructTraits<blink::mojom::ManifestShareTargetParamsDataView,
::blink::Manifest::ShareTargetParams>::
Read(blink::mojom::ManifestShareTargetParamsDataView data,
......@@ -154,6 +173,9 @@ bool StructTraits<blink::mojom::ManifestShareTargetParamsDataView,
return false;
out->url = base::NullableString16(std::move(string.string));
if (!data.ReadFiles(&out->files))
return false;
return true;
}
......@@ -163,6 +185,13 @@ bool StructTraits<blink::mojom::ManifestShareTargetDataView,
::blink::Manifest::ShareTarget* out) {
if (!data.ReadAction(&out->action))
return false;
if (!data.ReadMethod(&out->method))
return false;
if (!data.ReadEnctype(&out->enctype))
return false;
return data.ReadParams(&out->params);
}
......
......@@ -62,6 +62,11 @@ struct BLINK_COMMON_EXPORT Manifest {
std::vector<Purpose> purpose;
};
struct BLINK_COMMON_EXPORT ShareTargetFile {
base::string16 name;
std::vector<base::string16> accept;
};
// Structure representing a Web Share target's query parameter keys.
struct BLINK_COMMON_EXPORT ShareTargetParams {
ShareTargetParams();
......@@ -70,16 +75,34 @@ struct BLINK_COMMON_EXPORT Manifest {
base::NullableString16 title;
base::NullableString16 text;
base::NullableString16 url;
std::vector<ShareTargetFile> files;
};
// Structure representing how a Web Share target handles an incoming share.
struct BLINK_COMMON_EXPORT ShareTarget {
enum class Method {
kGet,
kPost,
};
enum class Enctype {
kApplication,
kMultipart,
};
ShareTarget();
~ShareTarget();
// The URL used for sharing. Query parameters are added to this comprised of
// keys from |params| and values from the shared data.
GURL action;
// The HTTP request method for the web share target.
Method method;
// The way that share data is encoded in "POST" request.
Enctype enctype;
ShareTargetParams params;
};
......
......@@ -165,6 +165,29 @@ struct BLINK_COMMON_EXPORT
::blink::Manifest::RelatedApplication* out);
};
template <>
struct BLINK_COMMON_EXPORT
StructTraits<blink::mojom::ManifestShareTargetFileDataView,
::blink::Manifest::ShareTargetFile> {
static base::StringPiece16 name(
const ::blink::Manifest::ShareTargetFile& share_target_file) {
return internal::TruncateString16(share_target_file.name);
}
static const std::vector<base::StringPiece16> accept(
const ::blink::Manifest::ShareTargetFile& share_target_file) {
std::vector<base::StringPiece16> accept_types;
for (const base::string16& accept_type : share_target_file.accept)
accept_types.push_back(internal::TruncateString16(accept_type));
return accept_types;
}
static bool Read(blink::mojom::ManifestShareTargetFileDataView data,
::blink::Manifest::ShareTargetFile* out);
};
template <>
struct BLINK_COMMON_EXPORT
StructTraits<blink::mojom::ManifestShareTargetParamsDataView,
......@@ -181,6 +204,11 @@ struct BLINK_COMMON_EXPORT
const ::blink::Manifest::ShareTargetParams& share_target_params) {
return internal::TruncateNullableString16(share_target_params.url);
}
static const std::vector<blink::Manifest::ShareTargetFile>& files(
const ::blink::Manifest::ShareTargetParams& share_target_params) {
return share_target_params.files;
}
static bool Read(blink::mojom::ManifestShareTargetParamsDataView data,
::blink::Manifest::ShareTargetParams* out);
};
......@@ -193,6 +221,14 @@ struct BLINK_COMMON_EXPORT
const ::blink::Manifest::ShareTarget& share_target) {
return share_target.action;
}
static ::blink::Manifest::ShareTarget::Method method(
const ::blink::Manifest::ShareTarget& share_target) {
return share_target.method;
}
static ::blink::Manifest::ShareTarget::Enctype enctype(
const ::blink::Manifest::ShareTarget& share_target) {
return share_target.enctype;
}
static const ::blink::Manifest::ShareTargetParams& params(
const ::blink::Manifest::ShareTarget& share_target) {
return share_target.params;
......@@ -236,6 +272,64 @@ struct BLINK_COMMON_EXPORT
}
};
template <>
struct BLINK_COMMON_EXPORT EnumTraits<blink::mojom::ManifestShareTarget_Method,
::blink::Manifest::ShareTarget::Method> {
static blink::mojom::ManifestShareTarget_Method ToMojom(
::blink::Manifest::ShareTarget::Method method) {
switch (method) {
case ::blink::Manifest::ShareTarget::Method::kGet:
return blink::mojom::ManifestShareTarget_Method::kGet;
case ::blink::Manifest::ShareTarget::Method::kPost:
return blink::mojom::ManifestShareTarget_Method::kPost;
}
NOTREACHED();
return blink::mojom::ManifestShareTarget_Method::kGet;
}
static bool FromMojom(blink::mojom::ManifestShareTarget_Method input,
::blink::Manifest::ShareTarget::Method* out) {
switch (input) {
case blink::mojom::ManifestShareTarget_Method::kGet:
*out = ::blink::Manifest::ShareTarget::Method::kGet;
return true;
case blink::mojom::ManifestShareTarget_Method::kPost:
*out = ::blink::Manifest::ShareTarget::Method::kPost;
return true;
}
return false;
}
};
template <>
struct BLINK_COMMON_EXPORT EnumTraits<blink::mojom::ManifestShareTarget_Enctype,
::blink::Manifest::ShareTarget::Enctype> {
static blink::mojom::ManifestShareTarget_Enctype ToMojom(
::blink::Manifest::ShareTarget::Enctype enctype) {
switch (enctype) {
case ::blink::Manifest::ShareTarget::Enctype::kApplication:
return blink::mojom::ManifestShareTarget_Enctype::kApplication;
case ::blink::Manifest::ShareTarget::Enctype::kMultipart:
return blink::mojom::ManifestShareTarget_Enctype::kMultipart;
}
NOTREACHED();
return blink::mojom::ManifestShareTarget_Enctype::kApplication;
}
static bool FromMojom(blink::mojom::ManifestShareTarget_Enctype input,
::blink::Manifest::ShareTarget::Enctype* out) {
switch (input) {
case blink::mojom::ManifestShareTarget_Enctype::kApplication:
*out = ::blink::Manifest::ShareTarget::Enctype::kApplication;
return true;
case blink::mojom::ManifestShareTarget_Enctype::kMultipart:
*out = ::blink::Manifest::ShareTarget::Enctype::kMultipart;
return true;
}
return false;
}
};
} // namespace mojo
#endif // THIRD_PARTY_BLINK_PUBLIC_COMMON_MANIFEST_MANIFEST_MOJOM_TRAITS_H_
......@@ -83,6 +83,14 @@ struct ManifestImageResource {
array<Purpose> purpose;
};
// Structure representing a share target file.
struct ManifestShareTargetFile {
mojo_base.mojom.String16? name;
// A sequence of accepted MIME types.
array<mojo_base.mojom.String16> accept;
};
// Structure representing a related application.
struct ManifestRelatedApplication {
// The platform on which the application can be found. This can be any
......@@ -106,12 +114,39 @@ struct ManifestShareTargetParams {
mojo_base.mojom.String16? title;
mojo_base.mojom.String16? text;
mojo_base.mojom.String16? url;
// An array of share target files, which is represented by two members:
// name and accept.
array<ManifestShareTargetFile>? files;
};
// Structure representing how a Web Share target handles an incoming share.
struct ManifestShareTarget {
// This enum corresponds to HTTP methods, where kGet corresponds to GET
// and kPost corresponds to POST.
enum Method {
kGet,
kPost,
};
// This enum corresponds to HTTP enctype, where kApplication corresponds
// to application/x-www-url-encoded and kMultipart corresponds to
// multipart/form-data.
enum Enctype {
kApplication,
kMultipart,
};
// The URL that will be opened when the share target is invoked.
url.mojom.Url action;
// The method that specifies the HTTP request method for web share target.
Method method;
// The enctype that specifies how share data is encoded in a POST request.
// It is ignored when method is "GET".
Enctype enctype;
ManifestShareTargetParams params;
};
......
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