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