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