Commit 2cfa55e2 authored by constantina's avatar constantina Committed by Commit bot

Default share to Share Target: Partial impl. of Web Share for Desktop.

Currently only shares to a demo Share Target App hosted at
https://wicg.github.io/web-share-target/demos/sharetarget.html;
follow-up CL will have more share options.

BUG=668389

Review-Url: https://codereview.chromium.org/2564483003
Cr-Commit-Position: refs/heads/master@{#443510}
parent 88398012
......@@ -4,8 +4,44 @@
#include "chrome/browser/webshare/share_service_impl.h"
#include "base/memory/ptr_util.h"
#include <algorithm>
#include <functional>
#include <utility>
#include "base/strings/string_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/base/escape.h"
namespace {
// Determines whether a character is allowed in a URL template placeholder.
bool IsIdentifier(char c) {
return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) || c == '-' || c == '_';
}
// Joins a std::vector<base::StringPiece> into a single std::string.
// TODO(constantina): Implement a base::JoinString() that takes StringPieces.
// i.e. move this to base/strings/string_util.h, and thoroughly test.
std::string JoinString(const std::vector<base::StringPiece>& pieces) {
size_t total_size = 0;
for (const auto& piece : pieces) {
total_size += piece.size();
}
std::string joined_pieces;
joined_pieces.reserve(total_size);
for (const auto& piece : pieces) {
piece.AppendToString(&joined_pieces);
}
return joined_pieces;
}
} // namespace
// static
void ShareServiceImpl::Create(blink::mojom::ShareServiceRequest request) {
......@@ -13,11 +49,101 @@ void ShareServiceImpl::Create(blink::mojom::ShareServiceRequest request) {
std::move(request));
}
// static
bool ShareServiceImpl::ReplacePlaceholders(base::StringPiece url_template,
base::StringPiece title,
base::StringPiece text,
const GURL& share_url,
std::string* url_template_filled) {
constexpr char kTitlePlaceholder[] = "title";
constexpr char kTextPlaceholder[] = "text";
constexpr char kUrlPlaceholder[] = "url";
std::map<base::StringPiece, std::string> placeholder_to_data;
placeholder_to_data[kTitlePlaceholder] =
net::EscapeQueryParamValue(title, false);
placeholder_to_data[kTextPlaceholder] =
net::EscapeQueryParamValue(text, false);
placeholder_to_data[kUrlPlaceholder] =
net::EscapeQueryParamValue(share_url.spec(), false);
std::vector<base::StringPiece> split_template;
bool last_saw_open = false;
size_t start_index_to_copy = 0;
for (size_t i = 0; i < url_template.size(); ++i) {
if (last_saw_open) {
if (url_template[i] == '}') {
base::StringPiece placeholder = url_template.substr(
start_index_to_copy + 1, i - 1 - start_index_to_copy);
auto it = placeholder_to_data.find(placeholder);
if (it != placeholder_to_data.end()) {
// Replace the placeholder text with the parameter value.
split_template.push_back(it->second);
}
last_saw_open = false;
start_index_to_copy = i + 1;
} else if (!IsIdentifier(url_template[i])) {
// Error: Non-identifier character seen after open.
return false;
}
} else {
if (url_template[i] == '}') {
// Error: Saw close, with no corresponding open.
return false;
} else if (url_template[i] == '{') {
split_template.push_back(
url_template.substr(start_index_to_copy, i - start_index_to_copy));
last_saw_open = true;
start_index_to_copy = i;
}
}
}
if (last_saw_open) {
// Error: Saw open that was never closed.
return false;
}
split_template.push_back(url_template.substr(
start_index_to_copy, url_template.size() - start_index_to_copy));
*url_template_filled = JoinString(split_template);
return true;
}
void ShareServiceImpl::OpenTargetURL(const GURL& target_url) {
// TODO(constantina): Prevent this code from being run/compiled in android.
#if defined(OS_LINUX) || defined(OS_WIN)
Browser* browser = BrowserList::GetInstance()->GetLastActive();
chrome::AddTabAt(browser, target_url,
browser->tab_strip_model()->active_index() + 1, true);
#endif
}
void ShareServiceImpl::Share(const std::string& title,
const std::string& text,
const GURL& url,
const GURL& share_url,
const ShareCallback& callback) {
// TODO(constantina): Implement Web Share Target here.
NOTIMPLEMENTED();
callback.Run(base::Optional<std::string>("Not implemented: navigator.share"));
// TODO(constantina): replace hard-coded URL with one from user-chosen site.
constexpr char kUrlBase[] = "https://wicg.github.io/web-share-target/";
constexpr char kUrlTemplate[] =
"demos/sharetarget.html?title={title}&text={text}&url={url}";
std::string url_template_filled;
if (!ReplacePlaceholders(kUrlTemplate, title, text, share_url,
&url_template_filled)) {
callback.Run(base::Optional<std::string>(
"Error: unable to replace placeholders in url template"));
return;
}
GURL target_url(kUrlBase + url_template_filled);
if (!target_url.is_valid()) {
callback.Run(base::Optional<std::string>(
"Error: url of share target is not a valid url."));
return;
}
OpenTargetURL(target_url);
callback.Run(base::nullopt);
}
......@@ -7,6 +7,7 @@
#include <string>
#include "base/gtest_prod_util.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "third_party/WebKit/public/platform/modules/webshare/webshare.mojom.h"
......@@ -20,12 +21,33 @@ class ShareServiceImpl : public blink::mojom::ShareService {
static void Create(mojo::InterfaceRequest<ShareService> request);
// blink::mojom::ShareService overrides:
void Share(const std::string& title,
const std::string& text,
const GURL& url,
const GURL& share_url,
const ShareCallback& callback) override;
private:
FRIEND_TEST_ALL_PREFIXES(ShareServiceImplUnittest, ReplacePlaceholders);
// Opens a new tab and navigates to |target_url|.
// Virtual for testing purposes.
virtual void OpenTargetURL(const GURL& target_url);
// Writes to |url_template_filled|, a copy of |url_template| with all
// instances of "{title}", "{text}", and "{url}" replaced with
// |title|, |text|, and |url| respectively.
// Replaces instances of "{X}" where "X" is any string besides "title",
// "text", and "url", with an empty string, for forwards compatibility.
// Returns false, if there are badly nested placeholders.
// This includes any case in which two "{" occur before a "}", or a "}"
// occurs with no preceding "{".
static bool ReplacePlaceholders(base::StringPiece url_template,
base::StringPiece title,
base::StringPiece text,
const GURL& share_url,
std::string* url_template_filled);
DISALLOW_COPY_AND_ASSIGN(ShareServiceImpl);
};
......
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