diff options
author | Steve Block <steveblock@google.com> | 2009-08-25 12:47:09 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2009-09-07 15:13:42 +0100 |
commit | bc2cb6973923b5560298950bd824d9627d6ed765 (patch) | |
tree | 5025297b0040ce7da02c297dd55d55a27ff7d9e4 /WebCore | |
parent | 9d9dbfe4e0be065094e561632b7a013dc111119f (diff) | |
download | external_webkit-bc2cb6973923b5560298950bd824d9627d6ed765.zip external_webkit-bc2cb6973923b5560298950bd824d9627d6ed765.tar.gz external_webkit-bc2cb6973923b5560298950bd824d9627d6ed765.tar.bz2 |
Implements Geolocation PositionOptions.maximumAge property.
This fixes bug http://b/issue?id=2054431.
Change-Id: Ic5fcd763e448b3bdc02de5d2a40d418ef25d39f9
Diffstat (limited to 'WebCore')
-rw-r--r-- | WebCore/page/Geolocation.cpp | 124 | ||||
-rw-r--r-- | WebCore/page/Geolocation.h | 11 |
2 files changed, 123 insertions, 12 deletions
diff --git a/WebCore/page/Geolocation.cpp b/WebCore/page/Geolocation.cpp index 359c125..adf5eb1 100644 --- a/WebCore/page/Geolocation.cpp +++ b/WebCore/page/Geolocation.cpp @@ -28,6 +28,7 @@ #include "Geolocation.h" #include "Chrome.h" +#include "CurrentTime.h" #include "Document.h" #include "Frame.h" #include "Page.h" @@ -57,6 +58,13 @@ void Geolocation::GeoNotifier::setFatalError(PassRefPtr<PositionError> error) m_timer.startOneShot(0); } +void Geolocation::GeoNotifier::setCachedPosition(Geoposition* cachedPosition) +{ + // We do not take owenership from the caller, but add our own ref count. + m_cachedPosition = cachedPosition; + m_timer.startOneShot(0); +} + void Geolocation::GeoNotifier::startTimerIfNeeded() { if (m_options->hasTimeout()) @@ -68,14 +76,19 @@ void Geolocation::GeoNotifier::timerFired(Timer<GeoNotifier>*) m_timer.stop(); if (m_fatalError) { - if (m_errorCallback) { + if (m_errorCallback) m_errorCallback->handleEvent(m_fatalError.get()); - } // This will cause this notifier to be deleted. m_geolocation->fatalErrorOccurred(this); return; } + if (m_cachedPosition) { + m_successCallback->handleEvent(m_cachedPosition.get()); + m_geolocation->requestReturnedCachedPosition(this); + return; + } + if (m_errorCallback) { RefPtr<PositionError> error = PositionError::create(PositionError::TIMEOUT, "Timed out"); m_errorCallback->handleEvent(error.get()); @@ -83,11 +96,45 @@ void Geolocation::GeoNotifier::timerFired(Timer<GeoNotifier>*) m_geolocation->requestTimedOut(this); } +class CachedPositionManager { + public: + CachedPositionManager() + { + if (s_instances++ == 0) + s_cachedPosition = new RefPtr<Geoposition>; + } + ~CachedPositionManager() + { + if (--s_instances == 0) { + // TODO(steveblock): Store cached position between sessions. + delete s_cachedPosition; + } + } + void setCachedPosition(Geoposition* cachedPosition) + { + // We do not take owenership from the caller, but add our own ref count. + *s_cachedPosition = cachedPosition; + } + Geoposition* cachedPosition() + { + return s_cachedPosition->get(); + } + + private: + static int s_instances; + static RefPtr<Geoposition>* s_cachedPosition; +}; + +int CachedPositionManager::s_instances = 0; +RefPtr<Geoposition>* CachedPositionManager::s_cachedPosition; + + Geolocation::Geolocation(Frame* frame) : m_frame(frame) , m_service(GeolocationService::create(this)) , m_allowGeolocation(Unknown) , m_shouldClearCache(false) + , m_cachedPositionManager(new CachedPositionManager) { if (!m_frame) return; @@ -101,6 +148,8 @@ void Geolocation::disconnectFrame() if (m_frame && m_frame->document()) m_frame->document()->setUsingGeolocation(false); m_frame = 0; + + delete m_cachedPositionManager; } void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options) @@ -131,10 +180,19 @@ PassRefPtr<Geolocation::GeoNotifier> Geolocation::makeRequest(PassRefPtr<Positio if (isDenied()) { notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); } else { - if (m_service->startUpdating(notifier->m_options.get())) - notifier->startTimerIfNeeded(); - else { - notifier->setFatalError(PositionError::create(PositionError::UNKNOWN_ERROR, "Failed to start Geolocation service")); + if (haveSuitableCachedPosition(notifier->m_options.get())) { + ASSERT(m_cachedPositionManager->cachedPosition()); + if (isAllowed()) + notifier->setCachedPosition(m_cachedPositionManager->cachedPosition()); + else { + m_requestsAwaitingCachedPosition.add(notifier); + requestPermission(); + } + } else { + if (m_service->startUpdating(notifier->m_options.get())) + notifier->startTimerIfNeeded(); + else + notifier->setFatalError(PositionError::create(PositionError::UNKNOWN_ERROR, "Failed to start Geolocation service")); } } @@ -165,6 +223,35 @@ void Geolocation::requestTimedOut(GeoNotifier* notifier) m_service->stopUpdating(); } +void Geolocation::requestReturnedCachedPosition(GeoNotifier* notifier) +{ + // If this is a one-shot request, stop it. + if (m_oneShots.contains(notifier)) { + m_oneShots.remove(notifier); + if (!hasListeners()) + m_service->stopUpdating(); + return; + } + + // Otherwise, start the service to get updates. + if (m_service->startUpdating(notifier->m_options.get())) + notifier->startTimerIfNeeded(); + else + notifier->setFatalError(PositionError::create(PositionError::UNKNOWN_ERROR, "Failed to start Geolocation service")); +} + +bool Geolocation::haveSuitableCachedPosition(PositionOptions* options) +{ + if (m_cachedPositionManager->cachedPosition() == 0) + return false; + if (!options->hasMaximumAge()) + return true; + if (options->maximumAge() == 0) + return false; + DOMTimeStamp currentTimeMillis = currentTime() * 1000.0; + return m_cachedPositionManager->cachedPosition()->timestamp() > currentTimeMillis - options->maximumAge(); +} + void Geolocation::clearWatch(int watchId) { m_watchers.remove(watchId); @@ -187,15 +274,28 @@ void Geolocation::resume() void Geolocation::setIsAllowed(bool allowed) { + // This may be due to either a new position from the service, or a cached + // position. m_allowGeolocation = allowed ? Yes : No; - - if (isAllowed()) { - makeSuccessCallbacks(); - } else { - WTF::RefPtr<WebCore::PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage); + + if (!isAllowed()) { + RefPtr<WebCore::PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage); error->setIsFatal(true); handleError(error.get()); + return; } + + // If the service has a last position, use it to call back for all requests. + // If any of the requests are waiting for permission for a cached position, + // the position from the service will be at least as fresh. + if (m_service->lastPosition()) + makeSuccessCallbacks(); + else { + GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end(); + for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) + (*iter)->setCachedPosition(m_cachedPositionManager->cachedPosition()); + } + m_requestsAwaitingCachedPosition.clear(); } void Geolocation::sendError(Vector<RefPtr<GeoNotifier> >& notifiers, PositionError* error) @@ -297,6 +397,8 @@ void Geolocation::geolocationServicePositionChanged(GeolocationService*) { ASSERT(m_service->lastPosition()); + m_cachedPositionManager->setCachedPosition(m_service->lastPosition()); + // Stop all currently running timers. stopTimers(); diff --git a/WebCore/page/Geolocation.h b/WebCore/page/Geolocation.h index f74c87b..0a19ec2 100644 --- a/WebCore/page/Geolocation.h +++ b/WebCore/page/Geolocation.h @@ -27,6 +27,7 @@ #define Geolocation_h #include "GeolocationService.h" +#include "Geoposition.h" #include "PositionCallback.h" #include "PositionError.h" #include "PositionErrorCallback.h" @@ -44,7 +45,8 @@ namespace WebCore { class Frame; -class Geoposition; +class CachedPositionManager; + class Geolocation : public RefCounted<Geolocation>, public GeolocationServiceClient { public: @@ -78,6 +80,7 @@ private: static PassRefPtr<GeoNotifier> create(Geolocation* geolocation, PassRefPtr<PositionCallback> positionCallback, PassRefPtr<PositionErrorCallback> positionErrorCallback, PassRefPtr<PositionOptions> options) { return adoptRef(new GeoNotifier(geolocation, positionCallback, positionErrorCallback, options)); } void setFatalError(PassRefPtr<PositionError> error); + void setCachedPosition(Geoposition* cachedPosition); void startTimerIfNeeded(); void timerFired(Timer<GeoNotifier>*); @@ -87,6 +90,7 @@ private: RefPtr<PositionOptions> m_options; Timer<GeoNotifier> m_timer; RefPtr<PositionError> m_fatalError; + RefPtr<Geoposition> m_cachedPosition; private: GeoNotifier(Geolocation* geolocation, PassRefPtr<PositionCallback>, PassRefPtr<PositionErrorCallback>, PassRefPtr<PositionOptions>); @@ -114,6 +118,8 @@ private: void fatalErrorOccurred(GeoNotifier* notifier); void requestTimedOut(GeoNotifier* notifier); + void requestReturnedCachedPosition(GeoNotifier* notifier); + bool haveSuitableCachedPosition(PositionOptions*); typedef HashSet<RefPtr<GeoNotifier> > GeoNotifierSet; typedef HashMap<int, RefPtr<GeoNotifier> > GeoNotifierMap; @@ -130,6 +136,9 @@ private: No } m_allowGeolocation; bool m_shouldClearCache; + + CachedPositionManager* m_cachedPositionManager; + GeoNotifierSet m_requestsAwaitingCachedPosition; }; } // namespace WebCore |