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