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 <vector>
6
7 #include "base/callback_helpers.h"
8 #include "base/containers/unique_ptr_adapters.h"
9 #include "mojo/public/cpp/bindings/pending_receiver.h"
10 #include "mojo/public/cpp/bindings/receiver.h"
11 #include "mojo/public/cpp/bindings/receiver_set.h"
12 #include "services/device/device_service.h"
13 #include "services/device/public/cpp/geolocation/geoposition.h"
14 #include "services/device/public/cpp/test/scoped_geolocation_overrider.h"
15 #include "services/device/public/mojom/geolocation.mojom.h"
16 #include "services/device/public/mojom/geolocation_context.mojom.h"
17
18 namespace device {
19
20 // This class is a fake implementation of GeolocationContext and Geolocation
21 // mojo interfaces for those tests which want to set an override geoposition
22 // value and verify their code where there are geolocation mojo calls.
23 class ScopedGeolocationOverrider::FakeGeolocationContext
24 : public mojom::GeolocationContext {
25 public:
26 explicit FakeGeolocationContext(mojom::GeopositionPtr position);
27 ~FakeGeolocationContext() override;
28
29 void UpdateLocation(mojom::GeopositionPtr position);
30 const mojom::Geoposition* GetGeoposition() const;
31
32 void Pause();
33 void Resume();
34
35 size_t GetGeolocationInstanceCount() const;
36
37 void BindForOverrideService(
38 mojo::PendingReceiver<mojom::GeolocationContext> receiver);
39 void OnDisconnect(FakeGeolocation* impl);
40
41 // mojom::GeolocationContext implementation:
42 void BindGeolocation(mojo::PendingReceiver<mojom::Geolocation> receiver,
43 const GURL& requesting_origin) override;
44 void SetOverride(mojom::GeopositionPtr geoposition) override;
45 void ClearOverride() override;
46
is_paused() const47 bool is_paused() const { return is_paused_; }
set_close_callback(base::RepeatingClosure callback)48 void set_close_callback(base::RepeatingClosure callback) {
49 close_callback_ = std::move(callback);
50 }
51
52 private:
53 mojom::GeopositionPtr position_;
54 // |override_position_| enables overriding the override set by this class, as
55 // required by the mojom::GeolocationContext interface.
56 mojom::GeopositionPtr override_position_;
57 std::set<std::unique_ptr<FakeGeolocation>, base::UniquePtrComparator> impls_;
58 mojo::ReceiverSet<mojom::GeolocationContext> context_receivers_;
59 bool is_paused_ = false;
60 base::RepeatingClosure close_callback_;
61 };
62
63 class ScopedGeolocationOverrider::FakeGeolocation : public mojom::Geolocation {
64 public:
65 FakeGeolocation(mojo::PendingReceiver<mojom::Geolocation> receiver,
66 FakeGeolocationContext* context);
67 ~FakeGeolocation() override;
68
69 void OnDisconnect();
70 void OnResume();
71
72 void UpdateLocation();
73
74 // mojom::Geolocation implementation:
75 void QueryNextPosition(QueryNextPositionCallback callback) override;
76 void SetHighAccuracy(bool high_accuracy) override;
77
78 private:
79 void RunPositionCallbackIfNeeded();
80
81 FakeGeolocationContext* context_;
82 bool needs_update_ = true;
83 QueryNextPositionCallback position_callback_;
84 mojo::Receiver<mojom::Geolocation> receiver_{this};
85 };
86
ScopedGeolocationOverrider(mojom::GeopositionPtr position)87 ScopedGeolocationOverrider::ScopedGeolocationOverrider(
88 mojom::GeopositionPtr position) {
89 OverrideGeolocation(std::move(position));
90 }
91
ScopedGeolocationOverrider(double latitude,double longitude)92 ScopedGeolocationOverrider::ScopedGeolocationOverrider(double latitude,
93 double longitude) {
94 auto position = mojom::Geoposition::New();
95 position->latitude = latitude;
96 position->longitude = longitude;
97 position->altitude = 0.;
98 position->accuracy = 0.;
99 position->timestamp = base::Time::Now();
100
101 OverrideGeolocation(std::move(position));
102 }
103
~ScopedGeolocationOverrider()104 ScopedGeolocationOverrider::~ScopedGeolocationOverrider() {
105 DeviceService::OverrideGeolocationContextBinderForTesting(
106 base::NullCallback());
107 }
108
OverrideGeolocation(mojom::GeopositionPtr position)109 void ScopedGeolocationOverrider::OverrideGeolocation(
110 mojom::GeopositionPtr position) {
111 geolocation_context_ =
112 std::make_unique<FakeGeolocationContext>(std::move(position));
113 DeviceService::OverrideGeolocationContextBinderForTesting(
114 base::BindRepeating(&FakeGeolocationContext::BindForOverrideService,
115 base::Unretained(geolocation_context_.get())));
116 }
117
UpdateLocation(mojom::GeopositionPtr position)118 void ScopedGeolocationOverrider::UpdateLocation(
119 mojom::GeopositionPtr position) {
120 geolocation_context_->UpdateLocation(std::move(position));
121 }
122
UpdateLocation(double latitude,double longitude)123 void ScopedGeolocationOverrider::UpdateLocation(double latitude,
124 double longitude) {
125 auto position = mojom::Geoposition::New();
126 position->latitude = latitude;
127 position->longitude = longitude;
128 position->altitude = 0.;
129 position->accuracy = 0.;
130 position->timestamp = base::Time::Now();
131
132 UpdateLocation(std::move(position));
133 }
134
Pause()135 void ScopedGeolocationOverrider::Pause() {
136 geolocation_context_->Pause();
137 }
138
Resume()139 void ScopedGeolocationOverrider::Resume() {
140 geolocation_context_->Resume();
141 }
142
GetGeolocationInstanceCount() const143 size_t ScopedGeolocationOverrider::GetGeolocationInstanceCount() const {
144 return geolocation_context_->GetGeolocationInstanceCount();
145 }
146
SetGeolocationCloseCallback(base::RepeatingClosure closure)147 void ScopedGeolocationOverrider::SetGeolocationCloseCallback(
148 base::RepeatingClosure closure) {
149 geolocation_context_->set_close_callback(std::move(closure));
150 }
151
FakeGeolocationContext(mojom::GeopositionPtr position)152 ScopedGeolocationOverrider::FakeGeolocationContext::FakeGeolocationContext(
153 mojom::GeopositionPtr position)
154 : position_(std::move(position)) {
155 if (position_) {
156 position_->valid = false;
157 if (ValidateGeoposition(*position_))
158 position_->valid = true;
159 }
160 }
161
~FakeGeolocationContext()162 ScopedGeolocationOverrider::FakeGeolocationContext::~FakeGeolocationContext() {}
163
UpdateLocation(mojom::GeopositionPtr position)164 void ScopedGeolocationOverrider::FakeGeolocationContext::UpdateLocation(
165 mojom::GeopositionPtr position) {
166 position_ = std::move(position);
167
168 if (!position_)
169 return;
170
171 position_->valid = false;
172 if (ValidateGeoposition(*position_))
173 position_->valid = true;
174
175 for (auto& impl : impls_) {
176 impl->UpdateLocation();
177 }
178 }
179
OnDisconnect(FakeGeolocation * impl)180 void ScopedGeolocationOverrider::FakeGeolocationContext::OnDisconnect(
181 FakeGeolocation* impl) {
182 // Note: We can't use set::erase() here, since FakeGeolocation* is not
183 // the impls_::key_type.
184 auto it = impls_.find(impl);
185 impls_.erase(it);
186
187 if (!close_callback_.is_null())
188 close_callback_.Run();
189 }
190
191 const mojom::Geoposition*
GetGeoposition() const192 ScopedGeolocationOverrider::FakeGeolocationContext::GetGeoposition() const {
193 if (override_position_)
194 return override_position_.get();
195
196 return position_.get();
197 }
198
BindForOverrideService(mojo::PendingReceiver<mojom::GeolocationContext> receiver)199 void ScopedGeolocationOverrider::FakeGeolocationContext::BindForOverrideService(
200 mojo::PendingReceiver<mojom::GeolocationContext> receiver) {
201 context_receivers_.Add(this, std::move(receiver));
202 }
203
BindGeolocation(mojo::PendingReceiver<mojom::Geolocation> receiver,const GURL & requesting_origin)204 void ScopedGeolocationOverrider::FakeGeolocationContext::BindGeolocation(
205 mojo::PendingReceiver<mojom::Geolocation> receiver,
206 const GURL& requesting_origin) {
207 impls_.insert(std::make_unique<FakeGeolocation>(std::move(receiver), this));
208 }
209
SetOverride(mojom::GeopositionPtr geoposition)210 void ScopedGeolocationOverrider::FakeGeolocationContext::SetOverride(
211 mojom::GeopositionPtr geoposition) {
212 override_position_ = std::move(geoposition);
213 if (override_position_.is_null())
214 return;
215
216 override_position_->valid = false;
217 if (ValidateGeoposition(*override_position_))
218 override_position_->valid = true;
219
220 for (auto& impl : impls_) {
221 impl->UpdateLocation();
222 }
223 }
224
ClearOverride()225 void ScopedGeolocationOverrider::FakeGeolocationContext::ClearOverride() {
226 override_position_.reset();
227 }
228
Pause()229 void ScopedGeolocationOverrider::FakeGeolocationContext::Pause() {
230 is_paused_ = true;
231 }
232
Resume()233 void ScopedGeolocationOverrider::FakeGeolocationContext::Resume() {
234 is_paused_ = false;
235 for (auto& impl : impls_) {
236 impl->OnResume();
237 }
238 }
239
240 size_t ScopedGeolocationOverrider::FakeGeolocationContext::
GetGeolocationInstanceCount() const241 GetGeolocationInstanceCount() const {
242 return impls_.size();
243 }
244
FakeGeolocation(mojo::PendingReceiver<mojom::Geolocation> receiver,FakeGeolocationContext * context)245 ScopedGeolocationOverrider::FakeGeolocation::FakeGeolocation(
246 mojo::PendingReceiver<mojom::Geolocation> receiver,
247 FakeGeolocationContext* context)
248 : context_(context) {
249 receiver_.Bind(std::move(receiver));
250 receiver_.set_disconnect_handler(
251 base::BindOnce(&ScopedGeolocationOverrider::FakeGeolocation::OnDisconnect,
252 base::Unretained(this)));
253 }
254
~FakeGeolocation()255 ScopedGeolocationOverrider::FakeGeolocation::~FakeGeolocation() {}
256
OnDisconnect()257 void ScopedGeolocationOverrider::FakeGeolocation::OnDisconnect() {
258 context_->OnDisconnect(this);
259 }
260
OnResume()261 void ScopedGeolocationOverrider::FakeGeolocation::OnResume() {
262 DCHECK(!context_->is_paused());
263 RunPositionCallbackIfNeeded();
264 }
265
266 void ScopedGeolocationOverrider::FakeGeolocation::
RunPositionCallbackIfNeeded()267 RunPositionCallbackIfNeeded() {
268 // No need to run position callback if paused or no new position pending.
269 if (context_->is_paused() || !needs_update_)
270 return;
271
272 if (position_callback_.is_null())
273 return;
274
275 const mojom::Geoposition* position = context_->GetGeoposition();
276 if (!position)
277 return;
278
279 std::move(position_callback_).Run(position->Clone());
280 needs_update_ = false;
281 }
282
UpdateLocation()283 void ScopedGeolocationOverrider::FakeGeolocation::UpdateLocation() {
284 // Needs update for new position.
285 needs_update_ = true;
286
287 RunPositionCallbackIfNeeded();
288 }
289
QueryNextPosition(QueryNextPositionCallback callback)290 void ScopedGeolocationOverrider::FakeGeolocation::QueryNextPosition(
291 QueryNextPositionCallback callback) {
292 // Pending callbacks might be overrided.
293 position_callback_ = std::move(callback);
294
295 RunPositionCallbackIfNeeded();
296 }
297
SetHighAccuracy(bool high_accuracy)298 void ScopedGeolocationOverrider::FakeGeolocation::SetHighAccuracy(
299 bool high_accuracy) {}
300
301 } // namespace device
302