Chromium Code Reviews| 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> rv; |
|
Michael van Ouwerkerk
2014/05/08 13:20:02
Nit: s/rv/subscription/
jam
2014/05/08 15:04:58
Done.
| |
| 51 CallbackList::iterator i = callbacks_.begin(); | 28 if (use_high_accuracy) { |
| 52 for (; i != callbacks_.end(); ++i) { | 29 rv = high_accuracy_callbacks_.Add(callback); |
| 53 if (i->first.Equals(callback)) { | 30 } else { |
| 54 i->second = use_high_accuracy; | 31 rv = 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 rv.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 |