Commit dfea211a authored by Eric Willigers's avatar Eric Willigers Committed by Commit Bot

Web Share Target: Store share target in web app database

We now record the ShareTarget manifest members in the database.
https://w3c.github.io/web-share-target/level-2/#sharetarget-and-its-members

This CL has no behavior changes outside the database.

Bug: 1125880
Change-Id: I630b4942357bfe26025497a134c56ca560e98943
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2396056
Commit-Queue: Eric Willigers <ericwilligers@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarAlan Cutter <alancutter@chromium.org>
Cr-Commit-Position: refs/heads/master@{#804846}
parent 4b66d724
......@@ -3860,6 +3860,7 @@ static_library("browser") {
"//components/schema_org:extractor",
"//components/services/app_service:lib",
"//components/services/app_service/public/cpp:app_file_handling",
"//components/services/app_service/public/cpp:app_share_target",
"//components/services/app_service/public/cpp:app_update",
"//components/services/app_service/public/cpp:icon_loader",
"//components/services/app_service/public/cpp:intents",
......
......@@ -2612,6 +2612,7 @@ static_library("ui") {
"//components/login",
"//components/metrics/structured:structured_events",
"//components/services/app_service/public/cpp:app_file_handling",
"//components/services/app_service/public/cpp:app_share_target",
"//components/services/app_service/public/cpp:app_update",
"//components/services/app_service/public/cpp:icon_loader",
"//components/services/app_service/public/cpp:instance_update",
......
......@@ -117,6 +117,9 @@ source_set("components") {
]
}
public_deps =
[ "//components/services/app_service/public/cpp:app_share_target" ]
deps = [
"//chrome/app/resources:platform_locale_settings",
"//chrome/app/theme:chrome_unscaled_resources",
......
......@@ -6,6 +6,9 @@ import("//third_party/protobuf/proto_library.gni")
proto_library("proto") {
proto_in_dir = "//"
sources = [ "web_app.proto" ]
sources = [
"web_app.proto",
"web_app_share_target.proto",
]
link_deps = [ "//components/sync/protocol" ]
}
......@@ -5,6 +5,7 @@
syntax = "proto2";
import "components/sync/protocol/web_app_specifics.proto";
import "chrome/browser/web_applications/proto/web_app_share_target.proto";
option optimize_for = LITE_RUNTIME;
......@@ -154,4 +155,6 @@ message WebAppProto {
// User preference: Run on OS Login mode for web app.
// If present, the web app is configured to run on OS login.
optional RunOnOsLoginMode user_run_on_os_login_mode = 24;
optional ShareTarget share_target = 25;
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package web_app;
// The following three messages are shared with
// //chrome/browser/android/webapk/webapk.proto
// A proto representing a ShareTargetParamsFile
// https://w3c.github.io/web-share-target/level-2/#sharetargetfiles-and-its-members
message ShareTargetParamsFile {
optional string name = 1;
repeated string accept = 2;
}
// A proto representing ShareTargetParams
// https://w3c.github.io/web-share-target/#dom-sharetargetparams
// Each field corresponds to key in ShareData. These are the query parameter
// keys to be used for the data supplied in a ShareData instance.
// ShareData: https://w3c.github.io/web-share#dom-sharedata
message ShareTargetParams {
optional string title = 1;
optional string text = 2;
optional string url = 3;
repeated ShareTargetParamsFile files = 4;
}
// A proto representing a ShareTarget.
// https://w3c.github.io/web-share-target/#dom-sharetarget
message ShareTarget {
// The URL to be resolved when sharing.
optional string action = 2;
optional ShareTargetParams params = 3;
optional string method = 4;
optional string enctype = 5;
reserved 1;
}
......@@ -225,6 +225,10 @@ void WebApp::SetFileHandlers(apps::FileHandlers file_handlers) {
file_handlers_ = std::move(file_handlers);
}
void WebApp::SetShareTarget(base::Optional<apps::ShareTarget> share_target) {
share_target_ = std::move(share_target);
}
void WebApp::SetAdditionalSearchTerms(
std::vector<std::string> additional_search_terms) {
additional_search_terms_ = std::move(additional_search_terms);
......@@ -322,6 +326,8 @@ std::ostream& operator<<(std::ostream& out, const WebApp& app) {
out << " downloaded_icon_sizes_maskable_: " << size << std::endl;
for (const apps::FileHandler& file_handler : app.file_handlers_)
out << " file_handler: " << file_handler << std::endl;
if (app.share_target_)
out << " share_target: " << *app.share_target_ << std::endl;
for (const std::string& additional_search_term : app.additional_search_terms_)
out << " additional_search_term: " << additional_search_term << std::endl;
for (const apps::ProtocolHandlerInfo& protocol_handler :
......@@ -359,10 +365,10 @@ bool operator==(const WebApp& app1, const WebApp& app2) {
app1.user_display_mode_, app1.user_page_ordinal_,
app1.user_launch_ordinal_, app1.chromeos_data_,
app1.is_locally_installed_, app1.is_in_sync_install_,
app1.file_handlers_, app1.additional_search_terms_,
app1.protocol_handlers_, app1.sync_fallback_data_,
app1.last_launch_time_, app1.install_time_,
app1.run_on_os_login_mode_) ==
app1.file_handlers_, app1.share_target_,
app1.additional_search_terms_, app1.protocol_handlers_,
app1.sync_fallback_data_, app1.last_launch_time_,
app1.install_time_, app1.run_on_os_login_mode_) ==
std::tie(app2.app_id_, app2.sources_, app2.name_, app2.start_url_,
app2.description_, app2.scope_, app2.theme_color_,
app2.background_color_, app2.icon_infos_,
......@@ -372,10 +378,10 @@ bool operator==(const WebApp& app1, const WebApp& app2) {
app2.user_display_mode_, app2.user_page_ordinal_,
app2.user_launch_ordinal_, app2.chromeos_data_,
app2.is_locally_installed_, app2.is_in_sync_install_,
app2.file_handlers_, app2.additional_search_terms_,
app2.protocol_handlers_, app2.sync_fallback_data_,
app2.last_launch_time_, app2.install_time_,
app2.run_on_os_login_mode_);
app2.file_handlers_, app2.share_target_,
app2.additional_search_terms_, app2.protocol_handlers_,
app2.sync_fallback_data_, app2.last_launch_time_,
app2.install_time_, app2.run_on_os_login_mode_);
}
bool operator!=(const WebApp& app1, const WebApp& app2) {
......
......@@ -17,6 +17,7 @@
#include "chrome/common/web_application_info.h"
#include "components/services/app_service/public/cpp/file_handler.h"
#include "components/services/app_service/public/cpp/protocol_handler_info.h"
#include "components/services/app_service/public/cpp/share_target.h"
#include "components/sync/model/string_ordinal.h"
#include "third_party/skia/include/core/SkColor.h"
#include "url/gurl.h"
......@@ -103,6 +104,10 @@ class WebApp {
const apps::FileHandlers& file_handlers() const { return file_handlers_; }
const base::Optional<apps::ShareTarget>& share_target() const {
return share_target_;
}
const std::vector<std::string>& additional_search_terms() const {
return additional_search_terms_;
}
......@@ -191,6 +196,7 @@ class WebApp {
void SetDownloadedShortcutsMenuIconsSizes(
std::vector<std::vector<SquareSizePx>> icon_sizes);
void SetFileHandlers(apps::FileHandlers file_handlers);
void SetShareTarget(base::Optional<apps::ShareTarget> share_target);
void SetAdditionalSearchTerms(
std::vector<std::string> additional_search_terms);
void SetProtocolHandlers(
......@@ -238,6 +244,7 @@ class WebApp {
std::vector<WebApplicationShortcutsMenuItemInfo> shortcuts_menu_item_infos_;
std::vector<std::vector<SquareSizePx>> downloaded_shortcuts_menu_icons_sizes_;
apps::FileHandlers file_handlers_;
base::Optional<apps::ShareTarget> share_target_;
std::vector<std::string> additional_search_terms_;
std::vector<apps::ProtocolHandlerInfo> protocol_handlers_;
base::Time last_launch_time_;
......
......@@ -186,6 +186,32 @@ std::unique_ptr<WebAppProto> WebAppDatabase::CreateWebAppProto(
}
}
if (web_app.share_target()) {
const apps::ShareTarget& share_target = *web_app.share_target();
auto* const mutable_share_target = local_data->mutable_share_target();
mutable_share_target->set_action(share_target.action.spec());
mutable_share_target->set_method(
apps::ShareTarget::MethodToString(share_target.method));
mutable_share_target->set_enctype(
apps::ShareTarget::EnctypeToString(share_target.enctype));
const apps::ShareTarget::Params& params = share_target.params;
auto* const mutable_share_target_params =
mutable_share_target->mutable_params();
mutable_share_target_params->set_title(params.title);
mutable_share_target_params->set_text(params.text);
mutable_share_target_params->set_url(params.url);
for (const auto& files_entry : params.files) {
ShareTargetParamsFile* mutable_share_target_files =
mutable_share_target_params->add_files();
mutable_share_target_files->set_name(files_entry.name);
for (const auto& file_type : files_entry.accept)
mutable_share_target_files->add_accept(file_type);
}
}
for (const WebApplicationShortcutsMenuItemInfo& shortcut_info :
web_app.shortcuts_menu_item_infos()) {
WebAppShortcutsMenuItemInfoProto* shortcut_info_proto =
......@@ -423,6 +449,48 @@ std::unique_ptr<WebApp> WebAppDatabase::CreateWebApp(
}
web_app->SetFileHandlers(std::move(file_handlers));
if (local_data.has_share_target()) {
apps::ShareTarget share_target;
const ShareTarget& local_share_target = local_data.share_target();
const ShareTargetParams& local_share_target_params =
local_share_target.params();
GURL action(local_share_target.action());
if (action.is_empty() || !action.is_valid()) {
DLOG(ERROR) << "WebApp proto action parse error: "
<< action.possibly_invalid_spec();
return nullptr;
}
share_target.action = action;
share_target.method =
apps::ShareTarget::StringToMethod(local_share_target.method());
share_target.enctype =
apps::ShareTarget::StringToEnctype(local_share_target.enctype());
share_target.params.title = local_share_target_params.title();
share_target.params.text = local_share_target_params.text();
share_target.params.url = local_share_target_params.url();
for (const auto& share_target_params_file :
local_share_target_params.files()) {
apps::ShareTarget::Files files_entry;
files_entry.name = share_target_params_file.name();
for (const auto& file_type : share_target_params_file.accept()) {
if (base::Contains(files_entry.accept, file_type)) {
// We intentionally don't return a nullptr here; instead, duplicate
// entries are absorbed.
DLOG(ERROR) << "apps::ShareTarget::Files parsing encountered "
<< "duplicate file type";
} else {
files_entry.accept.push_back(file_type);
}
}
share_target.params.files.push_back(std::move(files_entry));
}
web_app->SetShareTarget(std::move(share_target));
}
std::vector<WebApplicationShortcutsMenuItemInfo> shortcuts_menu_item_infos;
for (const auto& shortcut_info_proto :
local_data.shortcuts_menu_item_infos()) {
......
......@@ -29,6 +29,7 @@
#include "chrome/browser/web_applications/web_app_sync_bridge.h"
#include "components/services/app_service/public/cpp/file_handler.h"
#include "components/services/app_service/public/cpp/protocol_handler_info.h"
#include "components/services/app_service/public/cpp/share_target.h"
#include "components/sync/model/model_type_store.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
......@@ -98,6 +99,34 @@ class WebAppDatabaseTest : public WebAppTest {
return file_handlers;
}
static apps::ShareTarget CreateShareTarget(uint32_t suffix) {
apps::ShareTarget share_target;
share_target.action =
GURL("https://example.com/path/target/" + base::NumberToString(suffix));
share_target.method = (suffix % 2 == 0) ? apps::ShareTarget::Method::kPost
: apps::ShareTarget::Method::kGet;
share_target.enctype = (suffix / 2 % 2 == 0)
? apps::ShareTarget::Enctype::kMultipartFormData
: apps::ShareTarget::Enctype::kFormUrlEncoded;
if (suffix % 3 != 0)
share_target.params.title = "title" + base::NumberToString(suffix);
if (suffix % 3 != 1)
share_target.params.text = "text" + base::NumberToString(suffix);
if (suffix % 3 != 2)
share_target.params.url = "url" + base::NumberToString(suffix);
for (uint32_t index = 0; index < suffix % 5; ++index) {
apps::ShareTarget::Files files;
files.name = "files" + base::NumberToString(index);
files.accept.push_back(".extension" + base::NumberToString(index));
files.accept.push_back("type/subtype" + base::NumberToString(index));
share_target.params.files.push_back(files);
}
return share_target;
}
static std::vector<apps::ProtocolHandlerInfo> CreateProtocolHandlers(
uint32_t suffix) {
std::vector<apps::ProtocolHandlerInfo> protocol_handlers;
......@@ -248,6 +277,8 @@ class WebAppDatabaseTest : public WebAppTest {
app->SetIsGeneratedIcon(random.next_bool());
app->SetFileHandlers(CreateFileHandlers(random.next_uint()));
if (random.next_bool())
app->SetShareTarget(CreateShareTarget(random.next_uint()));
app->SetProtocolHandlers(CreateProtocolHandlers(random.next_uint()));
const int num_additional_search_terms = random.next_uint(8);
......@@ -348,7 +379,7 @@ TEST_F(WebAppDatabaseTest, WriteAndReadRegistry) {
controller().Init();
EXPECT_TRUE(registrar().is_empty());
const int num_apps = 100;
const int num_apps = 20;
const std::string base_url = "https://example.com/path";
auto app = CreateWebApp(base_url, 0);
......@@ -429,7 +460,7 @@ TEST_F(WebAppDatabaseTest, WriteAndDeleteAppsWithCallbacks) {
}
TEST_F(WebAppDatabaseTest, OpenDatabaseAndReadRegistry) {
Registry registry = WriteWebApps("https://example.com/path", 100);
Registry registry = WriteWebApps("https://example.com/path", 20);
controller().Init();
EXPECT_TRUE(IsRegistryEqual(mutable_registrar().registry(), registry));
......@@ -539,6 +570,7 @@ TEST_F(WebAppDatabaseTest, WebAppWithoutOptionalFields) {
EXPECT_FALSE(app->sync_fallback_data().scope.is_valid());
EXPECT_TRUE(app->sync_fallback_data().icon_infos.empty());
EXPECT_TRUE(app->file_handlers().empty());
EXPECT_FALSE(app->share_target().has_value());
EXPECT_TRUE(app->additional_search_terms().empty());
EXPECT_TRUE(app->protocol_handlers().empty());
EXPECT_TRUE(app->last_launch_time().is_null());
......@@ -595,6 +627,7 @@ TEST_F(WebAppDatabaseTest, WebAppWithoutOptionalFields) {
EXPECT_FALSE(app_copy->sync_fallback_data().scope.is_valid());
EXPECT_TRUE(app_copy->sync_fallback_data().icon_infos.empty());
EXPECT_TRUE(app_copy->file_handlers().empty());
EXPECT_FALSE(app_copy->share_target().has_value());
EXPECT_TRUE(app_copy->additional_search_terms().empty());
EXPECT_TRUE(app_copy->protocol_handlers().empty());
EXPECT_TRUE(app_copy->shortcuts_menu_item_infos().empty());
......@@ -677,4 +710,26 @@ TEST_F(WebAppDatabaseTest, WebAppWithFileHandlersRoundTrip) {
EXPECT_TRUE(IsRegistryEqual(mutable_registrar().registry(), registry));
}
TEST_F(WebAppDatabaseTest, WebAppWithShareTargetRoundTrip) {
controller().Init();
const std::string base_url = "https://example.com/path";
auto app = CreateWebApp(base_url, 0);
auto app_id = app->app_id();
apps::ShareTarget share_target;
share_target.action = GURL("https://example.com/path/target");
share_target.method = apps::ShareTarget::Method::kPost;
share_target.enctype = apps::ShareTarget::Enctype::kMultipartFormData;
share_target.params.title = "Title";
share_target.params.text = "Text";
share_target.params.url = "Url";
app->SetShareTarget(std::move(share_target));
controller().RegisterApp(std::move(app));
Registry registry = database_factory().ReadRegistry();
EXPECT_TRUE(IsRegistryEqual(mutable_registrar().registry(), registry));
}
} // namespace web_app
......@@ -16,6 +16,18 @@ source_set("app_file_handling") {
]
}
source_set("app_share_target") {
sources = [
"share_target.cc",
"share_target.h",
]
deps = [
"//base",
"//url",
]
}
component("app_update") {
output_name = "APP_UPDATE"
sources = [
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/services/app_service/public/cpp/share_target.h"
#include <tuple>
#include "base/strings/string_util.h"
namespace apps {
ShareTarget::Files::Files() = default;
ShareTarget::Files::Files(const ShareTarget::Files&) = default;
ShareTarget::Files::Files(ShareTarget::Files&&) = default;
ShareTarget::Files& ShareTarget::Files::operator=(const ShareTarget::Files&) =
default;
ShareTarget::Files& ShareTarget::Files::operator=(ShareTarget::Files&&) =
default;
ShareTarget::Files::~Files() = default;
ShareTarget::Params::Params() = default;
ShareTarget::Params::Params(const ShareTarget::Params&) = default;
ShareTarget::Params::Params(ShareTarget::Params&&) = default;
ShareTarget::Params& ShareTarget::Params::operator=(
const ShareTarget::Params&) = default;
ShareTarget::Params& ShareTarget::Params::operator=(ShareTarget::Params&&) =
default;
ShareTarget::Params::~Params() = default;
ShareTarget::ShareTarget() = default;
ShareTarget::ShareTarget(const ShareTarget&) = default;
ShareTarget::ShareTarget(ShareTarget&&) = default;
ShareTarget& ShareTarget::operator=(const ShareTarget&) = default;
ShareTarget& ShareTarget::operator=(ShareTarget&&) = default;
ShareTarget::~ShareTarget() = default;
// static
const char* ShareTarget::MethodToString(ShareTarget::Method method) {
switch (method) {
case Method::kGet:
return "GET";
case Method::kPost:
return "POST";
}
}
// static
ShareTarget::Method ShareTarget::StringToMethod(
const base::StringPiece& method) {
return base::EqualsCaseInsensitiveASCII(method, "POST") ? Method::kPost
: Method::kGet;
}
// static
const char* ShareTarget::EnctypeToString(ShareTarget::Enctype enctype) {
switch (enctype) {
case Enctype::kFormUrlEncoded:
return "application/x-www-form-urlencoded";
case Enctype::kMultipartFormData:
return "multipart/form-data";
}
}
// static
ShareTarget::Enctype ShareTarget::StringToEnctype(
const base::StringPiece& enctype) {
return base::EqualsCaseInsensitiveASCII(enctype, "multipart/form-data")
? Enctype::kMultipartFormData
: Enctype::kFormUrlEncoded;
}
std::ostream& operator<<(std::ostream& out, const ShareTarget& share_target) {
out << "action: " << share_target.action << std::endl;
out << "method: " << ShareTarget::MethodToString(share_target.method)
<< std::endl;
out << "enctype: " << ShareTarget::EnctypeToString(share_target.enctype)
<< std::endl;
out << share_target.params;
return out;
}
std::ostream& operator<<(std::ostream& out, const ShareTarget::Params& params) {
out << "title: " << params.title << std::endl;
out << "text: " << params.text << std::endl;
out << "url: " << params.url << std::endl;
for (const auto& files_entry : params.files)
out << files_entry;
return out;
}
std::ostream& operator<<(std::ostream& out, const ShareTarget::Files& files) {
out << "name: " << files.name << std::endl;
for (const auto& accept_entry : files.accept)
out << "accept: " << accept_entry << std::endl;
return out;
}
bool operator==(const ShareTarget& share_target1,
const ShareTarget& share_target2) {
return std::tie(share_target1.action, share_target1.method,
share_target1.enctype, share_target1.params) ==
std::tie(share_target2.action, share_target2.method,
share_target2.enctype, share_target2.params);
}
bool operator==(const ShareTarget::Params& params1,
const ShareTarget::Params& params2) {
return std::tie(params1.title, params1.text, params1.url, params1.files) ==
std::tie(params2.title, params2.text, params2.url, params2.files);
}
bool operator==(const ShareTarget::Files& files1,
const ShareTarget::Files& files2) {
return std::tie(files1.name, files1.accept) ==
std::tie(files2.name, files2.accept);
}
bool operator!=(const ShareTarget& share_target1,
const ShareTarget& share_target2) {
return !(share_target1 == share_target2);
}
bool operator!=(const ShareTarget::Params& params1,
const ShareTarget::Params& params2) {
return !(params1 == params2);
}
bool operator!=(const ShareTarget::Files& files1,
const ShareTarget::Files& files2) {
return !(files1 == files2);
}
} // namespace apps
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_SHARE_TARGET_H_
#define COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_SHARE_TARGET_H_
#include <ostream>
#include <string>
#include "base/strings/string_piece.h"
#include "url/gurl.h"
namespace apps {
// https://w3c.github.io/web-share-target/level-2/#sharetarget-and-its-members
// https://w3c.github.io/web-share-target/level-2/#sharetargetfiles-and-its-members
// https://w3c.github.io/web-share-target/level-2/#sharetargetparams-and-its-members
struct ShareTarget {
enum class Method {
kGet,
kPost,
};
enum class Enctype {
kFormUrlEncoded,
kMultipartFormData,
};
struct Files {
Files();
Files(const Files&);
Files(Files&&);
Files& operator=(const Files&);
Files& operator=(Files&&);
~Files();
std::string name;
std::vector<std::string> accept;
};
struct Params {
Params();
Params(const Params&);
Params(Params&&);
Params& operator=(const Params&);
Params& operator=(Params&&);
~Params();
std::string title;
std::string text;
std::string url;
std::vector<Files> files;
};
ShareTarget();
ShareTarget(const ShareTarget&);
ShareTarget(ShareTarget&&);
ShareTarget& operator=(const ShareTarget&);
ShareTarget& operator=(ShareTarget&&);
~ShareTarget();
static const char* MethodToString(Method);
static Method StringToMethod(const base::StringPiece&);
static const char* EnctypeToString(Enctype);
static Enctype StringToEnctype(const base::StringPiece&);
GURL action;
Method method = Method::kGet;
Enctype enctype = Enctype::kFormUrlEncoded;
Params params;
};
// For logging and debug purposes.
std::ostream& operator<<(std::ostream& out, const ShareTarget& share_target);
std::ostream& operator<<(std::ostream& out, const ShareTarget::Params& params);
std::ostream& operator<<(std::ostream& out, const ShareTarget::Files& files);
bool operator==(const ShareTarget& share_target1,
const ShareTarget& share_target2);
bool operator==(const ShareTarget::Params& params1,
const ShareTarget::Params& params2);
bool operator==(const ShareTarget::Files& files1,
const ShareTarget::Files& files2);
bool operator!=(const ShareTarget& share_target1,
const ShareTarget& share_target2);
bool operator!=(const ShareTarget::Params& params1,
const ShareTarget::Params& params2);
bool operator!=(const ShareTarget::Files& files1,
const ShareTarget::Files& files2);
} // namespace apps
#endif // COMPONENTS_SERVICES_APP_SERVICE_PUBLIC_CPP_SHARE_TARGET_H_
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