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