blob: d39e56e164d20010997de475b2c4a07fc624d544 [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
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:375#include <map>
constantina2cfa55e2017-01-13 06:36:556#include <memory>
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:377#include <utility>
constantina2cfa55e2017-01-13 06:36:558
constantinac8b2173b2016-12-15 05:55:519#include "base/bind.h"
10#include "base/callback.h"
11#include "base/run_loop.h"
constantinae4c513e72017-02-07 02:14:1012#include "base/strings/utf_string_conversions.h"
constantinac8b2173b2016-12-15 05:55:5113#include "chrome/browser/webshare/share_service_impl.h"
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:3714#include "chrome/browser/webshare/webshare_target.h"
constantina5558f3f32017-02-13 05:37:5415#include "chrome/common/pref_names.h"
constantinac8b2173b2016-12-15 05:55:5116#include "chrome/test/base/chrome_render_view_host_test_harness.h"
constantina5558f3f32017-02-13 05:37:5417#include "components/prefs/pref_registry_simple.h"
18#include "components/prefs/scoped_user_pref_update.h"
19#include "components/prefs/testing_pref_service.h"
constantinac8b2173b2016-12-15 05:55:5120#include "mojo/public/cpp/bindings/interface_request.h"
constantina2cfa55e2017-01-13 06:36:5521#include "mojo/public/cpp/bindings/strong_binding.h"
constantinac8b2173b2016-12-15 05:55:5122#include "testing/gtest/include/gtest/gtest.h"
23#include "url/gurl.h"
24
constantina2cfa55e2017-01-13 06:36:5525namespace {
26
27constexpr char kTitle[] = "My title";
28constexpr char kText[] = "My text";
29constexpr char kUrlSpec[] = "https://www.google.com/";
30
constantina5558f3f32017-02-13 05:37:5431constexpr char kTargetName[] = "Share Target";
32constexpr char kUrlTemplate[] = "share?title={title}&text={text}&url={url}";
33constexpr char kManifestUrlHigh[] =
34 "https://www.example-high.com/target/manifest.json";
35constexpr char kManifestUrlLow[] =
36 "https://www.example-low.com/target/manifest.json";
37constexpr char kManifestUrlMin[] =
38 "https://www.example-min.com/target/manifest.json";
39
mgiuca17e725c2017-03-03 02:57:2340void DidShare(blink::mojom::ShareError expected_error,
41 blink::mojom::ShareError error) {
mgiucabd4b24d2017-02-17 01:40:5742 EXPECT_EQ(expected_error, error);
43}
44
constantina2cfa55e2017-01-13 06:36:5545class ShareServiceTestImpl : public ShareServiceImpl {
constantinac8b2173b2016-12-15 05:55:5146 public:
constantina5558f3f32017-02-13 05:37:5447 explicit ShareServiceTestImpl(blink::mojom::ShareServiceRequest request)
48 : binding_(this) {
49 binding_.Bind(std::move(request));
50
51 pref_service_.reset(new TestingPrefServiceSimple());
52 pref_service_->registry()->RegisterDictionaryPref(
53 prefs::kWebShareVisitedTargets);
constantina2cfa55e2017-01-13 06:36:5554 }
55
constantina5558f3f32017-02-13 05:37:5456 void AddShareTargetToPrefs(const std::string& manifest_url,
57 const std::string& name,
58 const std::string& url_template) {
59 constexpr char kUrlTemplateKey[] = "url_template";
60 constexpr char kNameKey[] = "name";
61
62 DictionaryPrefUpdate update(GetPrefService(),
63 prefs::kWebShareVisitedTargets);
64 base::DictionaryValue* share_target_dict = update.Get();
65
66 std::unique_ptr<base::DictionaryValue> origin_dict(
67 new base::DictionaryValue);
68
jdoerriefbb03dd2017-08-17 14:43:2669 origin_dict->SetKey(kUrlTemplateKey, base::Value(url_template));
70 origin_dict->SetKey(kNameKey, base::Value(name));
constantina5558f3f32017-02-13 05:37:5471
72 share_target_dict->SetWithoutPathExpansion(manifest_url,
73 std::move(origin_dict));
74 }
75
76 void SetEngagementForTarget(const std::string& manifest_url,
77 blink::mojom::EngagementLevel level) {
78 engagement_map_[manifest_url] = level;
79 }
80
mgiucabd4b24d2017-02-17 01:40:5781 void set_run_loop(base::RunLoop* run_loop) {
82 quit_run_loop_ = run_loop->QuitClosure();
83 }
84
constantina2cfa55e2017-01-13 06:36:5585 const std::string& GetLastUsedTargetURL() { return last_used_target_url_; }
86
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:3787 const std::vector<WebShareTarget>& GetTargetsInPicker() {
constantina5558f3f32017-02-13 05:37:5488 return targets_in_picker_;
89 }
constantina2cfa55e2017-01-13 06:36:5590
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:3791 void PickTarget(const std::string& target_url) {
92 const auto& it =
93 std::find_if(targets_in_picker_.begin(), targets_in_picker_.end(),
94 [&target_url](const WebShareTarget& target) {
95 return target.manifest_url().spec() == target_url;
96 });
97 DCHECK(it != targets_in_picker_.end());
98 std::move(picker_callback_).Run(&*it);
99 }
100
ortuno928d14102017-05-02 00:09:05101 chrome::WebShareTargetPickerCallback picker_callback() {
102 return std::move(picker_callback_);
mgiucabd4b24d2017-02-17 01:40:57103 }
104
constantina5558f3f32017-02-13 05:37:54105 private:
mgiuca39e40672017-01-31 04:16:49106 void ShowPickerDialog(
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37107 std::vector<WebShareTarget> targets,
ortuno928d14102017-05-02 00:09:05108 chrome::WebShareTargetPickerCallback callback) override {
mgiucabd4b24d2017-02-17 01:40:57109 // Store the arguments passed to the picker dialog.
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37110 targets_in_picker_ = std::move(targets);
ortuno928d14102017-05-02 00:09:05111 picker_callback_ = std::move(callback);
mgiucabd4b24d2017-02-17 01:40:57112
113 // Quit the test's run loop. It is the test's responsibility to call the
114 // callback, to simulate the user's choice.
115 quit_run_loop_.Run();
mgiuca39e40672017-01-31 04:16:49116 }
117
constantina2cfa55e2017-01-13 06:36:55118 void OpenTargetURL(const GURL& target_url) override {
119 last_used_target_url_ = target_url.spec();
120 }
constantina5558f3f32017-02-13 05:37:54121
122 PrefService* GetPrefService() override { return pref_service_.get(); }
123
124 blink::mojom::EngagementLevel GetEngagementLevel(const GURL& url) override {
125 return engagement_map_[url.spec()];
126 }
127
128 mojo::Binding<blink::mojom::ShareService> binding_;
constantina5558f3f32017-02-13 05:37:54129 std::unique_ptr<TestingPrefServiceSimple> pref_service_;
mgiucabd4b24d2017-02-17 01:40:57130
constantina5558f3f32017-02-13 05:37:54131 std::map<std::string, blink::mojom::EngagementLevel> engagement_map_;
mgiucabd4b24d2017-02-17 01:40:57132 // Closure to quit the test's run loop.
133 base::Closure quit_run_loop_;
134
135 // The last URL passed to OpenTargetURL.
136 std::string last_used_target_url_;
137 // The targets passed to ShowPickerDialog.
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37138 std::vector<WebShareTarget> targets_in_picker_;
mgiucabd4b24d2017-02-17 01:40:57139 // The callback passed to ShowPickerDialog (which is supposed to be called
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37140 // with the user's chosen result, or nullptr if cancelled).
ortuno928d14102017-05-02 00:09:05141 chrome::WebShareTargetPickerCallback picker_callback_;
constantina2cfa55e2017-01-13 06:36:55142};
143
144class ShareServiceImplUnittest : public ChromeRenderViewHostTestHarness {
145 public:
146 ShareServiceImplUnittest() = default;
147 ~ShareServiceImplUnittest() override = default;
constantinac8b2173b2016-12-15 05:55:51148
149 void SetUp() override {
150 ChromeRenderViewHostTestHarness::SetUp();
151
constantina5558f3f32017-02-13 05:37:54152 share_service_helper_ = base::MakeUnique<ShareServiceTestImpl>(
153 mojo::MakeRequest(&share_service_));
154
155 share_service_helper_->SetEngagementForTarget(
156 kManifestUrlHigh, blink::mojom::EngagementLevel::HIGH);
157 share_service_helper_->SetEngagementForTarget(
158 kManifestUrlMin, blink::mojom::EngagementLevel::MINIMAL);
159 share_service_helper_->SetEngagementForTarget(
160 kManifestUrlLow, blink::mojom::EngagementLevel::LOW);
constantinac8b2173b2016-12-15 05:55:51161 }
162
constantina2cfa55e2017-01-13 06:36:55163 void TearDown() override { ChromeRenderViewHostTestHarness::TearDown(); }
constantinac8b2173b2016-12-15 05:55:51164
mgiucabd4b24d2017-02-17 01:40:57165 blink::mojom::ShareService* share_service() const {
166 return share_service_.get();
constantinac8b2173b2016-12-15 05:55:51167 }
168
mgiucabd4b24d2017-02-17 01:40:57169 ShareServiceTestImpl* share_service_helper() const {
170 return share_service_helper_.get();
171 }
172
173 void DeleteShareService() { share_service_helper_.reset(); }
174
175 private:
constantinac8b2173b2016-12-15 05:55:51176 blink::mojom::ShareServicePtr share_service_;
constantina5558f3f32017-02-13 05:37:54177 std::unique_ptr<ShareServiceTestImpl> share_service_helper_;
constantinac8b2173b2016-12-15 05:55:51178};
179
constantina2cfa55e2017-01-13 06:36:55180} // namespace
181
constantinac8b2173b2016-12-15 05:55:51182// Basic test to check the Share method calls the callback with the expected
183// parameters.
constantina2cfa55e2017-01-13 06:36:55184TEST_F(ShareServiceImplUnittest, ShareCallbackParams) {
mgiucabd4b24d2017-02-17 01:40:57185 share_service_helper()->AddShareTargetToPrefs(kManifestUrlLow, kTargetName,
186 kUrlTemplate);
187 share_service_helper()->AddShareTargetToPrefs(kManifestUrlHigh, kTargetName,
188 kUrlTemplate);
Matt Giucae0739ed02017-09-26 09:28:22189 // Expect this invalid URL to be ignored (not crash);
190 // https://crbug.com/762388.
191 share_service_helper()->AddShareTargetToPrefs("", kTargetName, kUrlTemplate);
mgiucabd4b24d2017-02-17 01:40:57192
mgiuca17e725c2017-03-03 02:57:23193 base::Callback<void(blink::mojom::ShareError)> callback =
194 base::Bind(&DidShare, blink::mojom::ShareError::OK);
mgiucabd4b24d2017-02-17 01:40:57195
196 base::RunLoop run_loop;
197 share_service_helper()->set_run_loop(&run_loop);
198
199 const GURL url(kUrlSpec);
200 share_service()->Share(kTitle, kText, url, callback);
201
202 run_loop.Run();
203
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37204 std::vector<WebShareTarget> expected_targets;
205 expected_targets.emplace_back(GURL(kManifestUrlHigh), kTargetName,
206 kUrlTemplate);
207 expected_targets.emplace_back(GURL(kManifestUrlLow), kTargetName,
208 kUrlTemplate);
209 EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
mgiucabd4b24d2017-02-17 01:40:57210
211 // Pick example-low.com.
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37212 share_service_helper()->PickTarget(kManifestUrlLow);
constantina2cfa55e2017-01-13 06:36:55213
mgiucabd4b24d2017-02-17 01:40:57214 const char kExpectedURL[] =
constantina5558f3f32017-02-13 05:37:54215 "https://www.example-low.com/target/"
216 "share?title=My%20title&text=My%20text&url=https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww."
217 "google.com%2F";
mgiucabd4b24d2017-02-17 01:40:57218 EXPECT_EQ(kExpectedURL, share_service_helper()->GetLastUsedTargetURL());
constantinac8b2173b2016-12-15 05:55:51219}
constantina2cfa55e2017-01-13 06:36:55220
constantina5558f3f32017-02-13 05:37:54221// Tests the result of cancelling the share in the picker UI, that doesn't have
222// any targets.
223TEST_F(ShareServiceImplUnittest, ShareCancelNoTargets) {
mgiuca39e40672017-01-31 04:16:49224 // Expect an error message in response.
mgiuca17e725c2017-03-03 02:57:23225 base::Callback<void(blink::mojom::ShareError)> callback =
226 base::Bind(&DidShare, blink::mojom::ShareError::CANCELED);
mgiuca39e40672017-01-31 04:16:49227
228 base::RunLoop run_loop;
mgiucabd4b24d2017-02-17 01:40:57229 share_service_helper()->set_run_loop(&run_loop);
mgiuca39e40672017-01-31 04:16:49230
231 const GURL url(kUrlSpec);
mgiucabd4b24d2017-02-17 01:40:57232 share_service()->Share(kTitle, kText, url, callback);
mgiuca39e40672017-01-31 04:16:49233
234 run_loop.Run();
mgiucabd4b24d2017-02-17 01:40:57235
236 EXPECT_TRUE(share_service_helper()->GetTargetsInPicker().empty());
237
238 // Cancel the dialog.
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37239 share_service_helper()->picker_callback().Run(nullptr);
mgiucabd4b24d2017-02-17 01:40:57240
241 EXPECT_TRUE(share_service_helper()->GetLastUsedTargetURL().empty());
mgiuca39e40672017-01-31 04:16:49242}
243
constantina5558f3f32017-02-13 05:37:54244// Tests the result of cancelling the share in the picker UI, that has targets.
245TEST_F(ShareServiceImplUnittest, ShareCancelWithTargets) {
mgiucabd4b24d2017-02-17 01:40:57246 share_service_helper()->AddShareTargetToPrefs(kManifestUrlHigh, kTargetName,
247 kUrlTemplate);
248 share_service_helper()->AddShareTargetToPrefs(kManifestUrlLow, kTargetName,
249 kUrlTemplate);
constantina5558f3f32017-02-13 05:37:54250
constantina5558f3f32017-02-13 05:37:54251 // Expect an error message in response.
mgiuca17e725c2017-03-03 02:57:23252 base::Callback<void(blink::mojom::ShareError)> callback =
253 base::Bind(&DidShare, blink::mojom::ShareError::CANCELED);
constantina5558f3f32017-02-13 05:37:54254
255 base::RunLoop run_loop;
mgiucabd4b24d2017-02-17 01:40:57256 share_service_helper()->set_run_loop(&run_loop);
constantina5558f3f32017-02-13 05:37:54257
258 const GURL url(kUrlSpec);
mgiucabd4b24d2017-02-17 01:40:57259 share_service()->Share(kTitle, kText, url, callback);
constantina5558f3f32017-02-13 05:37:54260
261 run_loop.Run();
mgiucabd4b24d2017-02-17 01:40:57262
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37263 std::vector<WebShareTarget> expected_targets;
264 expected_targets.emplace_back(GURL(kManifestUrlHigh), kTargetName,
265 kUrlTemplate);
266 expected_targets.emplace_back(GURL(kManifestUrlLow), kTargetName,
267 kUrlTemplate);
268 EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
mgiucabd4b24d2017-02-17 01:40:57269
270 // Cancel the dialog.
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37271 share_service_helper()->picker_callback().Run(nullptr);
mgiucabd4b24d2017-02-17 01:40:57272
273 EXPECT_TRUE(share_service_helper()->GetLastUsedTargetURL().empty());
constantina5558f3f32017-02-13 05:37:54274}
275
mgiucaf3a4c632017-02-21 08:08:59276// Tests a target with a broken URL template (ReplacePlaceholders failure).
277TEST_F(ShareServiceImplUnittest, ShareBrokenUrl) {
278 // Invalid placeholders. Detailed tests for broken templates are in the
279 // ReplacePlaceholders test; this just tests the share response.
280 constexpr char kBrokenUrlTemplate[] = "share?title={title";
281 share_service_helper()->AddShareTargetToPrefs(kManifestUrlHigh, kTargetName,
282 kBrokenUrlTemplate);
283
284 // Expect an error message in response.
mgiuca17e725c2017-03-03 02:57:23285 base::Callback<void(blink::mojom::ShareError)> callback =
286 base::Bind(&DidShare, blink::mojom::ShareError::INTERNAL_ERROR);
mgiucaf3a4c632017-02-21 08:08:59287
288 base::RunLoop run_loop;
289 share_service_helper()->set_run_loop(&run_loop);
290
291 const GURL url(kUrlSpec);
292 share_service()->Share(kTitle, kText, url, callback);
293
294 run_loop.Run();
295
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37296 std::vector<WebShareTarget> expected_targets;
297 expected_targets.emplace_back(GURL(kManifestUrlHigh), kTargetName,
298 kBrokenUrlTemplate);
299 EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
mgiucaf3a4c632017-02-21 08:08:59300
301 // Pick example-high.com.
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37302 share_service_helper()->PickTarget(kManifestUrlHigh);
mgiucaf3a4c632017-02-21 08:08:59303
304 EXPECT_TRUE(share_service_helper()->GetLastUsedTargetURL().empty());
305}
306
constantina5558f3f32017-02-13 05:37:54307// Test to check that only targets with enough engagement were in picker.
308TEST_F(ShareServiceImplUnittest, ShareWithSomeInsufficientlyEngagedTargets) {
mgiucabd4b24d2017-02-17 01:40:57309 share_service_helper()->AddShareTargetToPrefs(kManifestUrlMin, kTargetName,
310 kUrlTemplate);
311 share_service_helper()->AddShareTargetToPrefs(kManifestUrlLow, kTargetName,
312 kUrlTemplate);
313
mgiuca17e725c2017-03-03 02:57:23314 base::Callback<void(blink::mojom::ShareError)> callback =
315 base::Bind(&DidShare, blink::mojom::ShareError::OK);
mgiucabd4b24d2017-02-17 01:40:57316
317 base::RunLoop run_loop;
318 share_service_helper()->set_run_loop(&run_loop);
319
320 const GURL url(kUrlSpec);
321 share_service()->Share(kTitle, kText, url, callback);
322
323 run_loop.Run();
324
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37325 std::vector<WebShareTarget> expected_targets;
326 expected_targets.emplace_back(GURL(kManifestUrlLow), kTargetName,
327 kUrlTemplate);
328 EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
mgiucabd4b24d2017-02-17 01:40:57329
330 // Pick example-low.com.
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37331 share_service_helper()->PickTarget(kManifestUrlLow);
mgiucabd4b24d2017-02-17 01:40:57332
333 const char kExpectedURL[] =
constantina5558f3f32017-02-13 05:37:54334 "https://www.example-low.com/target/"
335 "share?title=My%20title&text=My%20text&url=https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww."
336 "google.com%2F";
mgiucabd4b24d2017-02-17 01:40:57337 EXPECT_EQ(kExpectedURL, share_service_helper()->GetLastUsedTargetURL());
338}
constantina5558f3f32017-02-13 05:37:54339
mgiucabd4b24d2017-02-17 01:40:57340// Test that deleting the share service while the picker is open does not crash
341// (https://crbug.com/690775).
342TEST_F(ShareServiceImplUnittest, ShareServiceDeletion) {
343 share_service_helper()->AddShareTargetToPrefs(kManifestUrlLow, kTargetName,
344 kUrlTemplate);
constantina5558f3f32017-02-13 05:37:54345
346 base::RunLoop run_loop;
mgiucabd4b24d2017-02-17 01:40:57347 share_service_helper()->set_run_loop(&run_loop);
constantina5558f3f32017-02-13 05:37:54348
349 const GURL url(kUrlSpec);
mgiucabd4b24d2017-02-17 01:40:57350 // Expect the callback to never be called (since the share service is
351 // destroyed before the picker is closed).
352 // TODO(mgiuca): This probably should still complete the share, if not
353 // cancelled, even if the underlying tab is closed.
mgiuca17e725c2017-03-03 02:57:23354 base::Callback<void(blink::mojom::ShareError)> callback =
355 base::Bind([](blink::mojom::ShareError error) { FAIL(); });
mgiucabd4b24d2017-02-17 01:40:57356 share_service()->Share(kTitle, kText, url, callback);
constantina5558f3f32017-02-13 05:37:54357
358 run_loop.Run();
mgiucabd4b24d2017-02-17 01:40:57359
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37360 std::vector<WebShareTarget> expected_targets;
361 expected_targets.emplace_back(GURL(kManifestUrlLow), kTargetName,
362 kUrlTemplate);
363 EXPECT_EQ(expected_targets, share_service_helper()->GetTargetsInPicker());
mgiucabd4b24d2017-02-17 01:40:57364
ortuno928d14102017-05-02 00:09:05365 chrome::WebShareTargetPickerCallback picker_callback =
mgiucabd4b24d2017-02-17 01:40:57366 share_service_helper()->picker_callback();
367
368 DeleteShareService();
369
370 // Pick example-low.com.
Giovanni Ortuño Urquidi97f8bd522017-06-16 04:04:37371 std::move(picker_callback).Run(&expected_targets[0]);
constantina5558f3f32017-02-13 05:37:54372}
373
constantina2cfa55e2017-01-13 06:36:55374// Replace various numbers of placeholders in various orders. Placeholders are
375// adjacent to eachother; there are no padding characters.
376TEST_F(ShareServiceImplUnittest, ReplacePlaceholders) {
377 const GURL url(kUrlSpec);
378 std::string url_template_filled;
379 bool succeeded;
380
381 // No placeholders
382 std::string url_template = "blank";
383 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
384 url, &url_template_filled);
385 EXPECT_TRUE(succeeded);
386 EXPECT_EQ("blank", url_template_filled);
387
388 // Empty |url_template|
389 url_template = "";
390 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
391 url, &url_template_filled);
392 EXPECT_TRUE(succeeded);
393 EXPECT_EQ("", url_template_filled);
394
395 // One title placeholder.
396 url_template = "{title}";
397 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
398 url, &url_template_filled);
399 EXPECT_TRUE(succeeded);
400 EXPECT_EQ("My%20title", url_template_filled);
401
402 // One text placeholder.
403 url_template = "{text}";
404 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
405 url, &url_template_filled);
406 EXPECT_TRUE(succeeded);
407 EXPECT_EQ("My%20text", url_template_filled);
408
409 // One url placeholder.
410 url_template = "{url}";
411 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
412 url, &url_template_filled);
413 EXPECT_TRUE(succeeded);
414 EXPECT_EQ("https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F", url_template_filled);
415
416 // One of each placeholder, in title, text, url order.
417 url_template = "{title}{text}{url}";
418 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
419 url, &url_template_filled);
420 EXPECT_TRUE(succeeded);
421 EXPECT_EQ("My%20titleMy%20texthttps%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F",
422 url_template_filled);
423
424 // One of each placeholder, in url, text, title order.
425 url_template = "{url}{text}{title}";
426 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
427 url, &url_template_filled);
428 EXPECT_TRUE(succeeded);
429 EXPECT_EQ("https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2FMy%20textMy%20title",
430 url_template_filled);
431
432 // Two of each placeholder, some next to each other, others not.
433 url_template = "{title}{url}{text}{text}{title}{url}";
434 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
435 url, &url_template_filled);
436 EXPECT_TRUE(succeeded);
437 EXPECT_EQ(
438 "My%20titlehttps%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2FMy%20textMy%20textMy%20"
439 "titlehttps%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F",
440 url_template_filled);
441
442 // Placeholders are in a query string, as values. The expected use case.
443 // Two of each placeholder, some next to each other, others not.
444 url_template =
445 "?title={title}&url={url}&text={text}&text={text}&"
446 "title={title}&url={url}";
447 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
448 url, &url_template_filled);
449 EXPECT_TRUE(succeeded);
450 EXPECT_EQ(
451 "?title=My%20title&url=https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F&text=My%20text&"
452 "text=My%20text&title=My%20title&url=https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F",
453 url_template_filled);
454
455 // Badly nested placeholders.
456 url_template = "{";
457 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
458 url, &url_template_filled);
459 EXPECT_FALSE(succeeded);
460
461 url_template = "{title";
462 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
463 url, &url_template_filled);
464 EXPECT_FALSE(succeeded);
465
466 url_template = "{title{text}}";
467 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
468 url, &url_template_filled);
469 EXPECT_FALSE(succeeded);
470
471 url_template = "{title{}";
472 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
473 url, &url_template_filled);
474 EXPECT_FALSE(succeeded);
475
476 url_template = "{title}}";
477 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
478 url, &url_template_filled);
479 EXPECT_FALSE(succeeded);
480
481 // Placeholder with non-identifier character.
482 url_template = "{title?}";
483 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
484 url, &url_template_filled);
485 EXPECT_FALSE(succeeded);
486
487 // Placeholder with digit character.
488 url_template = "{title1}";
489 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
490 url, &url_template_filled);
491 EXPECT_TRUE(succeeded);
492 EXPECT_EQ("", url_template_filled);
493
494 // Empty placeholder.
495 url_template = "{}";
496 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
497 url, &url_template_filled);
498 EXPECT_TRUE(succeeded);
499 EXPECT_EQ("", url_template_filled);
500
501 // Unexpected placeholders.
502 url_template = "{nonexistentplaceholder}";
503 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
504 url, &url_template_filled);
505 EXPECT_TRUE(succeeded);
506 EXPECT_EQ("", url_template_filled);
507
508 // |url_template| with % escapes.
509 url_template = "%20{title}%20";
510 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
511 url, &url_template_filled);
512 EXPECT_TRUE(succeeded);
513 EXPECT_EQ("%20My%20title%20", url_template_filled);
514
515 // Share data that contains percent escapes.
516 url_template = "{title}";
517 succeeded = ShareServiceImpl::ReplacePlaceholders(
518 url_template, "My%20title", kText, url, &url_template_filled);
519 EXPECT_TRUE(succeeded);
520 EXPECT_EQ("My%2520title", url_template_filled);
521
522 // Share data that contains placeholders. These should not be replaced.
523 url_template = "{title}";
524 succeeded = ShareServiceImpl::ReplacePlaceholders(
525 url_template, "{title}", kText, url, &url_template_filled);
526 EXPECT_TRUE(succeeded);
527 EXPECT_EQ("%7Btitle%7D", url_template_filled);
528
529 // All characters that shouldn't be escaped.
530 url_template = "{title}";
531 succeeded =
532 ShareServiceImpl::ReplacePlaceholders(url_template,
533 "-_.!~*'()0123456789"
534 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
535 "abcdefghijklmnopqrstuvwxyz",
536 kText, url, &url_template_filled);
537 EXPECT_TRUE(succeeded);
538 EXPECT_EQ(
539 "-_.!~*'()0123456789"
540 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
541 "abcdefghijklmnopqrstuvwxyz",
542 url_template_filled);
543
544 // All characters that should be escaped.
545 url_template = "{title}";
546 succeeded = ShareServiceImpl::ReplacePlaceholders(
547 url_template, " \"#$%&+,/:;<=>?@[\\]^`{|}", kText, url,
548 &url_template_filled);
549 EXPECT_TRUE(succeeded);
550 EXPECT_EQ(
551 "%20%22%23%24%25%26%2B%2C%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%60%7B%7C%"
552 "7D",
553 url_template_filled);
554
555 // Unicode chars.
556 // U+263B
557 url_template = "{title}";
558 succeeded = ShareServiceImpl::ReplacePlaceholders(
559 url_template, "\xe2\x98\xbb", kText, url, &url_template_filled);
560 EXPECT_TRUE(succeeded);
561 EXPECT_EQ("%E2%98%BB", url_template_filled);
562
563 // U+00E9
564 url_template = "{title}";
565 succeeded = ShareServiceImpl::ReplacePlaceholders(
566 url_template, "\xc3\xa9", kText, url, &url_template_filled);
567 EXPECT_TRUE(succeeded);
568 EXPECT_EQ("%C3%A9", url_template_filled);
569
570 // U+1F4A9
571 url_template = "{title}";
572 succeeded = ShareServiceImpl::ReplacePlaceholders(
573 url_template, "\xf0\x9f\x92\xa9", kText, url, &url_template_filled);
574 EXPECT_TRUE(succeeded);
575 EXPECT_EQ("%F0%9F%92%A9", url_template_filled);
576}