Index: content/browser/geolocation/geolocation_provider.cc |
diff --git a/content/browser/geolocation/geolocation_provider.cc b/content/browser/geolocation/geolocation_provider.cc |
index d61c7c7d064244162350a2d4e0b73b35095e05b0..da592e205a92fa36a84e3c5dc3c78383d98e6786 100644 |
--- a/content/browser/geolocation/geolocation_provider.cc |
+++ b/content/browser/geolocation/geolocation_provider.cc |
@@ -6,49 +6,66 @@ |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
+#include "base/callback.h" |
+#include "base/location.h" |
+#include "base/logging.h" |
#include "base/memory/singleton.h" |
-#include "base/threading/thread_restrictions.h" |
+#include "base/message_loop.h" |
#include "content/browser/geolocation/location_arbitrator.h" |
+#include "content/public/browser/browser_thread.h" |
+ |
+using content::BrowserThread; |
GeolocationProvider* GeolocationProvider::GetInstance() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
return Singleton<GeolocationProvider>::get(); |
} |
GeolocationProvider::GeolocationProvider() |
: base::Thread("Geolocation"), |
- client_loop_(base::MessageLoopProxy::current()), |
is_permission_granted_(false), |
ignore_location_updates_(false), |
arbitrator_(NULL) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
} |
GeolocationProvider::~GeolocationProvider() { |
- DCHECK(observers_.empty()); // observers must unregister. |
+ // All observers should have unregistered before this singleton is destructed. |
+ DCHECK(observers_.empty()); |
Stop(); |
DCHECK(!arbitrator_); |
} |
void GeolocationProvider::AddObserver(GeolocationObserver* observer, |
const GeolocationObserverOptions& update_options) { |
- DCHECK(OnClientThread()); |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
observers_[observer] = update_options; |
- OnObserversChanged(); |
+ OnClientsChanged(); |
if (position_.Validate() || |
position_.error_code != content::Geoposition::ERROR_CODE_NONE) |
observer->OnLocationUpdate(position_); |
} |
bool GeolocationProvider::RemoveObserver(GeolocationObserver* observer) { |
- DCHECK(OnClientThread()); |
- size_t remove = observers_.erase(observer); |
- OnObserversChanged(); |
- return remove > 0; |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ size_t removed = observers_.erase(observer); |
+ if (removed) |
+ OnClientsChanged(); |
+ return removed > 0; |
+} |
+ |
+void GeolocationProvider::RequestCallback( |
+ const content::GeolocationUpdateCallback& callback) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ callbacks_.push_back(callback); |
+ OnClientsChanged(); |
+ OnPermissionGranted(); |
} |
-void GeolocationProvider::OnObserversChanged() { |
- DCHECK(OnClientThread()); |
+void GeolocationProvider::OnClientsChanged() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
base::Closure task; |
- if (observers_.empty()) { |
+ if (observers_.empty() && callbacks_.empty()) { |
DCHECK(IsRunning()); |
task = base::Bind(&GeolocationProvider::StopProviders, |
base::Unretained(this)); |
@@ -58,29 +75,46 @@ void GeolocationProvider::OnObserversChanged() { |
if (HasPermissionBeenGranted()) |
InformProvidersPermissionGranted(); |
} |
- |
- // The high accuracy requirement may have changed. |
+ // Determine a set of options that satisfies all clients. |
+ GeolocationObserverOptions options = |
+ GeolocationObserverOptions::Collapse(observers_); |
+ // For callbacks, high accuracy position information is always requested. |
+ if (!callbacks_.empty()) |
+ options.Collapse(GeolocationObserverOptions(true)); |
+ |
+ // Send the current options to the providers as they may have changed. |
task = base::Bind(&GeolocationProvider::StartProviders, |
base::Unretained(this), |
- GeolocationObserverOptions::Collapse(observers_)); |
+ options); |
} |
message_loop()->PostTask(FROM_HERE, task); |
} |
-void GeolocationProvider::NotifyObservers( |
- const content::Geoposition& position) { |
- DCHECK(OnClientThread()); |
+void GeolocationProvider::NotifyClients(const content::Geoposition& position) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
DCHECK(position.Validate() || |
position.error_code != content::Geoposition::ERROR_CODE_NONE); |
position_ = position; |
ObserverMap::const_iterator it = observers_.begin(); |
while (it != observers_.end()) { |
- // Advance iterator before callback to guard against synchronous unregister. |
+ // Advance iterator before calling the observer to guard against synchronous |
+ // unregister. |
GeolocationObserver* observer = it->first; |
++it; |
observer->OnLocationUpdate(position_); |
} |
+ if (!callbacks_.empty()) { |
+ // Copy the callback list to guard against synchronous callback requests |
+ // reallocating the vector and invalidating the iterator. |
+ CallbackList callbacks = callbacks_; |
+ callbacks_.clear(); |
+ for (CallbackList::iterator it = callbacks.begin(); |
+ it != callbacks.end(); |
+ ++it) |
+ it->Run(position); |
+ OnClientsChanged(); |
+ } |
} |
void GeolocationProvider::StartProviders( |
@@ -97,9 +131,10 @@ void GeolocationProvider::StopProviders() { |
} |
void GeolocationProvider::OnPermissionGranted() { |
- DCHECK(OnClientThread()); |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ bool was_permission_granted = is_permission_granted_; |
is_permission_granted_ = true; |
- if (IsRunning()) |
+ if (IsRunning() && !was_permission_granted) |
InformProvidersPermissionGranted(); |
} |
@@ -135,29 +170,25 @@ void GeolocationProvider::OnLocationUpdate( |
// Will be true only in testing. |
if (ignore_location_updates_) |
return; |
- client_loop_->PostTask( |
- FROM_HERE, |
- base::Bind(&GeolocationProvider::NotifyObservers, |
- base::Unretained(this), position)); |
+ BrowserThread::PostTask(BrowserThread::IO, |
+ FROM_HERE, |
+ base::Bind(&GeolocationProvider::NotifyClients, |
+ base::Unretained(this), position)); |
} |
void GeolocationProvider::OverrideLocationForTesting( |
const content::Geoposition& position) { |
- DCHECK(OnClientThread()); |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
position_ = position; |
ignore_location_updates_ = true; |
- NotifyObservers(position); |
+ NotifyClients(position); |
} |
bool GeolocationProvider::HasPermissionBeenGranted() const { |
- DCHECK(OnClientThread()); |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
return is_permission_granted_; |
} |
-bool GeolocationProvider::OnClientThread() const { |
- return client_loop_->BelongsToCurrentThread(); |
-} |
- |
bool GeolocationProvider::OnGeolocationThread() const { |
return MessageLoop::current() == message_loop(); |
} |