Commit c5743a11 authored by Jay Harris's avatar Jay Harris Committed by Commit Bot

FileHandler: Updates manifest.file_handler to match the explainer.

Explainer:
https://github.com/WICG/file-handling/blob/master/explainer.md#example

Summary of Changes:
Old way:
{
  "file_handler": {
    "action": "/files",
    "files": {
      "name": "Text",
      "accept": [ "text/plain", ".txt", ".md" ]
    }
  }
}
New way:
{
  "file_handlers": [
    {
      "action": "/files",
      "name": "Text",
      "accept": {
        "text/plain": [ ".txt", ".md" ]
      }
    }
  ]
}

Note: This change is behind the NativeFileSystemAPI and
FileHandlingAPI flags, so this change won't effect any live web APIs.

Bug: 829689
Change-Id: I85ea3abdc22f1b6c89749e051e2ce5dc592357dc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1877497
Commit-Queue: Jay Harris <harrisjay@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarMatt Giuca <mgiuca@chromium.org>
Cr-Commit-Position: refs/heads/master@{#720766}
parent 4b598cd2
...@@ -56,10 +56,10 @@ const char kIconsDirName[] = "icons"; ...@@ -56,10 +56,10 @@ const char kIconsDirName[] = "icons";
const char kScopeUrlHandlerId[] = "scope"; const char kScopeUrlHandlerId[] = "scope";
std::unique_ptr<base::DictionaryValue> CreateFileHandlersForBookmarkApp( std::unique_ptr<base::DictionaryValue> CreateFileHandlersForBookmarkApp(
const blink::Manifest::FileHandler& manifest_file_handler) { const std::vector<blink::Manifest::FileHandler>& manifest_file_handlers) {
base::Value file_handlers(base::Value::Type::DICTIONARY); base::Value file_handlers(base::Value::Type::DICTIONARY);
for (const auto& handler : manifest_file_handler.files) { for (const auto& entry : manifest_file_handlers) {
base::Value file_handler(base::Value::Type::DICTIONARY); base::Value file_handler(base::Value::Type::DICTIONARY);
file_handler.SetKey(keys::kFileHandlerIncludeDirectories, file_handler.SetKey(keys::kFileHandlerIncludeDirectories,
base::Value(false)); base::Value(false));
...@@ -69,15 +69,22 @@ std::unique_ptr<base::DictionaryValue> CreateFileHandlersForBookmarkApp( ...@@ -69,15 +69,22 @@ std::unique_ptr<base::DictionaryValue> CreateFileHandlersForBookmarkApp(
base::Value mime_types(base::Value::Type::LIST); base::Value mime_types(base::Value::Type::LIST);
base::Value file_extensions(base::Value::Type::LIST); base::Value file_extensions(base::Value::Type::LIST);
for (const auto& acceptsUTF16 : handler.accept) { for (const auto& it : entry.accept) {
std::string acceptsUTF8 = base::UTF16ToUTF8(acceptsUTF16); std::string type = base::UTF16ToUTF8(it.first);
if (acceptsUTF8.size() == 0) if (type.empty())
continue; continue;
if (acceptsUTF8[0] == '.') { mime_types.Append(base::Value(type));
file_extensions.Append(base::Value(acceptsUTF8.substr(1))); for (const auto& extensionUTF16 : it.second) {
} else { std::string extension = base::UTF16ToUTF8(extensionUTF16);
mime_types.Append(base::Value(acceptsUTF8)); if (extension.empty())
continue;
if (extension[0] != '.')
continue;
// Remove the '.' before appending.
file_extensions.Append(base::Value(extension.substr(1)));
} }
} }
...@@ -85,13 +92,7 @@ std::unique_ptr<base::DictionaryValue> CreateFileHandlersForBookmarkApp( ...@@ -85,13 +92,7 @@ std::unique_ptr<base::DictionaryValue> CreateFileHandlersForBookmarkApp(
file_handler.SetKey(keys::kFileHandlerExtensions, file_handler.SetKey(keys::kFileHandlerExtensions,
std::move(file_extensions)); std::move(file_extensions));
// Use '{action}/?name={name}' as the id for the file handler, so we don't file_handlers.SetKey(entry.action.spec(), std::move(file_handler));
// have to introduce a new field to the extension manifest.
file_handlers.SetKey(
net::AppendQueryParameter(manifest_file_handler.action, "name",
base::UTF16ToUTF8(handler.name))
.spec(),
std::move(file_handler));
} }
return base::DictionaryValue::From( return base::DictionaryValue::From(
...@@ -215,9 +216,9 @@ scoped_refptr<Extension> ConvertWebAppToExtension( ...@@ -215,9 +216,9 @@ scoped_refptr<Extension> ConvertWebAppToExtension(
root->SetString(keys::kAppDisplayMode, root->SetString(keys::kAppDisplayMode,
blink::DisplayModeToString(web_app.display_mode)); blink::DisplayModeToString(web_app.display_mode));
if (web_app.file_handler) { if (web_app.file_handlers.size() != 0) {
root->SetDictionary(keys::kFileHandlers, CreateFileHandlersForBookmarkApp( root->SetDictionary(keys::kFileHandlers, CreateFileHandlersForBookmarkApp(
web_app.file_handler.value())); web_app.file_handlers));
} }
// Add the icons and linked icon information. // Add the icons and linked icon information.
......
...@@ -424,24 +424,21 @@ TEST(ExtensionFromWebApp, FileHandlersAreCorrectlyConverted) { ...@@ -424,24 +424,21 @@ TEST(ExtensionFromWebApp, FileHandlersAreCorrectlyConverted) {
web_app.scope = GURL("https://graphr.n/"); web_app.scope = GURL("https://graphr.n/");
{ {
blink::Manifest::FileHandler file_handler; blink::Manifest::FileHandler graph;
file_handler.action = GURL("https://graphr.n/open-file/"); graph.action = GURL("https://graphr.n/open-graph/");
blink::Manifest::FileFilter graph;
graph.name = base::ASCIIToUTF16("Graph"); graph.name = base::ASCIIToUTF16("Graph");
graph.accept.push_back(base::ASCIIToUTF16("text/svg+xml")); graph.accept[base::ASCIIToUTF16("text/svg+xml")].push_back(
graph.accept.push_back(base::ASCIIToUTF16("")); base::ASCIIToUTF16(""));
graph.accept.push_back(base::ASCIIToUTF16(".svg")); graph.accept[base::ASCIIToUTF16("text/svg+xml")].push_back(
file_handler.files.push_back(graph); base::ASCIIToUTF16(".svg"));
web_app.file_handlers.push_back(graph);
blink::Manifest::FileFilter raw;
blink::Manifest::FileHandler raw;
raw.action = GURL("https://graphr.n/open-raw/");
raw.name = base::ASCIIToUTF16("Raw"); raw.name = base::ASCIIToUTF16("Raw");
raw.accept.push_back(base::ASCIIToUTF16(".csv")); raw.accept[base::ASCIIToUTF16("text/csv")].push_back(
raw.accept.push_back(base::ASCIIToUTF16("text/csv")); base::ASCIIToUTF16(".csv"));
file_handler.files.push_back(raw); web_app.file_handlers.push_back(raw);
web_app.file_handler =
base::Optional<blink::Manifest::FileHandler>(std::move(file_handler));
} }
scoped_refptr<Extension> extension = ConvertWebAppToExtension( scoped_refptr<Extension> extension = ConvertWebAppToExtension(
...@@ -455,7 +452,7 @@ TEST(ExtensionFromWebApp, FileHandlersAreCorrectlyConverted) { ...@@ -455,7 +452,7 @@ TEST(ExtensionFromWebApp, FileHandlersAreCorrectlyConverted) {
EXPECT_EQ(2u, file_handler_info.size()); EXPECT_EQ(2u, file_handler_info.size());
EXPECT_EQ("https://graphr.n/open-file/?name=Graph", file_handler_info[0].id); EXPECT_EQ("https://graphr.n/open-graph/", file_handler_info[0].id);
EXPECT_FALSE(file_handler_info[0].include_directories); EXPECT_FALSE(file_handler_info[0].include_directories);
EXPECT_EQ(apps::file_handler_verbs::kOpenWith, file_handler_info[0].verb); EXPECT_EQ(apps::file_handler_verbs::kOpenWith, file_handler_info[0].verb);
// Extensions should contain SVG, and only SVG // Extensions should contain SVG, and only SVG
...@@ -465,7 +462,7 @@ TEST(ExtensionFromWebApp, FileHandlersAreCorrectlyConverted) { ...@@ -465,7 +462,7 @@ TEST(ExtensionFromWebApp, FileHandlersAreCorrectlyConverted) {
EXPECT_THAT(file_handler_info[0].types, EXPECT_THAT(file_handler_info[0].types,
testing::UnorderedElementsAre("text/svg+xml")); testing::UnorderedElementsAre("text/svg+xml"));
EXPECT_EQ("https://graphr.n/open-file/?name=Raw", file_handler_info[1].id); EXPECT_EQ("https://graphr.n/open-raw/", file_handler_info[1].id);
EXPECT_FALSE(file_handler_info[1].include_directories); EXPECT_FALSE(file_handler_info[1].include_directories);
EXPECT_EQ(apps::file_handler_verbs::kOpenWith, file_handler_info[1].verb); EXPECT_EQ(apps::file_handler_verbs::kOpenWith, file_handler_info[1].verb);
// Extensions should contain csv, and only csv // Extensions should contain csv, and only csv
......
...@@ -49,17 +49,13 @@ class WebAppFileHandlingBrowserTest ...@@ -49,17 +49,13 @@ class WebAppFileHandlingBrowserTest
web_app_info->app_url = url; web_app_info->app_url = url;
web_app_info->scope = url.GetWithoutFilename(); web_app_info->scope = url.GetWithoutFilename();
web_app_info->title = base::ASCIIToUTF16("A Hosted App"); web_app_info->title = base::ASCIIToUTF16("A Hosted App");
web_app_info->file_handler = blink::Manifest::FileHandler();
web_app_info->file_handler->action = GetFileHandlerActionURL(); blink::Manifest::FileHandler entry;
entry.action = GetFileHandlerActionURL();
{ entry.name = base::ASCIIToUTF16("text");
std::vector<blink::Manifest::FileFilter> filters; entry.accept[base::ASCIIToUTF16("text/*")].push_back(
blink::Manifest::FileFilter text = { base::ASCIIToUTF16(".txt"));
base::ASCIIToUTF16("text"), web_app_info->file_handlers.push_back(std::move(entry));
{base::ASCIIToUTF16(".txt"), base::ASCIIToUTF16("text/*")}};
filters.push_back(text);
web_app_info->file_handler->files = std::move(filters);
}
return WebAppControllerBrowserTest::InstallWebApp(std::move(web_app_info)); return WebAppControllerBrowserTest::InstallWebApp(std::move(web_app_info));
} }
......
...@@ -148,8 +148,7 @@ void UpdateWebAppInfoFromManifest(const blink::Manifest& manifest, ...@@ -148,8 +148,7 @@ void UpdateWebAppInfoFromManifest(const blink::Manifest& manifest,
if (!web_app_icons.empty()) if (!web_app_icons.empty())
web_app_info->icons = std::move(web_app_icons); web_app_info->icons = std::move(web_app_icons);
// Copy across the file handler info. web_app_info->file_handlers = manifest.file_handlers;
web_app_info->file_handler = manifest.file_handler;
} }
std::vector<GURL> GetValidIconUrlsToDownload( std::vector<GURL> GetValidIconUrlsToDownload(
......
...@@ -38,14 +38,12 @@ TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifest) { ...@@ -38,14 +38,12 @@ TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifest) {
base::NullableString16(base::UTF8ToUTF16(kAppShortName), false); base::NullableString16(base::UTF8ToUTF16(kAppShortName), false);
{ {
blink::Manifest::FileHandler file_handler; blink::Manifest::FileHandler handler;
file_handler.action = GURL("http://example.com/open-files"); handler.action = GURL("http://example.com/open-files");
blink::Manifest::FileFilter file; handler.accept[base::UTF8ToUTF16("image/png")].push_back(
file.accept.push_back(base::UTF8ToUTF16(".png")); base::UTF8ToUTF16(".png"));
file.name = base::UTF8ToUTF16("Images"); handler.name = base::UTF8ToUTF16("Images");
file_handler.files.push_back(file); manifest.file_handlers.push_back(handler);
manifest.file_handler =
base::Optional<blink::Manifest::FileHandler>(std::move(file_handler));
} }
UpdateWebAppInfoFromManifest(manifest, &web_app_info, UpdateWebAppInfoFromManifest(manifest, &web_app_info,
...@@ -85,13 +83,13 @@ TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifest) { ...@@ -85,13 +83,13 @@ TEST(WebAppInstallUtils, UpdateWebAppInfoFromManifest) {
EXPECT_EQ(kAppIcon3, web_app_info.icons[1].url); EXPECT_EQ(kAppIcon3, web_app_info.icons[1].url);
// Check file handlers were updated // Check file handlers were updated
EXPECT_TRUE(web_app_info.file_handler.has_value()); EXPECT_EQ(1u, web_app_info.file_handlers.size());
auto file_handler = web_app_info.file_handler.value(); auto file_handler = web_app_info.file_handlers;
EXPECT_EQ(manifest.file_handler->action, file_handler.action); EXPECT_EQ(manifest.file_handlers[0].action, file_handler[0].action);
EXPECT_EQ(1u, file_handler.files.size()); ASSERT_EQ(file_handler[0].accept.count(base::UTF8ToUTF16("image/png")), 1u);
EXPECT_EQ(base::UTF8ToUTF16("Images"), file_handler.files[0].name); EXPECT_EQ(file_handler[0].accept[base::UTF8ToUTF16("image/png")][0],
EXPECT_EQ(1u, file_handler.files[0].accept.size()); base::UTF8ToUTF16(".png"));
EXPECT_EQ(base::UTF8ToUTF16(".png"), file_handler.files[0].accept[0]); EXPECT_EQ(file_handler[0].name, base::UTF8ToUTF16("Images"));
} }
// Tests "scope" is only set for installable sites. // Tests "scope" is only set for installable sites.
......
...@@ -76,7 +76,7 @@ struct WebApplicationInfo { ...@@ -76,7 +76,7 @@ struct WebApplicationInfo {
bool open_as_window; bool open_as_window;
// The extensions and mime types the app can handle. // The extensions and mime types the app can handle.
base::Optional<blink::Manifest::FileHandler> file_handler; std::vector<blink::Manifest::FileHandler> file_handlers;
}; };
#endif // CHROME_COMMON_WEB_APPLICATION_INFO_H_ #endif // CHROME_COMMON_WEB_APPLICATION_INFO_H_
...@@ -69,7 +69,7 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read( ...@@ -69,7 +69,7 @@ bool StructTraits<blink::mojom::ManifestDataView, ::blink::Manifest>::Read(
if (!data.ReadShareTarget(&out->share_target)) if (!data.ReadShareTarget(&out->share_target))
return false; return false;
if (!data.ReadFileHandler(&out->file_handler)) if (!data.ReadFileHandlers(&out->file_handlers))
return false; return false;
if (!data.ReadRelatedApplications(&out->related_applications)) if (!data.ReadRelatedApplications(&out->related_applications))
...@@ -204,7 +204,13 @@ bool StructTraits<blink::mojom::ManifestFileHandlerDataView, ...@@ -204,7 +204,13 @@ bool StructTraits<blink::mojom::ManifestFileHandlerDataView,
if (!data.ReadAction(&out->action)) if (!data.ReadAction(&out->action))
return false; return false;
return data.ReadFiles(&out->files); if (!data.ReadName(&out->name))
return false;
if (!data.ReadAccept(&out->accept))
return false;
return true;
} }
} // namespace mojo } // namespace mojo
...@@ -109,7 +109,8 @@ struct BLINK_COMMON_EXPORT Manifest { ...@@ -109,7 +109,8 @@ struct BLINK_COMMON_EXPORT Manifest {
struct BLINK_COMMON_EXPORT FileHandler { struct BLINK_COMMON_EXPORT FileHandler {
// The URL which will be opened when the file handler is invoked. // The URL which will be opened when the file handler is invoked.
GURL action; GURL action;
std::vector<FileFilter> files; base::string16 name;
std::map<base::string16, std::vector<base::string16>> accept;
}; };
// Structure representing a related application. // Structure representing a related application.
...@@ -164,11 +165,11 @@ struct BLINK_COMMON_EXPORT Manifest { ...@@ -164,11 +165,11 @@ struct BLINK_COMMON_EXPORT Manifest {
// Null if parsing failed or the field was not present. // Null if parsing failed or the field was not present.
base::Optional<ShareTarget> share_target; base::Optional<ShareTarget> share_target;
// Null if parsing failed or the field was not present. // Empty if parsing failed or the field was not present.
// TODO(harrisjay): This field is non-standard and part of a Chrome // TODO(harrisjay): This field is non-standard and part of a Chrome
// experiment. See: // experiment. See:
// https://github.com/WICG/file-handling/blob/master/explainer.md // https://github.com/WICG/file-handling/blob/master/explainer.md
base::Optional<FileHandler> file_handler; std::vector<FileHandler> file_handlers;
// Empty if the parsing failed, the field was not present, empty or all the // Empty if the parsing failed, the field was not present, empty or all the
// applications inside the array were invalid. The order of the array // applications inside the array were invalid. The order of the array
......
...@@ -102,9 +102,9 @@ struct BLINK_COMMON_EXPORT ...@@ -102,9 +102,9 @@ struct BLINK_COMMON_EXPORT
return manifest.share_target; return manifest.share_target;
} }
static const base::Optional<::blink::Manifest::FileHandler>& file_handler( static const std::vector<::blink::Manifest::FileHandler>& file_handlers(
const ::blink::Manifest& manifest) { const ::blink::Manifest& manifest) {
return manifest.file_handler; return manifest.file_handlers;
} }
static const std::vector<::blink::Manifest::RelatedApplication>& static const std::vector<::blink::Manifest::RelatedApplication>&
...@@ -244,14 +244,20 @@ template <> ...@@ -244,14 +244,20 @@ template <>
struct BLINK_COMMON_EXPORT struct BLINK_COMMON_EXPORT
StructTraits<blink::mojom::ManifestFileHandlerDataView, StructTraits<blink::mojom::ManifestFileHandlerDataView,
::blink::Manifest::FileHandler> { ::blink::Manifest::FileHandler> {
static const GURL& action( static const GURL& action(const ::blink::Manifest::FileHandler& entry) {
const ::blink::Manifest::FileHandler& file_handler) { return entry.action;
return file_handler.action;
} }
static const std::vector<::blink::Manifest::FileFilter>& files(
const ::blink::Manifest::FileHandler& file_handler) { static const base::string16& name(
return file_handler.files; const ::blink::Manifest::FileHandler& entry) {
return entry.name;
} }
static const std::map<base::string16, std::vector<base::string16>>& accept(
const ::blink::Manifest::FileHandler& entry) {
return entry.accept;
}
static bool Read(blink::mojom::ManifestFileHandlerDataView data, static bool Read(blink::mojom::ManifestFileHandlerDataView data,
::blink::Manifest::FileHandler* out); ::blink::Manifest::FileHandler* out);
}; };
......
...@@ -34,7 +34,7 @@ struct Manifest { ...@@ -34,7 +34,7 @@ struct Manifest {
// experiment. See: // experiment. See:
// https://github.com/WICG/file-handling/blob/master/explainer.md // https://github.com/WICG/file-handling/blob/master/explainer.md
// As such, this field should not be exposed to the drive-by web. // As such, this field should not be exposed to the drive-by web.
ManifestFileHandler? file_handler; array<ManifestFileHandler> file_handlers;
array<ManifestRelatedApplication> related_applications; array<ManifestRelatedApplication> related_applications;
...@@ -152,7 +152,9 @@ struct ManifestShareTarget { ...@@ -152,7 +152,9 @@ struct ManifestShareTarget {
struct ManifestFileHandler { struct ManifestFileHandler {
// The URL that will be opened when the file handler is invoked. // The URL that will be opened when the file handler is invoked.
url.mojom.Url action; url.mojom.Url action;
array<ManifestFileFilter> files; mojo_base.mojom.String16? name;
// A mapping of a MIME types to a corresponding list of file extensions.
map<mojo_base.mojom.String16, array<mojo_base.mojom.String16>> accept;
}; };
// Debug information for a parsed manifest. // Debug information for a parsed manifest.
......
...@@ -81,9 +81,7 @@ void ManifestParser::Parse() { ...@@ -81,9 +81,7 @@ void ManifestParser::Parse() {
if (share_target.has_value()) if (share_target.has_value())
manifest_->share_target = std::move(*share_target); manifest_->share_target = std::move(*share_target);
auto file_handler = ParseFileHandler(root_object.get()); manifest_->file_handlers = ParseFileHandlers(root_object.get());
if (file_handler.has_value())
manifest_->file_handler = std::move(*file_handler);
manifest_->related_applications = ParseRelatedApplications(root_object.get()); manifest_->related_applications = ParseRelatedApplications(root_object.get());
manifest_->prefer_related_applications = manifest_->prefer_related_applications =
...@@ -627,30 +625,118 @@ ManifestParser::ParseShareTarget(const JSONObject* object) { ...@@ -627,30 +625,118 @@ ManifestParser::ParseShareTarget(const JSONObject* object) {
return share_target; return share_target;
} }
Vector<mojom::blink::ManifestFileHandlerPtr> ManifestParser::ParseFileHandlers(
const JSONObject* object) {
Vector<mojom::blink::ManifestFileHandlerPtr> result;
if (!object->Get("file_handlers"))
return result;
JSONArray* entry_array = object->GetArray("file_handlers");
if (!entry_array) {
AddErrorInfo("property 'file_handlers' ignored, type array expected.");
return result;
}
for (wtf_size_t i = 0; i < entry_array->size(); ++i) {
JSONObject* json_entry = JSONObject::Cast(entry_array->at(i));
if (!json_entry) {
AddErrorInfo("FileHandler ignored, type object expected.");
continue;
}
base::Optional<mojom::blink::ManifestFileHandlerPtr> entry =
ParseFileHandler(json_entry);
if (!entry)
continue;
result.push_back(std::move(entry.value()));
}
return result;
}
base::Optional<mojom::blink::ManifestFileHandlerPtr> base::Optional<mojom::blink::ManifestFileHandlerPtr>
ManifestParser::ParseFileHandler(const JSONObject* object) { ManifestParser::ParseFileHandler(const JSONObject* file_handler) {
const JSONObject* file_handler_object = object->GetJSONObject("file_handler"); mojom::blink::ManifestFileHandlerPtr entry =
if (!file_handler_object) mojom::blink::ManifestFileHandler::New();
entry->action = ParseURL(file_handler, "action", manifest_url_,
ParseURLOriginRestrictions::kSameOriginOnly);
if (!entry->action.IsValid()) {
AddErrorInfo("FileHandler ignored. Property 'action' is invalid.");
return base::nullopt; return base::nullopt;
}
auto file_handler = mojom::blink::ManifestFileHandler::New(); entry->name = ParseString(file_handler, "name", Trim).value_or("");
file_handler->action = ParseURL(file_handler_object, "action", manifest_url_,
ParseURLOriginRestrictions::kSameOriginOnly); entry->accept = ParseFileHandlerAccept(file_handler->GetJSONObject("accept"));
if (!file_handler->action.IsValid()) { if (entry->accept.IsEmpty()) {
AddErrorInfo( AddErrorInfo("FileHandler ignored. Property 'accept' is invalid.");
"property 'file_handler' ignored. Property 'action' is "
"invalid.");
return base::nullopt; return base::nullopt;
} }
file_handler->files = ParseTargetFiles("files", file_handler_object); return entry;
}
if (file_handler->files.size() == 0) { HashMap<String, Vector<String>> ManifestParser::ParseFileHandlerAccept(
AddErrorInfo("no file handlers were specified."); const JSONObject* accept) {
return base::nullopt; HashMap<String, Vector<String>> result;
if (!accept)
return result;
for (wtf_size_t i = 0; i < accept->size(); ++i) {
JSONObject::Entry entry = accept->at(i);
String& mimetype = entry.first;
Vector<String> extensions;
String extension;
JSONArray* extensions_array = JSONArray::Cast(entry.second);
if (extensions_array) {
for (wtf_size_t j = 0; j < extensions_array->size(); ++j) {
JSONValue* value = extensions_array->at(j);
if (!value->AsString(&extension)) {
AddErrorInfo(
"property 'accept' file extension ignored, type string "
"expected.");
continue;
}
if (!ParseFileHandlerAcceptExtension(value, &extension)) {
// Errors are added by ParseFileHandlerAcceptExtension.
continue;
}
extensions.push_back(extension);
}
} else if (ParseFileHandlerAcceptExtension(entry.second, &extension)) {
extensions.push_back(extension);
} else {
// Parsing errors will already have been added.
continue;
}
result.Set(mimetype, std::move(extensions));
} }
return file_handler; return result;
}
bool ManifestParser::ParseFileHandlerAcceptExtension(const JSONValue* extension,
String* output) {
if (!extension->AsString(output)) {
AddErrorInfo(
"property 'accept' type ignored. File extensions must be type array or "
"type string.");
return false;
}
if (!output->StartsWith(".")) {
AddErrorInfo(
"property 'accept' file extension ignored, must start with a '.'.");
return false;
}
return true;
} }
String ManifestParser::ParseRelatedApplicationPlatform( String ManifestParser::ParseRelatedApplicationPlatform(
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/graphics/color.h" #include "third_party/blink/renderer/platform/graphics/color.h"
#include "third_party/blink/renderer/platform/json/json_values.h" #include "third_party/blink/renderer/platform/json/json_values.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "third_party/blink/renderer/platform/wtf/vector.h" #include "third_party/blink/renderer/platform/wtf/vector.h"
...@@ -196,13 +197,34 @@ class MODULES_EXPORT ManifestParser { ...@@ -196,13 +197,34 @@ class MODULES_EXPORT ManifestParser {
base::Optional<mojom::blink::ManifestShareTargetPtr> ParseShareTarget( base::Optional<mojom::blink::ManifestShareTargetPtr> ParseShareTarget(
const JSONObject* object); const JSONObject* object);
// Parses the 'file_handler' field of a Manifest, as defined in: // Parses the 'file_handlers' field of a Manifest, as defined in:
// https://github.com/WICG/file-handling/blob/master/explainer.md // https://github.com/WICG/file-handling/blob/master/explainer.md
// Returns the parsed file handler information. The returned FileHandler is // Returns the parsed list of FileHandlers. The returned FileHandlers are
// null if the field didn't exist, parsing failed, or it was empty. // empty if the field didn't exist, parsing failed, or the input list was
base::Optional<mojom::blink::ManifestFileHandlerPtr> ParseFileHandler( // empty.
Vector<mojom::blink::ManifestFileHandlerPtr> ParseFileHandlers(
const JSONObject* object); const JSONObject* object);
// Parses a FileHandler from an entry in the 'file_handlers' list, as
// defined in: https://github.com/WICG/file-handling/blob/master/explainer.md.
// Returns |base::nullopt| if the FileHandler was invalid, or a
// FileHandler, if parsing succeeded.
base::Optional<mojom::blink::ManifestFileHandlerPtr> ParseFileHandler(
const JSONObject* file_handler_entry);
// Parses the 'accept' field of a FileHandler, as defined in:
// https://github.com/WICG/file-handling/blob/master/explainer.md.
// Returns the parsed accept map. Invalid accept entries are ignored.
HashMap<String, Vector<String>> ParseFileHandlerAccept(
const JSONObject* accept);
// Parses an extension in the 'accept' field of a FileHandler, as defined in:
// https://github.com/WICG/file-handling/blob/master/explainer.md. Returns
// whether the parsing was successful and, if so, populates |output| with the
// parsed extension.
bool ParseFileHandlerAcceptExtension(const JSONValue* extension,
String* ouput);
// Parses the 'platform' field of a related application, as defined in: // Parses the 'platform' field of a related application, as defined in:
// https://w3c.github.io/manifest/#dfn-steps-for-processing-the-platform-member-of-an-application // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-platform-member-of-an-application
// 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.
......
...@@ -45,12 +45,8 @@ TypeConverter<blink::Manifest, blink::mojom::blink::ManifestPtr>::Convert( ...@@ -45,12 +45,8 @@ TypeConverter<blink::Manifest, blink::mojom::blink::ManifestPtr>::Convert(
input->share_target.To<blink::Manifest::ShareTarget>(); input->share_target.To<blink::Manifest::ShareTarget>();
} }
if (!input->file_handler.is_null()) { for (auto& entry : input->file_handlers) {
blink::Manifest::FileHandler file_handler; output.file_handlers.push_back(entry.To<blink::Manifest::FileHandler>());
file_handler.action = input->file_handler->action;
for (auto& file : input->file_handler->files)
file_handler.files.push_back(file.To<blink::Manifest::FileFilter>());
output.file_handler = std::move(file_handler);
} }
for (auto& related_application : input->related_applications) { for (auto& related_application : input->related_applications) {
...@@ -179,6 +175,25 @@ TypeConverter<blink::Manifest::FileFilter, ...@@ -179,6 +175,25 @@ TypeConverter<blink::Manifest::FileFilter,
return output; return output;
} }
blink::Manifest::FileHandler
TypeConverter<blink::Manifest::FileHandler,
blink::mojom::blink::ManifestFileHandlerPtr>::
Convert(const blink::mojom::blink::ManifestFileHandlerPtr& input) {
blink::Manifest::FileHandler output;
if (input.is_null())
return output;
output.name = blink::WebString(input->name).Utf16();
output.action = input->action;
for (const auto& it : input->accept) {
auto& extensions = output.accept[blink::WebString(it.key).Utf16()];
for (const auto& extension : it.value)
extensions.push_back(blink::WebString(extension).Utf16());
}
return output;
}
blink::Manifest::RelatedApplication blink::Manifest::RelatedApplication
TypeConverter<blink::Manifest::RelatedApplication, TypeConverter<blink::Manifest::RelatedApplication,
blink::mojom::blink::ManifestRelatedApplicationPtr>:: blink::mojom::blink::ManifestRelatedApplicationPtr>::
......
...@@ -52,6 +52,13 @@ struct TypeConverter<blink::Manifest::FileFilter, ...@@ -52,6 +52,13 @@ struct TypeConverter<blink::Manifest::FileFilter,
const blink::mojom::blink::ManifestFileFilterPtr& input); const blink::mojom::blink::ManifestFileFilterPtr& input);
}; };
template <>
struct TypeConverter<blink::Manifest::FileHandler,
blink::mojom::blink::ManifestFileHandlerPtr> {
static blink::Manifest::FileHandler Convert(
const blink::mojom::blink::ManifestFileHandlerPtr& input);
};
template <> template <>
struct TypeConverter<blink::Manifest::RelatedApplication, struct TypeConverter<blink::Manifest::RelatedApplication,
blink::mojom::blink::ManifestRelatedApplicationPtr> { blink::mojom::blink::ManifestRelatedApplicationPtr> {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "third_party/blink/renderer/modules/manifest/manifest_type_converters.h" #include "third_party/blink/renderer/modules/manifest/manifest_type_converters.h"
#include "base/strings/utf_string_conversions.h"
#include "mojo/public/cpp/bindings/type_converter.h" #include "mojo/public/cpp/bindings/type_converter.h"
#include "third_party/blink/public/mojom/manifest/manifest.mojom-blink.h" #include "third_party/blink/public/mojom/manifest/manifest.mojom-blink.h"
#include "third_party/blink/renderer/modules/manifest/manifest_parser.h" #include "third_party/blink/renderer/modules/manifest/manifest_parser.h"
...@@ -39,31 +40,35 @@ TEST_F(ManifestTypeConvertersTest, NoFileHandlerDoesNotConvert) { ...@@ -39,31 +40,35 @@ TEST_F(ManifestTypeConvertersTest, NoFileHandlerDoesNotConvert) {
const mojom::blink::ManifestPtr& mojo_manifest = Load(json); const mojom::blink::ManifestPtr& mojo_manifest = Load(json);
auto manifest = mojo_manifest.To<blink::Manifest>(); auto manifest = mojo_manifest.To<blink::Manifest>();
EXPECT_FALSE(manifest.file_handler.has_value()); EXPECT_EQ(0u, manifest.file_handlers.size());
} }
TEST_F(ManifestTypeConvertersTest, BasicFileHandlerIsCorrectlyConverted) { TEST_F(ManifestTypeConvertersTest, BasicFileHandlerIsCorrectlyConverted) {
const mojom::blink::ManifestPtr& mojo_manifest = Load( const mojom::blink::ManifestPtr& mojo_manifest = Load(
"{" "{"
" \"file_handler\": {" " \"file_handlers\": ["
" \"files\": [" " {"
" {" " \"name\": \"name\","
" \"name\": \"name\", " " \"action\": \"/files\","
" \"accept\": \"image/png\"" " \"accept\": {"
" \"image/png\": ["
" \".png\""
" ]"
" }" " }"
" ], " " }"
" \"action\": \"/files\"" " ]"
" }"
"}"); "}");
auto manifest = mojo_manifest.To<blink::Manifest>(); auto manifest = mojo_manifest.To<blink::Manifest>();
ASSERT_TRUE(manifest.file_handler.has_value()); ASSERT_EQ(manifest.file_handlers.size(), 1u);
EXPECT_EQ(manifest.file_handlers[0].action, "http://example.com/files");
EXPECT_TRUE(base::EqualsASCII(manifest.file_handlers[0].name, "name"));
ASSERT_EQ(manifest.file_handlers[0].accept.size(), 1u);
EXPECT_EQ(manifest.file_handler->action, "http://example.com/files"); base::string16 mime = base::UTF8ToUTF16("image/png");
ASSERT_EQ(manifest.file_handler->files.size(), 1u); ASSERT_EQ(manifest.file_handlers[0].accept.count(mime), 1u);
EXPECT_TRUE(base::EqualsASCII(manifest.file_handler->files[0].name, "name")); EXPECT_EQ(manifest.file_handlers[0].accept[mime].size(), 1u);
ASSERT_EQ(manifest.file_handler->files[0].accept.size(), 1u); EXPECT_TRUE(
EXPECT_TRUE(base::EqualsASCII(manifest.file_handler->files[0].accept[0], base::EqualsASCII(manifest.file_handlers[0].accept[mime][0], ".png"));
"image/png"));
} }
} // namespace blink } // namespace blink
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