1 // Copyright 2017 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 "content/browser/geolocation/geolocation_service_impl.h"
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/run_loop.h"
10 #include "content/browser/permissions/permission_controller_impl.h"
11 #include "content/public/browser/device_service.h"
12 #include "content/public/browser/permission_controller.h"
13 #include "content/public/browser/permission_type.h"
14 #include "content/public/test/mock_permission_manager.h"
15 #include "content/public/test/navigation_simulator.h"
16 #include "content/public/test/test_browser_context.h"
17 #include "content/test/test_render_frame_host.h"
18 #include "mojo/public/cpp/bindings/remote.h"
19 #include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
20 #include "services/device/public/mojom/geolocation.mojom.h"
21 #include "services/device/public/mojom/geolocation_context.mojom.h"
22 #include "services/device/public/mojom/geoposition.mojom.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom.h"
25 
26 using blink::mojom::GeolocationService;
27 using blink::mojom::PermissionStatus;
28 using device::mojom::Geolocation;
29 using device::mojom::GeopositionPtr;
30 
31 namespace content {
32 namespace {
33 
34 using PermissionCallback = base::OnceCallback<void(PermissionStatus)>;
35 
36 double kMockLatitude = 1.0;
37 double kMockLongitude = 10.0;
38 
39 class TestPermissionManager : public MockPermissionManager {
40  public:
TestPermissionManager()41   TestPermissionManager()
42       : request_id_(PermissionController::kNoPendingOperation) {}
43   ~TestPermissionManager() override = default;
44 
RequestPermission(PermissionType permissions,RenderFrameHost * render_frame_host,const GURL & requesting_origin,bool user_gesture,PermissionCallback callback)45   int RequestPermission(PermissionType permissions,
46                         RenderFrameHost* render_frame_host,
47                         const GURL& requesting_origin,
48                         bool user_gesture,
49                         PermissionCallback callback) override {
50     EXPECT_EQ(permissions, PermissionType::GEOLOCATION);
51     EXPECT_TRUE(user_gesture);
52     request_callback_.Run(std::move(callback));
53     return request_id_;
54   }
55 
SetRequestId(int request_id)56   void SetRequestId(int request_id) { request_id_ = request_id; }
57 
SetRequestCallback(base::RepeatingCallback<void (PermissionCallback)> request_callback)58   void SetRequestCallback(
59       base::RepeatingCallback<void(PermissionCallback)> request_callback) {
60     request_callback_ = std::move(request_callback);
61   }
62 
63  private:
64   int request_id_;
65 
66   base::RepeatingCallback<void(PermissionCallback)> request_callback_;
67 };
68 
69 class GeolocationServiceTest : public RenderViewHostImplTestHarness {
70  protected:
GeolocationServiceTest()71   GeolocationServiceTest() {}
72 
~GeolocationServiceTest()73   ~GeolocationServiceTest() override {}
74 
SetUp()75   void SetUp() override {
76     RenderViewHostImplTestHarness::SetUp();
77     NavigateAndCommit(GURL("https://www.google.com/maps"));
78     browser_context_.reset(new content::TestBrowserContext());
79     browser_context_->SetPermissionControllerDelegate(
80         std::make_unique<TestPermissionManager>());
81 
82     geolocation_overrider_ =
83         std::make_unique<device::ScopedGeolocationOverrider>(kMockLatitude,
84                                                              kMockLongitude);
85     GetDeviceService().BindGeolocationContext(
86         context_.BindNewPipeAndPassReceiver());
87   }
88 
TearDown()89   void TearDown() override {
90     context_.reset();
91     geolocation_overrider_.reset();
92     browser_context_.reset();
93     RenderViewHostImplTestHarness::TearDown();
94   }
95 
CreateEmbeddedFrameAndGeolocationService(bool allow_via_feature_policy)96   void CreateEmbeddedFrameAndGeolocationService(bool allow_via_feature_policy) {
97     const GURL kEmbeddedUrl("https://embeddables.com/someframe");
98     if (allow_via_feature_policy) {
99       RenderFrameHostTester::For(main_rfh())
100           ->SimulateFeaturePolicyHeader(
101               blink::mojom::FeaturePolicyFeature::kGeolocation,
102               std::vector<url::Origin>{url::Origin::Create(kEmbeddedUrl)});
103     }
104     RenderFrameHost* embedded_rfh =
105         RenderFrameHostTester::For(main_rfh())->AppendChild("");
106     RenderFrameHostTester::For(embedded_rfh)->InitializeRenderFrameIfNeeded();
107     auto navigation_simulator = NavigationSimulator::CreateRendererInitiated(
108         kEmbeddedUrl, embedded_rfh);
109     navigation_simulator->Commit();
110     embedded_rfh = navigation_simulator->GetFinalRenderFrameHost();
111 
112     BrowserContext::SetPermissionControllerForTesting(
113         embedded_rfh->GetProcess()->GetBrowserContext(),
114         std::make_unique<PermissionControllerImpl>(browser_context_.get()));
115     service_.reset(new GeolocationServiceImpl(context_.get(), embedded_rfh));
116     service_->Bind(service_remote_.BindNewPipeAndPassReceiver());
117   }
118 
service_remote()119   mojo::Remote<blink::mojom::GeolocationService>& service_remote() {
120     return service_remote_;
121   }
122 
permission_manager()123   TestPermissionManager* permission_manager() {
124     return static_cast<TestPermissionManager*>(
125         browser_context_->GetPermissionControllerDelegate());
126   }
127 
128  private:
129   std::unique_ptr<device::ScopedGeolocationOverrider> geolocation_overrider_;
130 
131   std::unique_ptr<TestBrowserContext> browser_context_;
132   std::unique_ptr<GeolocationServiceImpl> service_;
133   mojo::Remote<blink::mojom::GeolocationService> service_remote_;
134   mojo::Remote<device::mojom::GeolocationContext> context_;
135 
136   DISALLOW_COPY_AND_ASSIGN(GeolocationServiceTest);
137 };
138 
139 }  // namespace
140 
TEST_F(GeolocationServiceTest,PermissionGrantedPolicyViolation)141 TEST_F(GeolocationServiceTest, PermissionGrantedPolicyViolation) {
142   // The embedded frame is not allowed.
143   CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/false);
144 
145   permission_manager()->SetRequestCallback(
146       base::BindRepeating([](PermissionCallback callback) {
147         ADD_FAILURE() << "Permissions checked unexpectedly.";
148       }));
149   mojo::Remote<Geolocation> geolocation;
150   service_remote()->CreateGeolocation(
151       geolocation.BindNewPipeAndPassReceiver(), true,
152       base::BindRepeating([](blink::mojom::PermissionStatus status) {
153         EXPECT_EQ(blink::mojom::PermissionStatus::DENIED, status);
154       }));
155 
156   base::RunLoop loop;
157   geolocation.set_disconnect_handler(loop.QuitClosure());
158 
159   geolocation->QueryNextPosition(base::BindOnce([](GeopositionPtr geoposition) {
160     ADD_FAILURE() << "Position updated unexpectedly";
161   }));
162   loop.Run();
163 }
164 
TEST_F(GeolocationServiceTest,PermissionGrantedNoPolicyViolation)165 TEST_F(GeolocationServiceTest, PermissionGrantedNoPolicyViolation) {
166   // Allow the embedded frame.
167   CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
168 
169   permission_manager()->SetRequestCallback(
170       base::BindRepeating([](PermissionCallback callback) {
171         std::move(callback).Run(PermissionStatus::GRANTED);
172       }));
173   mojo::Remote<Geolocation> geolocation;
174   service_remote()->CreateGeolocation(
175       geolocation.BindNewPipeAndPassReceiver(), true,
176       base::BindRepeating([](blink::mojom::PermissionStatus status) {
177         EXPECT_EQ(blink::mojom::PermissionStatus::GRANTED, status);
178       }));
179 
180   base::RunLoop loop;
181   geolocation.set_disconnect_handler(base::BindOnce(
182       [] { ADD_FAILURE() << "Connection error handler called unexpectedly"; }));
183 
184   geolocation->QueryNextPosition(base::BindOnce(
185       [](base::OnceClosure callback, GeopositionPtr geoposition) {
186         EXPECT_DOUBLE_EQ(kMockLatitude, geoposition->latitude);
187         EXPECT_DOUBLE_EQ(kMockLongitude, geoposition->longitude);
188         std::move(callback).Run();
189       },
190       loop.QuitClosure()));
191   loop.Run();
192 }
193 
TEST_F(GeolocationServiceTest,PermissionGrantedSync)194 TEST_F(GeolocationServiceTest, PermissionGrantedSync) {
195   CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
196   permission_manager()->SetRequestCallback(
197       base::BindRepeating([](PermissionCallback callback) {
198         std::move(callback).Run(PermissionStatus::GRANTED);
199       }));
200   mojo::Remote<Geolocation> geolocation;
201   service_remote()->CreateGeolocation(
202       geolocation.BindNewPipeAndPassReceiver(), true,
203       base::BindRepeating([](blink::mojom::PermissionStatus status) {
204         EXPECT_EQ(blink::mojom::PermissionStatus::GRANTED, status);
205       }));
206 
207   base::RunLoop loop;
208   geolocation.set_disconnect_handler(base::BindOnce(
209       [] { ADD_FAILURE() << "Connection error handler called unexpectedly"; }));
210 
211   geolocation->QueryNextPosition(base::BindOnce(
212       [](base::OnceClosure callback, GeopositionPtr geoposition) {
213         EXPECT_DOUBLE_EQ(kMockLatitude, geoposition->latitude);
214         EXPECT_DOUBLE_EQ(kMockLongitude, geoposition->longitude);
215         std::move(callback).Run();
216       },
217       loop.QuitClosure()));
218   loop.Run();
219 }
220 
TEST_F(GeolocationServiceTest,PermissionDeniedSync)221 TEST_F(GeolocationServiceTest, PermissionDeniedSync) {
222   CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
223   permission_manager()->SetRequestCallback(
224       base::BindRepeating([](PermissionCallback callback) {
225         std::move(callback).Run(PermissionStatus::DENIED);
226       }));
227   mojo::Remote<Geolocation> geolocation;
228   service_remote()->CreateGeolocation(
229       geolocation.BindNewPipeAndPassReceiver(), true,
230       base::BindRepeating([](blink::mojom::PermissionStatus status) {
231         EXPECT_EQ(blink::mojom::PermissionStatus::DENIED, status);
232       }));
233 
234   base::RunLoop loop;
235   geolocation.set_disconnect_handler(loop.QuitClosure());
236 
237   geolocation->QueryNextPosition(base::BindOnce([](GeopositionPtr geoposition) {
238     ADD_FAILURE() << "Position updated unexpectedly";
239   }));
240   loop.Run();
241 }
242 
TEST_F(GeolocationServiceTest,PermissionGrantedAsync)243 TEST_F(GeolocationServiceTest, PermissionGrantedAsync) {
244   CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
245   permission_manager()->SetRequestId(42);
246   permission_manager()->SetRequestCallback(
247       base::BindRepeating([](PermissionCallback permission_callback) {
248         base::ThreadTaskRunnerHandle::Get()->PostTask(
249             FROM_HERE, base::BindOnce(std::move(permission_callback),
250                                       PermissionStatus::GRANTED));
251       }));
252   mojo::Remote<Geolocation> geolocation;
253   service_remote()->CreateGeolocation(
254       geolocation.BindNewPipeAndPassReceiver(), true,
255       base::BindRepeating([](blink::mojom::PermissionStatus status) {
256         EXPECT_EQ(blink::mojom::PermissionStatus::GRANTED, status);
257       }));
258 
259   base::RunLoop loop;
260   geolocation.set_disconnect_handler(base::BindOnce(
261       [] { ADD_FAILURE() << "Connection error handler called unexpectedly"; }));
262 
263   geolocation->QueryNextPosition(base::BindOnce(
264       [](base::OnceClosure callback, GeopositionPtr geoposition) {
265         EXPECT_DOUBLE_EQ(kMockLatitude, geoposition->latitude);
266         EXPECT_DOUBLE_EQ(kMockLongitude, geoposition->longitude);
267         std::move(callback).Run();
268       },
269       loop.QuitClosure()));
270   loop.Run();
271 }
272 
TEST_F(GeolocationServiceTest,PermissionDeniedAsync)273 TEST_F(GeolocationServiceTest, PermissionDeniedAsync) {
274   CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
275   permission_manager()->SetRequestId(42);
276   permission_manager()->SetRequestCallback(
277       base::BindRepeating([](PermissionCallback permission_callback) {
278         base::ThreadTaskRunnerHandle::Get()->PostTask(
279             FROM_HERE, base::BindOnce(std::move(permission_callback),
280                                       PermissionStatus::DENIED));
281       }));
282   mojo::Remote<Geolocation> geolocation;
283   service_remote()->CreateGeolocation(
284       geolocation.BindNewPipeAndPassReceiver(), true,
285       base::BindRepeating([](blink::mojom::PermissionStatus status) {
286         EXPECT_EQ(blink::mojom::PermissionStatus::DENIED, status);
287       }));
288 
289   base::RunLoop loop;
290   geolocation.set_disconnect_handler(loop.QuitClosure());
291 
292   geolocation->QueryNextPosition(base::BindOnce([](GeopositionPtr geoposition) {
293     ADD_FAILURE() << "Position updated unexpectedly";
294   }));
295   loop.Run();
296 }
297 
TEST_F(GeolocationServiceTest,ServiceClosedBeforePermissionResponse)298 TEST_F(GeolocationServiceTest, ServiceClosedBeforePermissionResponse) {
299   CreateEmbeddedFrameAndGeolocationService(/*allow_via_feature_policy=*/true);
300   permission_manager()->SetRequestId(42);
301   mojo::Remote<Geolocation> geolocation;
302   service_remote()->CreateGeolocation(
303       geolocation.BindNewPipeAndPassReceiver(), true,
304       base::BindRepeating([](blink::mojom::PermissionStatus) {
305         ADD_FAILURE() << "PositionStatus received unexpectedly.";
306       }));
307   // Don't immediately respond to the request.
308   permission_manager()->SetRequestCallback(base::DoNothing());
309 
310   base::RunLoop loop;
311   service_remote().reset();
312 
313   geolocation->QueryNextPosition(base::BindOnce([](GeopositionPtr geoposition) {
314     ADD_FAILURE() << "Position updated unexpectedly";
315   }));
316   loop.RunUntilIdle();
317 }
318 
319 }  // namespace content
320