summaryrefslogtreecommitdiffstats
path: root/WebCore/page/Geolocation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/page/Geolocation.cpp')
-rw-r--r--WebCore/page/Geolocation.cpp159
1 files changed, 110 insertions, 49 deletions
diff --git a/WebCore/page/Geolocation.cpp b/WebCore/page/Geolocation.cpp
index bf877e6..978a1b0 100644
--- a/WebCore/page/Geolocation.cpp
+++ b/WebCore/page/Geolocation.cpp
@@ -28,7 +28,6 @@
#include "Geolocation.h"
#include "Chrome.h"
-#include "CurrentTime.h"
#include "Document.h"
#include "DOMWindow.h"
#include "EventNames.h"
@@ -38,10 +37,11 @@
#include "SQLiteStatement.h"
#include "SQLiteTransaction.h"
#include "SQLValue.h"
+#include <wtf/CurrentTime.h>
namespace WebCore {
-static const char* permissionDeniedErrorMessage = "User denied Geolocation";
+static const char permissionDeniedErrorMessage[] = "User denied Geolocation";
Geolocation::GeoNotifier::GeoNotifier(Geolocation* geolocation, PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
: m_geolocation(geolocation)
@@ -49,7 +49,6 @@ Geolocation::GeoNotifier::GeoNotifier(Geolocation* geolocation, PassRefPtr<Posit
, m_errorCallback(errorCallback)
, m_options(options)
, m_timer(this, &Geolocation::GeoNotifier::timerFired)
- , m_fatalError(0)
{
ASSERT(m_geolocation);
ASSERT(m_successCallback);
@@ -60,10 +59,17 @@ Geolocation::GeoNotifier::GeoNotifier(Geolocation* geolocation, PassRefPtr<Posit
void Geolocation::GeoNotifier::setFatalError(PassRefPtr<PositionError> error)
{
+ // This method is called at most once on a given GeoNotifier object.
+ ASSERT(!m_fatalError);
m_fatalError = error;
m_timer.startOneShot(0);
}
+bool Geolocation::GeoNotifier::hasZeroTimeout() const
+{
+ return m_options->hasTimeout() && m_options->timeout() == 0;
+}
+
void Geolocation::GeoNotifier::setCachedPosition(Geoposition* cachedPosition)
{
// We do not take owenership from the caller, but add our own ref count.
@@ -81,25 +87,69 @@ void Geolocation::GeoNotifier::timerFired(Timer<GeoNotifier>*)
{
m_timer.stop();
+ // Cache our pointer to the Geolocation object, as this object could be
+ // deleted by a call to clearWatch in a callback.
+ Geolocation* geolocation = m_geolocation;
+
if (m_fatalError) {
if (m_errorCallback)
m_errorCallback->handleEvent(m_fatalError.get());
// This will cause this notifier to be deleted.
- m_geolocation->fatalErrorOccurred(this);
+ geolocation->fatalErrorOccurred(this);
return;
}
if (m_cachedPosition) {
m_successCallback->handleEvent(m_cachedPosition.get());
- m_geolocation->requestReturnedCachedPosition(this);
+ geolocation->requestReturnedCachedPosition(this);
return;
}
if (m_errorCallback) {
- RefPtr<PositionError> error = PositionError::create(PositionError::TIMEOUT, "Timed out");
+ RefPtr<PositionError> error = PositionError::create(PositionError::TIMEOUT, "Timeout expired");
m_errorCallback->handleEvent(error.get());
}
- m_geolocation->requestTimedOut(this);
+ geolocation->requestTimedOut(this);
+}
+
+void Geolocation::Watchers::set(int id, PassRefPtr<GeoNotifier> notifier)
+{
+ m_idToNotifierMap.set(id, notifier);
+ m_notifierToIdMap.set(notifier, id);
+}
+
+void Geolocation::Watchers::remove(int id)
+{
+ IdToNotifierMap::iterator iter = m_idToNotifierMap.find(id);
+ if (iter == m_idToNotifierMap.end())
+ return;
+ m_notifierToIdMap.remove(iter->second);
+ m_idToNotifierMap.remove(iter);
+}
+
+void Geolocation::Watchers::remove(GeoNotifier* notifier)
+{
+ NotifierToIdMap::iterator iter = m_notifierToIdMap.find(notifier);
+ if (iter == m_notifierToIdMap.end())
+ return;
+ m_idToNotifierMap.remove(iter->second);
+ m_notifierToIdMap.remove(iter);
+}
+
+void Geolocation::Watchers::clear()
+{
+ m_idToNotifierMap.clear();
+ m_notifierToIdMap.clear();
+}
+
+bool Geolocation::Watchers::isEmpty() const
+{
+ return m_idToNotifierMap.isEmpty();
+}
+
+void Geolocation::Watchers::getNotifiersVector(Vector<RefPtr<GeoNotifier> >& copy) const
+{
+ copyValuesToVector(m_idToNotifierMap, copy);
}
static const char* databaseName = "/CachedPosition.db";
@@ -132,7 +182,9 @@ class CachedPositionManager {
}
static void setDatabasePath(String databasePath)
{
- s_databaseFile = databasePath + databaseName;
+ if (!s_databaseFile)
+ s_databaseFile = new String;
+ *s_databaseFile = databasePath + databaseName;
// If we don't have have a cached position, attempt to read one from the
// DB at the new path.
if (s_instances && *s_cachedPosition == 0)
@@ -143,7 +195,7 @@ class CachedPositionManager {
static PassRefPtr<Geoposition> readFromDB()
{
SQLiteDatabase database;
- if (!database.open(s_databaseFile))
+ if (!s_databaseFile || !database.open(*s_databaseFile))
return 0;
// Create the table here, such that even if we've just created the
@@ -184,7 +236,7 @@ class CachedPositionManager {
ASSERT(position);
SQLiteDatabase database;
- if (!database.open(s_databaseFile))
+ if (!s_databaseFile || !database.open(*s_databaseFile))
return;
SQLiteTransaction transaction(database);
@@ -232,16 +284,17 @@ class CachedPositionManager {
}
static int s_instances;
static RefPtr<Geoposition>* s_cachedPosition;
- static String s_databaseFile;
+ static String* s_databaseFile;
};
int CachedPositionManager::s_instances = 0;
RefPtr<Geoposition>* CachedPositionManager::s_cachedPosition;
-String CachedPositionManager::s_databaseFile;
+String* CachedPositionManager::s_databaseFile = 0;
Geolocation::Geolocation(Frame* frame)
- : m_frame(frame)
+ : EventListener(GeolocationEventListenerType)
+ , m_frame(frame)
, m_service(GeolocationService::create(this))
, m_allowGeolocation(Unknown)
, m_shouldClearCache(false)
@@ -274,7 +327,7 @@ void Geolocation::disconnectFrame()
void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
{
- RefPtr<GeoNotifier> notifier = makeRequest(successCallback, errorCallback, options);
+ RefPtr<GeoNotifier> notifier = startRequest(successCallback, errorCallback, options);
ASSERT(notifier);
m_oneShots.add(notifier);
@@ -282,24 +335,26 @@ void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallbac
int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
{
- RefPtr<GeoNotifier> notifier = makeRequest(successCallback, errorCallback, options);
+ RefPtr<GeoNotifier> notifier = startRequest(successCallback, errorCallback, options);
ASSERT(notifier);
- static int sIdentifier = 0;
- m_watchers.set(++sIdentifier, notifier);
-
- return sIdentifier;
+ static int nextAvailableWatchId = 1;
+ // In case of overflow, make sure the ID remains positive, but reuse the ID values.
+ if (nextAvailableWatchId < 1)
+ nextAvailableWatchId = 1;
+ m_watchers.set(nextAvailableWatchId, notifier.release());
+ return nextAvailableWatchId++;
}
-PassRefPtr<Geolocation::GeoNotifier> Geolocation::makeRequest(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
+PassRefPtr<Geolocation::GeoNotifier> Geolocation::startRequest(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
{
RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
// Check whether permissions have already been denied. Note that if this is the case,
// the permission state can not change again in the lifetime of this page.
- if (isDenied()) {
+ if (isDenied())
notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
- } else {
+ else {
if (haveSuitableCachedPosition(notifier->m_options.get())) {
ASSERT(m_cachedPositionManager->cachedPosition());
if (isAllowed())
@@ -309,7 +364,7 @@ PassRefPtr<Geolocation::GeoNotifier> Geolocation::makeRequest(PassRefPtr<Positio
requestPermission();
}
} else {
- if (m_service->startUpdating(notifier->m_options.get()))
+ if (notifier->hasZeroTimeout() || m_service->startUpdating(notifier->m_options.get()))
notifier->startTimerIfNeeded();
else
notifier->setFatalError(PositionError::create(PositionError::UNKNOWN_ERROR, "Failed to start Geolocation service"));
@@ -323,12 +378,7 @@ void Geolocation::fatalErrorOccurred(Geolocation::GeoNotifier* notifier)
{
// This request has failed fatally. Remove it from our lists.
m_oneShots.remove(notifier);
- for (GeoNotifierMap::iterator iter = m_watchers.begin(); iter != m_watchers.end(); ++iter) {
- if (iter->second == notifier) {
- m_watchers.remove(iter);
- break;
- }
- }
+ m_watchers.remove(notifier);
if (!hasListeners())
m_service->stopUpdating();
@@ -399,7 +449,7 @@ void Geolocation::setIsAllowed(bool allowed)
m_allowGeolocation = allowed ? Yes : No;
if (!isAllowed()) {
- RefPtr<WebCore::PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage);
+ RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage);
error->setIsFatal(true);
handleError(error.get());
return;
@@ -461,7 +511,7 @@ void Geolocation::stopTimersForOneShots()
void Geolocation::stopTimersForWatchers()
{
Vector<RefPtr<GeoNotifier> > copy;
- copyValuesToVector(m_watchers, copy);
+ m_watchers.getNotifiersVector(copy);
stopTimer(copy);
}
@@ -475,15 +525,16 @@ void Geolocation::stopTimers()
void Geolocation::handleError(PositionError* error)
{
ASSERT(error);
-
+
Vector<RefPtr<GeoNotifier> > oneShotsCopy;
copyToVector(m_oneShots, oneShotsCopy);
Vector<RefPtr<GeoNotifier> > watchersCopy;
- copyValuesToVector(m_watchers, watchersCopy);
+ m_watchers.getNotifiersVector(watchersCopy);
// Clear the lists before we make the callbacks, to avoid clearing notifiers
- // added by calls to Geolocation methods from the callbacks.
+ // added by calls to Geolocation methods from the callbacks, and to prevent
+ // further callbacks to these notifiers.
m_oneShots.clear();
if (error->isFatal())
m_watchers.clear();
@@ -513,19 +564,20 @@ void Geolocation::requestPermission()
page->chrome()->requestGeolocationPermissionForFrame(m_frame, this);
}
-void Geolocation::geolocationServicePositionChanged(GeolocationService*)
+void Geolocation::geolocationServicePositionChanged(GeolocationService* service)
{
+ ASSERT_UNUSED(service, service == m_service);
ASSERT(m_service->lastPosition());
m_cachedPositionManager->setCachedPosition(m_service->lastPosition());
// Stop all currently running timers.
stopTimers();
-
+
if (!isAllowed()) {
// requestPermission() will ask the chrome for permission. This may be
// implemented synchronously or asynchronously. In both cases,
- // makeSucessCallbacks() will be called if permission is granted, so
+ // makeSuccessCallbacks() will be called if permission is granted, so
// there's nothing more to do here.
requestPermission();
return;
@@ -538,15 +590,16 @@ void Geolocation::makeSuccessCallbacks()
{
ASSERT(m_service->lastPosition());
ASSERT(isAllowed());
-
+
Vector<RefPtr<GeoNotifier> > oneShotsCopy;
copyToVector(m_oneShots, oneShotsCopy);
-
+
Vector<RefPtr<GeoNotifier> > watchersCopy;
- copyValuesToVector(m_watchers, watchersCopy);
-
+ m_watchers.getNotifiersVector(watchersCopy);
+
// Clear the lists before we make the callbacks, to avoid clearing notifiers
- // added by calls to Geolocation methods from the callbacks.
+ // added by calls to Geolocation methods from the callbacks, and to prevent
+ // further callbacks to these notifiers.
m_oneShots.clear();
sendPosition(oneShotsCopy, m_service->lastPosition());
@@ -563,14 +616,22 @@ void Geolocation::geolocationServiceErrorOccurred(GeolocationService* service)
handleError(service->lastError());
}
-void Geolocation::handleEvent(Event* event, bool)
+bool Geolocation::operator==(const EventListener& listener)
{
- ASSERT_UNUSED(event, event->type() == eventTypes().unloadEvent);
- // Cancel any ongoing requests on page unload. This is required to release
- // references to JS callbacks in the page, to allow the frame to be cleaned up
- // by WebKit.
- m_oneShots.clear();
- m_watchers.clear();
+ if (listener.type() != GeolocationEventListenerType)
+ return false;
+ const Geolocation* geolocation = static_cast<const Geolocation*>(&listener);
+ return m_frame == geolocation->m_frame;
+}
+
+void Geolocation::handleEvent(ScriptExecutionContext*, Event* event)
+{
+ ASSERT_UNUSED(event, event->type() == eventNames().unloadEvent);
+ // Cancel any ongoing requests on page unload. This is required to release
+ // references to JS callbacks in the page, to allow the frame to be cleaned up
+ // by WebKit.
+ m_oneShots.clear();
+ m_watchers.clear();
}
void Geolocation::setDatabasePath(String databasePath)