- add sources.
[platform/framework/web/crosswalk.git] / src / content / browser / geolocation / geolocation_provider_impl.cc
1 // Copyright (c) 2012 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_provider_impl.h"
6
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/callback.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/memory/singleton.h"
13 #include "base/message_loop/message_loop.h"
14 #include "content/browser/geolocation/location_arbitrator_impl.h"
15 #include "content/public/browser/browser_thread.h"
16
17 namespace content {
18
19 namespace {
20 void OverrideLocationForTestingOnIOThread(
21     const Geoposition& position,
22     const base::Closure& completion_callback,
23     scoped_refptr<base::MessageLoopProxy> callback_loop) {
24   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
25   GeolocationProviderImpl::GetInstance()->OverrideLocationForTesting(position);
26   callback_loop->PostTask(FROM_HERE, completion_callback);
27 }
28 }  // namespace
29
30 GeolocationProvider* GeolocationProvider::GetInstance() {
31   return GeolocationProviderImpl::GetInstance();
32 }
33
34 void GeolocationProvider::OverrideLocationForTesting(
35     const Geoposition& position,
36     const base::Closure& completion_callback) {
37   base::Closure closure = base::Bind(&OverrideLocationForTestingOnIOThread,
38                                      position,
39                                      completion_callback,
40                                      base::MessageLoopProxy::current());
41   if (!BrowserThread::CurrentlyOn(BrowserThread::IO))
42     BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, closure);
43   else
44     closure.Run();
45 }
46
47 void GeolocationProviderImpl::AddLocationUpdateCallback(
48     const LocationUpdateCallback& callback, bool use_high_accuracy) {
49   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
50   bool found = false;
51   CallbackList::iterator i = callbacks_.begin();  
52   for (; i != callbacks_.end(); ++i) {
53     if (i->first.Equals(callback)) {
54       i->second = use_high_accuracy;
55       found = true;
56       break;
57     }
58   }
59   if (!found)
60     callbacks_.push_back(std::make_pair(callback, use_high_accuracy));
61
62   OnClientsChanged();
63   if (position_.Validate() ||
64       position_.error_code != Geoposition::ERROR_CODE_NONE) {
65     callback.Run(position_);
66   }
67 }
68
69 bool GeolocationProviderImpl::RemoveLocationUpdateCallback(
70     const LocationUpdateCallback& callback) {
71   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
72   bool removed = false;
73   CallbackList::iterator i = callbacks_.begin();
74   for (; i != callbacks_.end(); ++i) {
75     if (i->first.Equals(callback)) {
76       callbacks_.erase(i);
77       removed = true;
78       break;
79     }
80   }
81   if (removed)
82     OnClientsChanged();
83   return removed;
84 }
85
86 void GeolocationProviderImpl::UserDidOptIntoLocationServices() {
87   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
88   bool was_permission_granted = user_did_opt_into_location_services_;
89   user_did_opt_into_location_services_ = true;
90   if (IsRunning() && !was_permission_granted)
91     InformProvidersPermissionGranted();
92 }
93
94 bool GeolocationProviderImpl::LocationServicesOptedIn() const {
95   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
96   return user_did_opt_into_location_services_;
97 }
98
99 void GeolocationProviderImpl::OnLocationUpdate(const Geoposition& position) {
100   DCHECK(OnGeolocationThread());
101   // Will be true only in testing.
102   if (ignore_location_updates_)
103     return;
104   BrowserThread::PostTask(BrowserThread::IO,
105                           FROM_HERE,
106                           base::Bind(&GeolocationProviderImpl::NotifyClients,
107                                      base::Unretained(this), position));
108 }
109
110 void GeolocationProviderImpl::OverrideLocationForTesting(
111     const Geoposition& position) {
112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
113   position_ = position;
114   ignore_location_updates_ = true;
115   NotifyClients(position);
116 }
117
118 GeolocationProviderImpl* GeolocationProviderImpl::GetInstance() {
119   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
120   return Singleton<GeolocationProviderImpl>::get();
121 }
122
123 GeolocationProviderImpl::GeolocationProviderImpl()
124     : base::Thread("Geolocation"),
125       user_did_opt_into_location_services_(false),
126       ignore_location_updates_(false),
127       arbitrator_(NULL) {
128   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
129 }
130
131 GeolocationProviderImpl::~GeolocationProviderImpl() {
132   // All callbacks should have unregistered before this singleton is destructed.
133   DCHECK(callbacks_.empty());
134   Stop();
135   DCHECK(!arbitrator_);
136 }
137
138 bool GeolocationProviderImpl::OnGeolocationThread() const {
139   return base::MessageLoop::current() == message_loop();
140 }
141
142 void GeolocationProviderImpl::OnClientsChanged() {
143   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
144   base::Closure task;
145   if (callbacks_.empty()) {
146     DCHECK(IsRunning());
147     // We have no more observers, so we clear the cached geoposition so that
148     // when the next observer is added we will not provide a stale position.
149     position_ = Geoposition();
150     task = base::Bind(&GeolocationProviderImpl::StopProviders,
151                       base::Unretained(this));
152   } else {
153     if (!IsRunning()) {
154       Start();
155       if (LocationServicesOptedIn())
156         InformProvidersPermissionGranted();
157     }
158     // Determine a set of options that satisfies all clients.
159     bool use_high_accuracy = false;
160     CallbackList::iterator i = callbacks_.begin();
161     for (; i != callbacks_.end(); ++i) {
162       if (i->second) {
163         use_high_accuracy = true;
164         break;
165       }
166     }
167
168     // Send the current options to the providers as they may have changed.
169     task = base::Bind(&GeolocationProviderImpl::StartProviders,
170                       base::Unretained(this),
171                       use_high_accuracy);
172   }
173
174   message_loop()->PostTask(FROM_HERE, task);
175 }
176
177 void GeolocationProviderImpl::StopProviders() {
178   DCHECK(OnGeolocationThread());
179   DCHECK(arbitrator_);
180   arbitrator_->StopProviders();
181 }
182
183 void GeolocationProviderImpl::StartProviders(bool use_high_accuracy) {
184   DCHECK(OnGeolocationThread());
185   DCHECK(arbitrator_);
186   arbitrator_->StartProviders(use_high_accuracy);
187 }
188
189 void GeolocationProviderImpl::InformProvidersPermissionGranted() {
190   DCHECK(IsRunning());
191   if (!OnGeolocationThread()) {
192     message_loop()->PostTask(
193         FROM_HERE,
194         base::Bind(&GeolocationProviderImpl::InformProvidersPermissionGranted,
195                    base::Unretained(this)));
196     return;
197   }
198   DCHECK(OnGeolocationThread());
199   DCHECK(arbitrator_);
200   arbitrator_->OnPermissionGranted();
201 }
202
203 void GeolocationProviderImpl::NotifyClients(const Geoposition& position) {
204   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
205   DCHECK(position.Validate() ||
206          position.error_code != Geoposition::ERROR_CODE_NONE);
207   position_ = position;
208   CallbackList::const_iterator it = callbacks_.begin();
209   while (it != callbacks_.end()) {
210     // Advance iterator before calling the observer to guard against synchronous
211     // unregister.
212     LocationUpdateCallback callback = it->first;
213     ++it;
214     callback.Run(position_);
215   }
216 }
217
218 void GeolocationProviderImpl::Init() {
219   DCHECK(OnGeolocationThread());
220   DCHECK(!arbitrator_);
221   arbitrator_ = CreateArbitrator();
222 }
223
224 void GeolocationProviderImpl::CleanUp() {
225   DCHECK(OnGeolocationThread());
226   delete arbitrator_;
227   arbitrator_ = NULL;
228 }
229
230 LocationArbitrator* GeolocationProviderImpl::CreateArbitrator() {
231   LocationArbitratorImpl::LocationUpdateCallback callback = base::Bind(
232       &GeolocationProviderImpl::OnLocationUpdate, base::Unretained(this));
233   return new LocationArbitratorImpl(callback);
234 }
235
236 }  // namespace content