blob: f00bb8cc4fa2ed976e44e1f32b958e905f49594c [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
constantina2cfa55e2017-01-13 06:36:555#include <memory>
6
constantinac8b2173b2016-12-15 05:55:517#include "base/bind.h"
8#include "base/callback.h"
9#include "base/run_loop.h"
constantinae4c513e72017-02-07 02:14:1010#include "base/strings/utf_string_conversions.h"
constantinac8b2173b2016-12-15 05:55:5111#include "chrome/browser/webshare/share_service_impl.h"
constantina5558f3f32017-02-13 05:37:5412#include "chrome/common/pref_names.h"
constantinac8b2173b2016-12-15 05:55:5113#include "chrome/test/base/chrome_render_view_host_test_harness.h"
constantina5558f3f32017-02-13 05:37:5414#include "components/prefs/pref_registry_simple.h"
15#include "components/prefs/scoped_user_pref_update.h"
16#include "components/prefs/testing_pref_service.h"
constantinac8b2173b2016-12-15 05:55:5117#include "mojo/public/cpp/bindings/interface_request.h"
constantina2cfa55e2017-01-13 06:36:5518#include "mojo/public/cpp/bindings/strong_binding.h"
constantinac8b2173b2016-12-15 05:55:5119#include "testing/gtest/include/gtest/gtest.h"
20#include "url/gurl.h"
21
constantina2cfa55e2017-01-13 06:36:5522namespace {
23
24constexpr char kTitle[] = "My title";
25constexpr char kText[] = "My text";
26constexpr char kUrlSpec[] = "https://www.google.com/";
27
constantina5558f3f32017-02-13 05:37:5428constexpr char kTargetName[] = "Share Target";
29constexpr char kUrlTemplate[] = "share?title={title}&text={text}&url={url}";
30constexpr char kManifestUrlHigh[] =
31 "https://www.example-high.com/target/manifest.json";
32constexpr char kManifestUrlLow[] =
33 "https://www.example-low.com/target/manifest.json";
34constexpr char kManifestUrlMin[] =
35 "https://www.example-min.com/target/manifest.json";
36
mgiucabd4b24d2017-02-17 01:40:5737void DidShare(const base::Optional<std::string>& expected_error,
38 const base::Optional<std::string>& error) {
39 EXPECT_EQ(expected_error, error);
40}
41
constantina2cfa55e2017-01-13 06:36:5542class ShareServiceTestImpl : public ShareServiceImpl {
constantinac8b2173b2016-12-15 05:55:5143 public:
constantina5558f3f32017-02-13 05:37:5444 explicit ShareServiceTestImpl(blink::mojom::ShareServiceRequest request)
45 : binding_(this) {
46 binding_.Bind(std::move(request));
47
48 pref_service_.reset(new TestingPrefServiceSimple());
49 pref_service_->registry()->RegisterDictionaryPref(
50 prefs::kWebShareVisitedTargets);
constantina2cfa55e2017-01-13 06:36:5551 }
52
constantina5558f3f32017-02-13 05:37:5453 void AddShareTargetToPrefs(const std::string& manifest_url,
54 const std::string& name,
55 const std::string& url_template) {
56 constexpr char kUrlTemplateKey[] = "url_template";
57 constexpr char kNameKey[] = "name";
58
59 DictionaryPrefUpdate update(GetPrefService(),
60 prefs::kWebShareVisitedTargets);
61 base::DictionaryValue* share_target_dict = update.Get();
62
63 std::unique_ptr<base::DictionaryValue> origin_dict(
64 new base::DictionaryValue);
65
66 origin_dict->SetStringWithoutPathExpansion(kUrlTemplateKey, url_template);
67 origin_dict->SetStringWithoutPathExpansion(kNameKey, name);
68
69 share_target_dict->SetWithoutPathExpansion(manifest_url,
70 std::move(origin_dict));
71 }
72
73 void SetEngagementForTarget(const std::string& manifest_url,
74 blink::mojom::EngagementLevel level) {
75 engagement_map_[manifest_url] = level;
76 }
77
mgiucabd4b24d2017-02-17 01:40:5778 void set_run_loop(base::RunLoop* run_loop) {
79 quit_run_loop_ = run_loop->QuitClosure();
80 }
81
constantina2cfa55e2017-01-13 06:36:5582 const std::string& GetLastUsedTargetURL() { return last_used_target_url_; }
83
constantina5558f3f32017-02-13 05:37:5484 const std::vector<std::pair<base::string16, GURL>>& GetTargetsInPicker() {
85 return targets_in_picker_;
86 }
constantina2cfa55e2017-01-13 06:36:5587
mgiucabd4b24d2017-02-17 01:40:5788 const base::Callback<void(base::Optional<std::string>)>& picker_callback() {
89 return picker_callback_;
90 }
91
constantina5558f3f32017-02-13 05:37:5492 private:
mgiuca39e40672017-01-31 04:16:4993 void ShowPickerDialog(
constantinae4c513e72017-02-07 02:14:1094 const std::vector<std::pair<base::string16, GURL>>& targets,
95 const base::Callback<void(base::Optional<std::string>)>& callback)
96 override {
mgiucabd4b24d2017-02-17 01:40:5797 // Store the arguments passed to the picker dialog.
constantina5558f3f32017-02-13 05:37:5498 targets_in_picker_ = targets;
mgiucabd4b24d2017-02-17 01:40:5799 picker_callback_ = callback;
100
101 // Quit the test's run loop. It is the test's responsibility to call the
102 // callback, to simulate the user's choice.
103 quit_run_loop_.Run();
mgiuca39e40672017-01-31 04:16:49104 }
105
constantina2cfa55e2017-01-13 06:36:55106 void OpenTargetURL(const GURL& target_url) override {
107 last_used_target_url_ = target_url.spec();
108 }
constantina5558f3f32017-02-13 05:37:54109
110 PrefService* GetPrefService() override { return pref_service_.get(); }
111
112 blink::mojom::EngagementLevel GetEngagementLevel(const GURL& url) override {
113 return engagement_map_[url.spec()];
114 }
115
116 mojo::Binding<blink::mojom::ShareService> binding_;
constantina5558f3f32017-02-13 05:37:54117 std::unique_ptr<TestingPrefServiceSimple> pref_service_;
mgiucabd4b24d2017-02-17 01:40:57118
constantina5558f3f32017-02-13 05:37:54119 std::map<std::string, blink::mojom::EngagementLevel> engagement_map_;
mgiucabd4b24d2017-02-17 01:40:57120 // Closure to quit the test's run loop.
121 base::Closure quit_run_loop_;
122
123 // The last URL passed to OpenTargetURL.
124 std::string last_used_target_url_;
125 // The targets passed to ShowPickerDialog.
constantina5558f3f32017-02-13 05:37:54126 std::vector<std::pair<base::string16, GURL>> targets_in_picker_;
mgiucabd4b24d2017-02-17 01:40:57127 // The callback passed to ShowPickerDialog (which is supposed to be called
128 // with the user's chosen result, or nullopt if cancelled).
129 base::Callback<void(base::Optional<std::string>)> picker_callback_;
constantina2cfa55e2017-01-13 06:36:55130};
131
132class ShareServiceImplUnittest : public ChromeRenderViewHostTestHarness {
133 public:
134 ShareServiceImplUnittest() = default;
135 ~ShareServiceImplUnittest() override = default;
constantinac8b2173b2016-12-15 05:55:51136
137 void SetUp() override {
138 ChromeRenderViewHostTestHarness::SetUp();
139
constantina5558f3f32017-02-13 05:37:54140 share_service_helper_ = base::MakeUnique<ShareServiceTestImpl>(
141 mojo::MakeRequest(&share_service_));
142
143 share_service_helper_->SetEngagementForTarget(
144 kManifestUrlHigh, blink::mojom::EngagementLevel::HIGH);
145 share_service_helper_->SetEngagementForTarget(
146 kManifestUrlMin, blink::mojom::EngagementLevel::MINIMAL);
147 share_service_helper_->SetEngagementForTarget(
148 kManifestUrlLow, blink::mojom::EngagementLevel::LOW);
constantinac8b2173b2016-12-15 05:55:51149 }
150
constantina2cfa55e2017-01-13 06:36:55151 void TearDown() override { ChromeRenderViewHostTestHarness::TearDown(); }
constantinac8b2173b2016-12-15 05:55:51152
mgiucabd4b24d2017-02-17 01:40:57153 blink::mojom::ShareService* share_service() const {
154 return share_service_.get();
constantinac8b2173b2016-12-15 05:55:51155 }
156
mgiucabd4b24d2017-02-17 01:40:57157 ShareServiceTestImpl* share_service_helper() const {
158 return share_service_helper_.get();
159 }
160
161 void DeleteShareService() { share_service_helper_.reset(); }
162
163 private:
constantinac8b2173b2016-12-15 05:55:51164 blink::mojom::ShareServicePtr share_service_;
constantina5558f3f32017-02-13 05:37:54165 std::unique_ptr<ShareServiceTestImpl> share_service_helper_;
constantinac8b2173b2016-12-15 05:55:51166};
167
constantina2cfa55e2017-01-13 06:36:55168} // namespace
169
constantinac8b2173b2016-12-15 05:55:51170// Basic test to check the Share method calls the callback with the expected
171// parameters.
constantina2cfa55e2017-01-13 06:36:55172TEST_F(ShareServiceImplUnittest, ShareCallbackParams) {
mgiucabd4b24d2017-02-17 01:40:57173 share_service_helper()->AddShareTargetToPrefs(kManifestUrlLow, kTargetName,
174 kUrlTemplate);
175 share_service_helper()->AddShareTargetToPrefs(kManifestUrlHigh, kTargetName,
176 kUrlTemplate);
177
178 base::Callback<void(const base::Optional<std::string>&)> callback =
179 base::Bind(&DidShare, base::Optional<std::string>());
180
181 base::RunLoop run_loop;
182 share_service_helper()->set_run_loop(&run_loop);
183
184 const GURL url(kUrlSpec);
185 share_service()->Share(kTitle, kText, url, callback);
186
187 run_loop.Run();
188
189 const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
190 make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlHigh)),
191 make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlLow))};
192 EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
193
194 // Pick example-low.com.
195 share_service_helper()->picker_callback().Run(
constantina5558f3f32017-02-13 05:37:54196 base::Optional<std::string>(kManifestUrlLow));
constantina2cfa55e2017-01-13 06:36:55197
mgiucabd4b24d2017-02-17 01:40:57198 const char kExpectedURL[] =
constantina5558f3f32017-02-13 05:37:54199 "https://www.example-low.com/target/"
200 "share?title=My%20title&text=My%20text&url=https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww."
201 "google.com%2F";
mgiucabd4b24d2017-02-17 01:40:57202 EXPECT_EQ(kExpectedURL, share_service_helper()->GetLastUsedTargetURL());
constantinac8b2173b2016-12-15 05:55:51203}
constantina2cfa55e2017-01-13 06:36:55204
constantina5558f3f32017-02-13 05:37:54205// Tests the result of cancelling the share in the picker UI, that doesn't have
206// any targets.
207TEST_F(ShareServiceImplUnittest, ShareCancelNoTargets) {
mgiuca39e40672017-01-31 04:16:49208 // Expect an error message in response.
209 base::Callback<void(const base::Optional<std::string>&)> callback =
mgiucabd4b24d2017-02-17 01:40:57210 base::Bind(&DidShare, base::Optional<std::string>("Share was cancelled"));
mgiuca39e40672017-01-31 04:16:49211
212 base::RunLoop run_loop;
mgiucabd4b24d2017-02-17 01:40:57213 share_service_helper()->set_run_loop(&run_loop);
mgiuca39e40672017-01-31 04:16:49214
215 const GURL url(kUrlSpec);
mgiucabd4b24d2017-02-17 01:40:57216 share_service()->Share(kTitle, kText, url, callback);
mgiuca39e40672017-01-31 04:16:49217
218 run_loop.Run();
mgiucabd4b24d2017-02-17 01:40:57219
220 EXPECT_TRUE(share_service_helper()->GetTargetsInPicker().empty());
221
222 // Cancel the dialog.
223 share_service_helper()->picker_callback().Run(base::nullopt);
224
225 EXPECT_TRUE(share_service_helper()->GetLastUsedTargetURL().empty());
mgiuca39e40672017-01-31 04:16:49226}
227
constantina5558f3f32017-02-13 05:37:54228// Tests the result of cancelling the share in the picker UI, that has targets.
229TEST_F(ShareServiceImplUnittest, ShareCancelWithTargets) {
mgiucabd4b24d2017-02-17 01:40:57230 share_service_helper()->AddShareTargetToPrefs(kManifestUrlHigh, kTargetName,
231 kUrlTemplate);
232 share_service_helper()->AddShareTargetToPrefs(kManifestUrlLow, kTargetName,
233 kUrlTemplate);
constantina5558f3f32017-02-13 05:37:54234
constantina5558f3f32017-02-13 05:37:54235 // Expect an error message in response.
236 base::Callback<void(const base::Optional<std::string>&)> callback =
mgiucabd4b24d2017-02-17 01:40:57237 base::Bind(&DidShare, base::Optional<std::string>("Share was cancelled"));
constantina5558f3f32017-02-13 05:37:54238
239 base::RunLoop run_loop;
mgiucabd4b24d2017-02-17 01:40:57240 share_service_helper()->set_run_loop(&run_loop);
constantina5558f3f32017-02-13 05:37:54241
242 const GURL url(kUrlSpec);
mgiucabd4b24d2017-02-17 01:40:57243 share_service()->Share(kTitle, kText, url, callback);
constantina5558f3f32017-02-13 05:37:54244
245 run_loop.Run();
mgiucabd4b24d2017-02-17 01:40:57246
247 const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
248 make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlHigh)),
249 make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlLow))};
250 EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
251
252 // Cancel the dialog.
253 share_service_helper()->picker_callback().Run(base::nullopt);
254
255 EXPECT_TRUE(share_service_helper()->GetLastUsedTargetURL().empty());
constantina5558f3f32017-02-13 05:37:54256}
257
258// Test to check that only targets with enough engagement were in picker.
259TEST_F(ShareServiceImplUnittest, ShareWithSomeInsufficientlyEngagedTargets) {
mgiucabd4b24d2017-02-17 01:40:57260 share_service_helper()->AddShareTargetToPrefs(kManifestUrlMin, kTargetName,
261 kUrlTemplate);
262 share_service_helper()->AddShareTargetToPrefs(kManifestUrlLow, kTargetName,
263 kUrlTemplate);
264
265 base::Callback<void(const base::Optional<std::string>&)> callback =
266 base::Bind(&DidShare, base::Optional<std::string>());
267
268 base::RunLoop run_loop;
269 share_service_helper()->set_run_loop(&run_loop);
270
271 const GURL url(kUrlSpec);
272 share_service()->Share(kTitle, kText, url, callback);
273
274 run_loop.Run();
275
276 const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
277 make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlLow))};
278 EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
279
280 // Pick example-low.com.
281 share_service_helper()->picker_callback().Run(
282 base::Optional<std::string>(kManifestUrlLow));
283
284 const char kExpectedURL[] =
constantina5558f3f32017-02-13 05:37:54285 "https://www.example-low.com/target/"
286 "share?title=My%20title&text=My%20text&url=https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww."
287 "google.com%2F";
mgiucabd4b24d2017-02-17 01:40:57288 EXPECT_EQ(kExpectedURL, share_service_helper()->GetLastUsedTargetURL());
289}
constantina5558f3f32017-02-13 05:37:54290
mgiucabd4b24d2017-02-17 01:40:57291// Test that deleting the share service while the picker is open does not crash
292// (https://crbug.com/690775).
293TEST_F(ShareServiceImplUnittest, ShareServiceDeletion) {
294 share_service_helper()->AddShareTargetToPrefs(kManifestUrlLow, kTargetName,
295 kUrlTemplate);
constantina5558f3f32017-02-13 05:37:54296
297 base::RunLoop run_loop;
mgiucabd4b24d2017-02-17 01:40:57298 share_service_helper()->set_run_loop(&run_loop);
constantina5558f3f32017-02-13 05:37:54299
300 const GURL url(kUrlSpec);
mgiucabd4b24d2017-02-17 01:40:57301 // Expect the callback to never be called (since the share service is
302 // destroyed before the picker is closed).
303 // TODO(mgiuca): This probably should still complete the share, if not
304 // cancelled, even if the underlying tab is closed.
305 base::Callback<void(const base::Optional<std::string>&)> callback =
306 base::Bind([](const base::Optional<std::string>& error) { FAIL(); });
307 share_service()->Share(kTitle, kText, url, callback);
constantina5558f3f32017-02-13 05:37:54308
309 run_loop.Run();
mgiucabd4b24d2017-02-17 01:40:57310
311 const std::vector<std::pair<base::string16, GURL>> kExpectedTargets{
312 make_pair(base::UTF8ToUTF16(kTargetName), GURL(kManifestUrlLow))};
313 EXPECT_EQ(kExpectedTargets, share_service_helper()->GetTargetsInPicker());
314
315 const base::Callback<void(base::Optional<std::string>)> picker_callback =
316 share_service_helper()->picker_callback();
317
318 DeleteShareService();
319
320 // Pick example-low.com.
321 picker_callback.Run(base::Optional<std::string>(kManifestUrlLow));
constantina5558f3f32017-02-13 05:37:54322}
323
constantina2cfa55e2017-01-13 06:36:55324// Replace various numbers of placeholders in various orders. Placeholders are
325// adjacent to eachother; there are no padding characters.
326TEST_F(ShareServiceImplUnittest, ReplacePlaceholders) {
327 const GURL url(kUrlSpec);
328 std::string url_template_filled;
329 bool succeeded;
330
331 // No placeholders
332 std::string url_template = "blank";
333 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
334 url, &url_template_filled);
335 EXPECT_TRUE(succeeded);
336 EXPECT_EQ("blank", url_template_filled);
337
338 // Empty |url_template|
339 url_template = "";
340 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
341 url, &url_template_filled);
342 EXPECT_TRUE(succeeded);
343 EXPECT_EQ("", url_template_filled);
344
345 // One title placeholder.
346 url_template = "{title}";
347 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
348 url, &url_template_filled);
349 EXPECT_TRUE(succeeded);
350 EXPECT_EQ("My%20title", url_template_filled);
351
352 // One text placeholder.
353 url_template = "{text}";
354 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
355 url, &url_template_filled);
356 EXPECT_TRUE(succeeded);
357 EXPECT_EQ("My%20text", url_template_filled);
358
359 // One url placeholder.
360 url_template = "{url}";
361 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
362 url, &url_template_filled);
363 EXPECT_TRUE(succeeded);
364 EXPECT_EQ("https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F", url_template_filled);
365
366 // One of each placeholder, in title, text, url order.
367 url_template = "{title}{text}{url}";
368 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
369 url, &url_template_filled);
370 EXPECT_TRUE(succeeded);
371 EXPECT_EQ("My%20titleMy%20texthttps%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F",
372 url_template_filled);
373
374 // One of each placeholder, in url, text, title order.
375 url_template = "{url}{text}{title}";
376 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
377 url, &url_template_filled);
378 EXPECT_TRUE(succeeded);
379 EXPECT_EQ("https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2FMy%20textMy%20title",
380 url_template_filled);
381
382 // Two of each placeholder, some next to each other, others not.
383 url_template = "{title}{url}{text}{text}{title}{url}";
384 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
385 url, &url_template_filled);
386 EXPECT_TRUE(succeeded);
387 EXPECT_EQ(
388 "My%20titlehttps%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2FMy%20textMy%20textMy%20"
389 "titlehttps%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F",
390 url_template_filled);
391
392 // Placeholders are in a query string, as values. The expected use case.
393 // Two of each placeholder, some next to each other, others not.
394 url_template =
395 "?title={title}&url={url}&text={text}&text={text}&"
396 "title={title}&url={url}";
397 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
398 url, &url_template_filled);
399 EXPECT_TRUE(succeeded);
400 EXPECT_EQ(
401 "?title=My%20title&url=https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F&text=My%20text&"
402 "text=My%20text&title=My%20title&url=https%3A%2F%2Fptop.only.wip.la%3A443%2Fhttps%2Fwww.google.com%2F",
403 url_template_filled);
404
405 // Badly nested placeholders.
406 url_template = "{";
407 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
408 url, &url_template_filled);
409 EXPECT_FALSE(succeeded);
410
411 url_template = "{title";
412 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
413 url, &url_template_filled);
414 EXPECT_FALSE(succeeded);
415
416 url_template = "{title{text}}";
417 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
418 url, &url_template_filled);
419 EXPECT_FALSE(succeeded);
420
421 url_template = "{title{}";
422 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
423 url, &url_template_filled);
424 EXPECT_FALSE(succeeded);
425
426 url_template = "{title}}";
427 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
428 url, &url_template_filled);
429 EXPECT_FALSE(succeeded);
430
431 // Placeholder with non-identifier character.
432 url_template = "{title?}";
433 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
434 url, &url_template_filled);
435 EXPECT_FALSE(succeeded);
436
437 // Placeholder with digit character.
438 url_template = "{title1}";
439 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
440 url, &url_template_filled);
441 EXPECT_TRUE(succeeded);
442 EXPECT_EQ("", url_template_filled);
443
444 // Empty placeholder.
445 url_template = "{}";
446 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
447 url, &url_template_filled);
448 EXPECT_TRUE(succeeded);
449 EXPECT_EQ("", url_template_filled);
450
451 // Unexpected placeholders.
452 url_template = "{nonexistentplaceholder}";
453 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
454 url, &url_template_filled);
455 EXPECT_TRUE(succeeded);
456 EXPECT_EQ("", url_template_filled);
457
458 // |url_template| with % escapes.
459 url_template = "%20{title}%20";
460 succeeded = ShareServiceImpl::ReplacePlaceholders(url_template, kTitle, kText,
461 url, &url_template_filled);
462 EXPECT_TRUE(succeeded);
463 EXPECT_EQ("%20My%20title%20", url_template_filled);
464
465 // Share data that contains percent escapes.
466 url_template = "{title}";
467 succeeded = ShareServiceImpl::ReplacePlaceholders(
468 url_template, "My%20title", kText, url, &url_template_filled);
469 EXPECT_TRUE(succeeded);
470 EXPECT_EQ("My%2520title", url_template_filled);
471
472 // Share data that contains placeholders. These should not be replaced.
473 url_template = "{title}";
474 succeeded = ShareServiceImpl::ReplacePlaceholders(
475 url_template, "{title}", kText, url, &url_template_filled);
476 EXPECT_TRUE(succeeded);
477 EXPECT_EQ("%7Btitle%7D", url_template_filled);
478
479 // All characters that shouldn't be escaped.
480 url_template = "{title}";
481 succeeded =
482 ShareServiceImpl::ReplacePlaceholders(url_template,
483 "-_.!~*'()0123456789"
484 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
485 "abcdefghijklmnopqrstuvwxyz",
486 kText, url, &url_template_filled);
487 EXPECT_TRUE(succeeded);
488 EXPECT_EQ(
489 "-_.!~*'()0123456789"
490 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
491 "abcdefghijklmnopqrstuvwxyz",
492 url_template_filled);
493
494 // All characters that should be escaped.
495 url_template = "{title}";
496 succeeded = ShareServiceImpl::ReplacePlaceholders(
497 url_template, " \"#$%&+,/:;<=>?@[\\]^`{|}", kText, url,
498 &url_template_filled);
499 EXPECT_TRUE(succeeded);
500 EXPECT_EQ(
501 "%20%22%23%24%25%26%2B%2C%2F%3A%3B%3C%3D%3E%3F%40%5B%5C%5D%5E%60%7B%7C%"
502 "7D",
503 url_template_filled);
504
505 // Unicode chars.
506 // U+263B
507 url_template = "{title}";
508 succeeded = ShareServiceImpl::ReplacePlaceholders(
509 url_template, "\xe2\x98\xbb", kText, url, &url_template_filled);
510 EXPECT_TRUE(succeeded);
511 EXPECT_EQ("%E2%98%BB", url_template_filled);
512
513 // U+00E9
514 url_template = "{title}";
515 succeeded = ShareServiceImpl::ReplacePlaceholders(
516 url_template, "\xc3\xa9", kText, url, &url_template_filled);
517 EXPECT_TRUE(succeeded);
518 EXPECT_EQ("%C3%A9", url_template_filled);
519
520 // U+1F4A9
521 url_template = "{title}";
522 succeeded = ShareServiceImpl::ReplacePlaceholders(
523 url_template, "\xf0\x9f\x92\xa9", kText, url, &url_template_filled);
524 EXPECT_TRUE(succeeded);
525 EXPECT_EQ("%F0%9F%92%A9", url_template_filled);
526}