summaryrefslogtreecommitdiffstats
path: root/WebCore
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2009-08-25 12:47:09 +0100
committerSteve Block <steveblock@google.com>2009-09-07 15:13:42 +0100
commitbc2cb6973923b5560298950bd824d9627d6ed765 (patch)
tree5025297b0040ce7da02c297dd55d55a27ff7d9e4 /WebCore
parent9d9dbfe4e0be065094e561632b7a013dc111119f (diff)
downloadexternal_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.cpp124
-rw-r--r--WebCore/page/Geolocation.h11
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