1 // 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 "components/arc/intent_helper/arc_intent_helper_bridge.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/memory/ptr_util.h"
11 #include "base/optional.h"
12 #include "components/arc/intent_helper/open_url_delegate.h"
13 #include "components/arc/mojom/intent_helper.mojom.h"
14 #include "components/arc/session/arc_bridge_service.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace arc {
18
19 namespace {
20
21 constexpr char kPackageName[] = "default.package.name";
22
GetIntentFilter(const std::string & host,const std::string & pkg_name)23 IntentFilter GetIntentFilter(const std::string& host,
24 const std::string& pkg_name) {
25 std::vector<IntentFilter::AuthorityEntry> authorities;
26 authorities.emplace_back(host, /*port=*/-1);
27 return IntentFilter(pkg_name, /*actions=*/std::vector<std::string>(),
28 std::move(authorities),
29 std::vector<IntentFilter::PatternMatcher>(),
30 /*schemes=*/std::vector<std::string>(),
31 /*mime_types=*/std::vector<std::string>());
32 }
33
34 } // namespace
35
36 class ArcIntentHelperTest : public testing::Test {
37 protected:
38 ArcIntentHelperTest() = default;
39
40 class TestOpenUrlDelegate : public OpenUrlDelegate {
41 public:
42 ~TestOpenUrlDelegate() override = default;
43
44 // OpenUrlDelegate:
OpenUrlFromArc(const GURL & url)45 void OpenUrlFromArc(const GURL& url) override { last_opened_url_ = url; }
OpenWebAppFromArc(const GURL & url)46 void OpenWebAppFromArc(const GURL& url) override { last_opened_url_ = url; }
OpenArcCustomTab(const GURL & url,int32_t task_id,mojom::IntentHelperHost::OnOpenCustomTabCallback callback)47 void OpenArcCustomTab(
48 const GURL& url,
49 int32_t task_id,
50 mojom::IntentHelperHost::OnOpenCustomTabCallback callback) override {
51 std::move(callback).Run(mojo::NullRemote());
52 }
OpenChromePageFromArc(mojom::ChromePage chrome_page)53 void OpenChromePageFromArc(mojom::ChromePage chrome_page) override {}
54
TakeLastOpenedUrl()55 GURL TakeLastOpenedUrl() {
56 GURL result = std::move(last_opened_url_);
57 last_opened_url_ = GURL();
58 return result;
59 }
60
61 private:
62 GURL last_opened_url_;
63 };
64
65 std::unique_ptr<ArcBridgeService> arc_bridge_service_;
66 std::unique_ptr<TestOpenUrlDelegate> test_open_url_delegate_;
67 std::unique_ptr<ArcIntentHelperBridge> instance_;
68
69 private:
SetUp()70 void SetUp() override {
71 arc_bridge_service_ = std::make_unique<ArcBridgeService>();
72 test_open_url_delegate_ = std::make_unique<TestOpenUrlDelegate>();
73 instance_ = std::make_unique<ArcIntentHelperBridge>(
74 nullptr /* context */, arc_bridge_service_.get());
75 ArcIntentHelperBridge::SetOpenUrlDelegate(test_open_url_delegate_.get());
76 }
77
TearDown()78 void TearDown() override {
79 ArcIntentHelperBridge::SetOpenUrlDelegate(nullptr);
80 instance_.reset();
81 test_open_url_delegate_.reset();
82 arc_bridge_service_.reset();
83 }
84
85 DISALLOW_COPY_AND_ASSIGN(ArcIntentHelperTest);
86 };
87
88 // Tests if IsIntentHelperPackage works as expected. Probably too trivial
89 // to test but just in case.
TEST_F(ArcIntentHelperTest,TestIsIntentHelperPackage)90 TEST_F(ArcIntentHelperTest, TestIsIntentHelperPackage) {
91 EXPECT_FALSE(ArcIntentHelperBridge::IsIntentHelperPackage(""));
92 EXPECT_FALSE(ArcIntentHelperBridge::IsIntentHelperPackage(
93 ArcIntentHelperBridge::kArcIntentHelperPackageName + std::string("a")));
94 EXPECT_FALSE(ArcIntentHelperBridge::IsIntentHelperPackage(
95 ArcIntentHelperBridge::kArcIntentHelperPackageName +
96 std::string("/.ArcIntentHelperActivity")));
97 EXPECT_TRUE(ArcIntentHelperBridge::IsIntentHelperPackage(
98 ArcIntentHelperBridge::kArcIntentHelperPackageName));
99 }
100
101 // Tests if FilterOutIntentHelper removes handlers as expected.
TEST_F(ArcIntentHelperTest,TestFilterOutIntentHelper)102 TEST_F(ArcIntentHelperTest, TestFilterOutIntentHelper) {
103 {
104 std::vector<mojom::IntentHandlerInfoPtr> orig;
105 std::vector<mojom::IntentHandlerInfoPtr> filtered =
106 ArcIntentHelperBridge::FilterOutIntentHelper(std::move(orig));
107 EXPECT_EQ(0U, filtered.size());
108 }
109
110 {
111 std::vector<mojom::IntentHandlerInfoPtr> orig;
112 orig.push_back(mojom::IntentHandlerInfo::New());
113 orig[0]->name = "0";
114 orig[0]->package_name = "package_name0";
115 orig.push_back(mojom::IntentHandlerInfo::New());
116 orig[1]->name = "1";
117 orig[1]->package_name = "package_name1";
118
119 // FilterOutIntentHelper is no-op in this case.
120 std::vector<mojom::IntentHandlerInfoPtr> filtered =
121 ArcIntentHelperBridge::FilterOutIntentHelper(std::move(orig));
122 EXPECT_EQ(2U, filtered.size());
123 }
124
125 {
126 std::vector<mojom::IntentHandlerInfoPtr> orig;
127 orig.push_back(mojom::IntentHandlerInfo::New());
128 orig[0]->name = "0";
129 orig[0]->package_name = ArcIntentHelperBridge::kArcIntentHelperPackageName;
130 orig.push_back(mojom::IntentHandlerInfo::New());
131 orig[1]->name = "1";
132 orig[1]->package_name = "package_name1";
133
134 // FilterOutIntentHelper should remove the first element.
135 std::vector<mojom::IntentHandlerInfoPtr> filtered =
136 ArcIntentHelperBridge::FilterOutIntentHelper(std::move(orig));
137 ASSERT_EQ(1U, filtered.size());
138 EXPECT_EQ("1", filtered[0]->name);
139 EXPECT_EQ("package_name1", filtered[0]->package_name);
140 }
141
142 {
143 std::vector<mojom::IntentHandlerInfoPtr> orig;
144 orig.push_back(mojom::IntentHandlerInfo::New());
145 orig[0]->name = "0";
146 orig[0]->package_name = ArcIntentHelperBridge::kArcIntentHelperPackageName;
147 orig.push_back(mojom::IntentHandlerInfo::New());
148 orig[1]->name = "1";
149 orig[1]->package_name = "package_name1";
150 orig.push_back(mojom::IntentHandlerInfo::New());
151 orig[2]->name = "2";
152 orig[2]->package_name = ArcIntentHelperBridge::kArcIntentHelperPackageName;
153
154 // FilterOutIntentHelper should remove two elements.
155 std::vector<mojom::IntentHandlerInfoPtr> filtered =
156 ArcIntentHelperBridge::FilterOutIntentHelper(std::move(orig));
157 ASSERT_EQ(1U, filtered.size());
158 EXPECT_EQ("1", filtered[0]->name);
159 EXPECT_EQ("package_name1", filtered[0]->package_name);
160 }
161
162 {
163 std::vector<mojom::IntentHandlerInfoPtr> orig;
164 orig.push_back(mojom::IntentHandlerInfo::New());
165 orig[0]->name = "0";
166 orig[0]->package_name = ArcIntentHelperBridge::kArcIntentHelperPackageName;
167 orig.push_back(mojom::IntentHandlerInfo::New());
168 orig[1]->name = "1";
169 orig[1]->package_name = ArcIntentHelperBridge::kArcIntentHelperPackageName;
170
171 // FilterOutIntentHelper should remove all elements.
172 std::vector<mojom::IntentHandlerInfoPtr> filtered =
173 ArcIntentHelperBridge::FilterOutIntentHelper(std::move(orig));
174 EXPECT_EQ(0U, filtered.size());
175 }
176 }
177
178 // Tests if observer works as expected.
TEST_F(ArcIntentHelperTest,TestObserver)179 TEST_F(ArcIntentHelperTest, TestObserver) {
180 class FakeObserver : public ArcIntentHelperObserver {
181 public:
182 FakeObserver() = default;
183 void OnIntentFiltersUpdated(
184 const base::Optional<std::string>& package_name) override {
185 updated_ = true;
186 }
187 bool IsUpdated() { return updated_; }
188 void Reset() { updated_ = false; }
189
190 private:
191 bool updated_ = false;
192 };
193
194 // Observer should be called when intent filter is updated.
195 auto observer = std::make_unique<FakeObserver>();
196 instance_->AddObserver(observer.get());
197 EXPECT_FALSE(observer->IsUpdated());
198 instance_->OnIntentFiltersUpdated(std::vector<IntentFilter>());
199 EXPECT_TRUE(observer->IsUpdated());
200
201 // Observer should not be called after it's removed.
202 observer->Reset();
203 instance_->RemoveObserver(observer.get());
204 instance_->OnIntentFiltersUpdated(std::vector<IntentFilter>());
205 EXPECT_FALSE(observer->IsUpdated());
206 }
207
208 // Tests that ShouldChromeHandleUrl returns true by default.
TEST_F(ArcIntentHelperTest,TestDefault)209 TEST_F(ArcIntentHelperTest, TestDefault) {
210 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
211 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
212 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("file:///etc/password")));
213 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("chrome://help")));
214 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("about://chrome")));
215 }
216
217 // Tests that ShouldChromeHandleUrl returns false when there's a match.
TEST_F(ArcIntentHelperTest,TestSingleFilter)218 TEST_F(ArcIntentHelperTest, TestSingleFilter) {
219 std::vector<IntentFilter> array;
220 array.emplace_back(GetIntentFilter("www.google.com", kPackageName));
221 instance_->OnIntentFiltersUpdated(std::move(array));
222
223 EXPECT_FALSE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
224 EXPECT_FALSE(
225 instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
226
227 EXPECT_TRUE(
228 instance_->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
229 }
230
231 // Tests the same with multiple filters.
TEST_F(ArcIntentHelperTest,TestMultipleFilters)232 TEST_F(ArcIntentHelperTest, TestMultipleFilters) {
233 std::vector<IntentFilter> array;
234 array.emplace_back(GetIntentFilter("www.google.com", kPackageName));
235 array.emplace_back(GetIntentFilter("www.google.co.uk", kPackageName));
236 array.emplace_back(GetIntentFilter("dev.chromium.org", kPackageName));
237 instance_->OnIntentFiltersUpdated(std::move(array));
238
239 EXPECT_FALSE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
240 EXPECT_FALSE(
241 instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
242 EXPECT_FALSE(
243 instance_->ShouldChromeHandleUrl(GURL("http://www.google.co.uk")));
244 EXPECT_FALSE(
245 instance_->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
246 EXPECT_FALSE(
247 instance_->ShouldChromeHandleUrl(GURL("http://dev.chromium.org")));
248 EXPECT_FALSE(
249 instance_->ShouldChromeHandleUrl(GURL("https://dev.chromium.org")));
250
251 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.android.com")));
252 }
253
254 // Tests that ShouldChromeHandleUrl returns true for non http(s) URLs.
TEST_F(ArcIntentHelperTest,TestNonHttp)255 TEST_F(ArcIntentHelperTest, TestNonHttp) {
256 std::vector<IntentFilter> array;
257 array.emplace_back(GetIntentFilter("www.google.com", kPackageName));
258 instance_->OnIntentFiltersUpdated(std::move(array));
259
260 EXPECT_TRUE(
261 instance_->ShouldChromeHandleUrl(GURL("chrome://www.google.com")));
262 EXPECT_TRUE(
263 instance_->ShouldChromeHandleUrl(GURL("custom://www.google.com")));
264 }
265
266 // Tests that ShouldChromeHandleUrl discards the previous filters when
267 // UpdateIntentFilters is called with new ones.
TEST_F(ArcIntentHelperTest,TestMultipleUpdate)268 TEST_F(ArcIntentHelperTest, TestMultipleUpdate) {
269 std::vector<IntentFilter> array;
270 array.emplace_back(GetIntentFilter("www.google.com", kPackageName));
271 array.emplace_back(GetIntentFilter("dev.chromium.org", kPackageName));
272 instance_->OnIntentFiltersUpdated(std::move(array));
273
274 std::vector<IntentFilter> array2;
275 array2.emplace_back(GetIntentFilter("www.google.co.uk", kPackageName));
276 array2.emplace_back(GetIntentFilter("dev.chromium.org", kPackageName));
277 array2.emplace_back(GetIntentFilter("www.android.com", kPackageName));
278 instance_->OnIntentFiltersUpdated(std::move(array2));
279
280 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
281 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
282 EXPECT_FALSE(
283 instance_->ShouldChromeHandleUrl(GURL("http://www.google.co.uk")));
284 EXPECT_FALSE(
285 instance_->ShouldChromeHandleUrl(GURL("https://www.google.co.uk")));
286 EXPECT_FALSE(
287 instance_->ShouldChromeHandleUrl(GURL("http://dev.chromium.org")));
288 EXPECT_FALSE(
289 instance_->ShouldChromeHandleUrl(GURL("https://dev.chromium.org")));
290 EXPECT_FALSE(
291 instance_->ShouldChromeHandleUrl(GURL("http://www.android.com")));
292 EXPECT_FALSE(
293 instance_->ShouldChromeHandleUrl(GURL("https://www.android.com")));
294 }
295
296 // Tests that intent helper app (on ARC) is not taken as an app candidate, other
297 // suitable app candidates should still match if possible.
TEST_F(ArcIntentHelperTest,TestIntentHelperAppIsNotAValidCandidate)298 TEST_F(ArcIntentHelperTest, TestIntentHelperAppIsNotAValidCandidate) {
299 std::vector<IntentFilter> array;
300 array.emplace_back(GetIntentFilter(
301 "www.google.com", ArcIntentHelperBridge::kArcIntentHelperPackageName));
302 array.emplace_back(GetIntentFilter(
303 "www.android.com", ArcIntentHelperBridge::kArcIntentHelperPackageName));
304 // Let the package name start with "z" to ensure the intent helper package
305 // is not always the last package checked in the ShouldChromeHandleUrl
306 // filter matching logic. This is to ensure this unit test tests the package
307 // name checking logic properly.
308 array.emplace_back(GetIntentFilter("dev.chromium.org", "z.package.name"));
309 instance_->OnIntentFiltersUpdated(std::move(array));
310
311 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.google.com")));
312 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("https://www.google.com")));
313 EXPECT_TRUE(instance_->ShouldChromeHandleUrl(GURL("http://www.android.com")));
314 EXPECT_TRUE(
315 instance_->ShouldChromeHandleUrl(GURL("https://www.android.com")));
316 EXPECT_FALSE(
317 instance_->ShouldChromeHandleUrl(GURL("http://dev.chromium.org")));
318 EXPECT_FALSE(
319 instance_->ShouldChromeHandleUrl(GURL("https://dev.chromium.org")));
320 }
321
322 // Tests that OnOpenUrl opens the URL in Chrome browser.
TEST_F(ArcIntentHelperTest,TestOnOpenUrl)323 TEST_F(ArcIntentHelperTest, TestOnOpenUrl) {
324 instance_->OnOpenUrl("http://google.com");
325 EXPECT_EQ(GURL("http://google.com"),
326 test_open_url_delegate_->TakeLastOpenedUrl());
327
328 instance_->OnOpenUrl("https://google.com");
329 EXPECT_EQ(GURL("https://google.com"),
330 test_open_url_delegate_->TakeLastOpenedUrl());
331 }
332
333 // Tests that OnOpenWebApp opens only HTTPS URLs.
TEST_F(ArcIntentHelperTest,TestOnOpenWebApp)334 TEST_F(ArcIntentHelperTest, TestOnOpenWebApp) {
335 instance_->OnOpenWebApp("http://google.com");
336 EXPECT_EQ(GURL(), test_open_url_delegate_->TakeLastOpenedUrl());
337
338 instance_->OnOpenWebApp("https://google.com");
339 EXPECT_EQ(GURL("https://google.com"),
340 test_open_url_delegate_->TakeLastOpenedUrl());
341 }
342
343 // Tests that OnOpenUrl does not open URLs with the 'chrome://' and equivalent
344 // schemes like 'about:'.
TEST_F(ArcIntentHelperTest,TestOnOpenUrl_ChromeScheme)345 TEST_F(ArcIntentHelperTest, TestOnOpenUrl_ChromeScheme) {
346 instance_->OnOpenUrl("chrome://www.google.com");
347 EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid());
348
349 instance_->OnOpenUrl("chrome://settings");
350 EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid());
351
352 instance_->OnOpenUrl("about:");
353 EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid());
354
355 instance_->OnOpenUrl("about:settings");
356 EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid());
357
358 instance_->OnOpenUrl("about:blank");
359 EXPECT_FALSE(test_open_url_delegate_->TakeLastOpenedUrl().is_valid());
360 }
361
362 // Tests that AppendStringToIntentHelperPackageName works.
TEST_F(ArcIntentHelperTest,TestAppendStringToIntentHelperPackageName)363 TEST_F(ArcIntentHelperTest, TestAppendStringToIntentHelperPackageName) {
364 std::string package_name = ArcIntentHelperBridge::kArcIntentHelperPackageName;
365 std::string fake_activity = "this_is_a_fake_activity";
366 EXPECT_EQ(ArcIntentHelperBridge::AppendStringToIntentHelperPackageName(
367 fake_activity),
368 package_name + "." + fake_activity);
369
370 const std::string empty_string;
371 EXPECT_EQ(ArcIntentHelperBridge::AppendStringToIntentHelperPackageName(
372 empty_string),
373 package_name + ".");
374 }
375
376 } // namespace arc
377