blob: 6b12400c3c504f60efbcd012ffb45f0dcde34ba0 [file] [log] [blame]
constantinac8b2173b2016-12-15 05:55:511// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/webshare/share_service_impl.h"
6
constantina2cfa55e2017-01-13 06:36:557#include <algorithm>
8#include <functional>
9#include <utility>
10
11#include "base/strings/string_util.h"
12#include "chrome/browser/ui/browser.h"
13#include "chrome/browser/ui/browser_commands.h"
14#include "chrome/browser/ui/browser_list.h"
15#include "chrome/browser/ui/browser_tabstrip.h"
16#include "chrome/browser/ui/tabs/tab_strip_model.h"
constantinac8b2173b2016-12-15 05:55:5117#include "mojo/public/cpp/bindings/strong_binding.h"
constantina2cfa55e2017-01-13 06:36:5518#include "net/base/escape.h"
19
20namespace {
21
22// Determines whether a character is allowed in a URL template placeholder.
23bool IsIdentifier(char c) {
24 return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) || c == '-' || c == '_';
25}
26
27// Joins a std::vector<base::StringPiece> into a single std::string.
28// TODO(constantina): Implement a base::JoinString() that takes StringPieces.
29// i.e. move this to base/strings/string_util.h, and thoroughly test.
30std::string JoinString(const std::vector<base::StringPiece>& pieces) {
31 size_t total_size = 0;
32 for (const auto& piece : pieces) {
33 total_size += piece.size();
34 }
35 std::string joined_pieces;
36 joined_pieces.reserve(total_size);
37
38 for (const auto& piece : pieces) {
39 piece.AppendToString(&joined_pieces);
40 }
41 return joined_pieces;
42}
43
44} // namespace
constantinac8b2173b2016-12-15 05:55:5145
46// static
47void ShareServiceImpl::Create(blink::mojom::ShareServiceRequest request) {
48 mojo::MakeStrongBinding(base::MakeUnique<ShareServiceImpl>(),
49 std::move(request));
50}
51
constantina2cfa55e2017-01-13 06:36:5552// static
53bool ShareServiceImpl::ReplacePlaceholders(base::StringPiece url_template,
54 base::StringPiece title,
55 base::StringPiece text,
56 const GURL& share_url,
57 std::string* url_template_filled) {
58 constexpr char kTitlePlaceholder[] = "title";
59 constexpr char kTextPlaceholder[] = "text";
60 constexpr char kUrlPlaceholder[] = "url";
61
62 std::map<base::StringPiece, std::string> placeholder_to_data;
63 placeholder_to_data[kTitlePlaceholder] =
64 net::EscapeQueryParamValue(title, false);
65 placeholder_to_data[kTextPlaceholder] =
66 net::EscapeQueryParamValue(text, false);
67 placeholder_to_data[kUrlPlaceholder] =
68 net::EscapeQueryParamValue(share_url.spec(), false);
69
70 std::vector<base::StringPiece> split_template;
71 bool last_saw_open = false;
72 size_t start_index_to_copy = 0;
73 for (size_t i = 0; i < url_template.size(); ++i) {
74 if (last_saw_open) {
75 if (url_template[i] == '}') {
76 base::StringPiece placeholder = url_template.substr(
77 start_index_to_copy + 1, i - 1 - start_index_to_copy);
78 auto it = placeholder_to_data.find(placeholder);
79 if (it != placeholder_to_data.end()) {
80 // Replace the placeholder text with the parameter value.
81 split_template.push_back(it->second);
82 }
83
84 last_saw_open = false;
85 start_index_to_copy = i + 1;
86 } else if (!IsIdentifier(url_template[i])) {
87 // Error: Non-identifier character seen after open.
88 return false;
89 }
90 } else {
91 if (url_template[i] == '}') {
92 // Error: Saw close, with no corresponding open.
93 return false;
94 } else if (url_template[i] == '{') {
95 split_template.push_back(
96 url_template.substr(start_index_to_copy, i - start_index_to_copy));
97
98 last_saw_open = true;
99 start_index_to_copy = i;
100 }
101 }
102 }
103 if (last_saw_open) {
104 // Error: Saw open that was never closed.
105 return false;
106 }
107 split_template.push_back(url_template.substr(
108 start_index_to_copy, url_template.size() - start_index_to_copy));
109
110 *url_template_filled = JoinString(split_template);
111 return true;
112}
113
114void ShareServiceImpl::OpenTargetURL(const GURL& target_url) {
115// TODO(constantina): Prevent this code from being run/compiled in android.
116#if defined(OS_LINUX) || defined(OS_WIN)
117 Browser* browser = BrowserList::GetInstance()->GetLastActive();
118 chrome::AddTabAt(browser, target_url,
119 browser->tab_strip_model()->active_index() + 1, true);
120#endif
121}
122
constantinac8b2173b2016-12-15 05:55:51123void ShareServiceImpl::Share(const std::string& title,
124 const std::string& text,
constantina2cfa55e2017-01-13 06:36:55125 const GURL& share_url,
constantinac8b2173b2016-12-15 05:55:51126 const ShareCallback& callback) {
constantina2cfa55e2017-01-13 06:36:55127 // TODO(constantina): replace hard-coded URL with one from user-chosen site.
128 constexpr char kUrlBase[] = "https://wicg.github.io/web-share-target/";
129 constexpr char kUrlTemplate[] =
130 "demos/sharetarget.html?title={title}&text={text}&url={url}";
131
132 std::string url_template_filled;
133 if (!ReplacePlaceholders(kUrlTemplate, title, text, share_url,
134 &url_template_filled)) {
135 callback.Run(base::Optional<std::string>(
136 "Error: unable to replace placeholders in url template"));
137 return;
138 }
139
140 GURL target_url(kUrlBase + url_template_filled);
141 if (!target_url.is_valid()) {
142 callback.Run(base::Optional<std::string>(
143 "Error: url of share target is not a valid url."));
144 return;
145 }
146 OpenTargetURL(target_url);
147
148 callback.Run(base::nullopt);
constantinac8b2173b2016-12-15 05:55:51149}