diff options
Diffstat (limited to 'Source/WebCore/page/GeolocationPositionCache.cpp')
-rw-r--r-- | Source/WebCore/page/GeolocationPositionCache.cpp | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/Source/WebCore/page/GeolocationPositionCache.cpp b/Source/WebCore/page/GeolocationPositionCache.cpp new file mode 100644 index 0000000..5a0f42d --- /dev/null +++ b/Source/WebCore/page/GeolocationPositionCache.cpp @@ -0,0 +1,261 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "GeolocationPositionCache.h" + +#if ENABLE(GEOLOCATION) + +#include "CrossThreadTask.h" +#include "Geoposition.h" +#include "SQLValue.h" +#include "SQLiteDatabase.h" +#include "SQLiteFileSystem.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/Threading.h> + +using namespace WTF; + +namespace WebCore { + +static int numUsers = 0; + +GeolocationPositionCache* GeolocationPositionCache::instance() +{ + DEFINE_STATIC_LOCAL(GeolocationPositionCache*, instance, (0)); + if (!instance) + instance = new GeolocationPositionCache(); + return instance; +} + +GeolocationPositionCache::GeolocationPositionCache() + : m_threadId(0) +{ +} + +void GeolocationPositionCache::addUser() +{ + ASSERT(numUsers >= 0); + MutexLocker databaseLock(m_databaseFileMutex); + if (!numUsers && !m_threadId && !m_databaseFile.isNull()) { + startBackgroundThread(); + MutexLocker lock(m_cachedPositionMutex); + if (!m_cachedPosition) + triggerReadFromDatabase(); + } + ++numUsers; +} + +void GeolocationPositionCache::removeUser() +{ + MutexLocker lock(m_cachedPositionMutex); + --numUsers; + ASSERT(numUsers >= 0); + if (!numUsers && m_cachedPosition && m_threadId) + triggerWriteToDatabase(); +} + +void GeolocationPositionCache::setDatabasePath(const String& path) +{ + static const char* databaseName = "CachedGeoposition.db"; + String newFile = SQLiteFileSystem::appendDatabaseFileNameToPath(path, databaseName); + MutexLocker lock(m_databaseFileMutex); + if (m_databaseFile != newFile) { + m_databaseFile = newFile; + if (numUsers && !m_threadId) { + startBackgroundThread(); + if (!m_cachedPosition) + triggerReadFromDatabase(); + } + } +} + +void GeolocationPositionCache::setCachedPosition(Geoposition* cachedPosition) +{ + MutexLocker lock(m_cachedPositionMutex); + m_cachedPosition = cachedPosition; +} + +Geoposition* GeolocationPositionCache::cachedPosition() +{ + MutexLocker lock(m_cachedPositionMutex); + return m_cachedPosition.get(); +} + +void GeolocationPositionCache::startBackgroundThread() +{ + // FIXME: Consider sharing this thread with other background tasks. + m_threadId = createThread(threadEntryPoint, this, "WebCore: Geolocation cache"); +} + +void* GeolocationPositionCache::threadEntryPoint(void* object) +{ + static_cast<GeolocationPositionCache*>(object)->threadEntryPointImpl(); + return 0; +} + +void GeolocationPositionCache::threadEntryPointImpl() +{ + while (OwnPtr<ScriptExecutionContext::Task> task = m_queue.waitForMessage()) { + // We don't need a ScriptExecutionContext in the callback, so pass 0 here. + task->performTask(0); + } +} + +void GeolocationPositionCache::triggerReadFromDatabase() +{ + m_queue.append(createCallbackTask(&GeolocationPositionCache::readFromDatabase, this)); +} + +void GeolocationPositionCache::readFromDatabase(ScriptExecutionContext*, GeolocationPositionCache* cache) +{ + cache->readFromDatabaseImpl(); +} + +void GeolocationPositionCache::readFromDatabaseImpl() +{ + SQLiteDatabase database; + { + MutexLocker lock(m_databaseFileMutex); + if (!database.open(m_databaseFile)) + return; + } + + // Create the table here, such that even if we've just created the + // DB, the commands below should succeed. + if (!database.executeCommand("CREATE TABLE IF NOT EXISTS CachedPosition (" + "latitude REAL NOT NULL, " + "longitude REAL NOT NULL, " + "altitude REAL, " + "accuracy REAL NOT NULL, " + "altitudeAccuracy REAL, " + "heading REAL, " + "speed REAL, " + "timestamp INTEGER NOT NULL)")) + return; + + SQLiteStatement statement(database, "SELECT * FROM CachedPosition"); + if (statement.prepare() != SQLResultOk) + return; + + if (statement.step() != SQLResultRow) + return; + + bool providesAltitude = statement.getColumnValue(2).type() != SQLValue::NullValue; + bool providesAltitudeAccuracy = statement.getColumnValue(4).type() != SQLValue::NullValue; + bool providesHeading = statement.getColumnValue(5).type() != SQLValue::NullValue; + bool providesSpeed = statement.getColumnValue(6).type() != SQLValue::NullValue; + RefPtr<Coordinates> coordinates = Coordinates::create(statement.getColumnDouble(0), // latitude + statement.getColumnDouble(1), // longitude + providesAltitude, statement.getColumnDouble(2), // altitude + statement.getColumnDouble(3), // accuracy + providesAltitudeAccuracy, statement.getColumnDouble(4), // altitudeAccuracy + providesHeading, statement.getColumnDouble(5), // heading + providesSpeed, statement.getColumnDouble(6)); // speed + DOMTimeStamp timestamp = statement.getColumnInt64(7); // timestamp + + // A position may have been set since we called triggerReadFromDatabase(). + MutexLocker lock(m_cachedPositionMutex); + if (m_cachedPosition) + return; + m_cachedPosition = Geoposition::create(coordinates.release(), timestamp); +} + +void GeolocationPositionCache::triggerWriteToDatabase() +{ + m_queue.append(createCallbackTask(writeToDatabase, this)); +} + +void GeolocationPositionCache::writeToDatabase(ScriptExecutionContext*, GeolocationPositionCache* cache) +{ + cache->writeToDatabaseImpl(); +} + +void GeolocationPositionCache::writeToDatabaseImpl() +{ + SQLiteDatabase database; + { + MutexLocker lock(m_databaseFileMutex); + if (!database.open(m_databaseFile)) + return; + } + + RefPtr<Geoposition> cachedPosition; + { + MutexLocker lock(m_cachedPositionMutex); + if (m_cachedPosition) + cachedPosition = m_cachedPosition->threadSafeCopy(); + } + + SQLiteTransaction transaction(database); + + if (!database.executeCommand("DELETE FROM CachedPosition")) + return; + + SQLiteStatement statement(database, "INSERT INTO CachedPosition (" + "latitude, " + "longitude, " + "altitude, " + "accuracy, " + "altitudeAccuracy, " + "heading, " + "speed, " + "timestamp) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); + if (statement.prepare() != SQLResultOk) + return; + + statement.bindDouble(1, cachedPosition->coords()->latitude()); + statement.bindDouble(2, cachedPosition->coords()->longitude()); + if (cachedPosition->coords()->canProvideAltitude()) + statement.bindDouble(3, cachedPosition->coords()->altitude()); + else + statement.bindNull(3); + statement.bindDouble(4, cachedPosition->coords()->accuracy()); + if (cachedPosition->coords()->canProvideAltitudeAccuracy()) + statement.bindDouble(5, cachedPosition->coords()->altitudeAccuracy()); + else + statement.bindNull(5); + if (cachedPosition->coords()->canProvideHeading()) + statement.bindDouble(6, cachedPosition->coords()->heading()); + else + statement.bindNull(6); + if (cachedPosition->coords()->canProvideSpeed()) + statement.bindDouble(7, cachedPosition->coords()->speed()); + else + statement.bindNull(7); + statement.bindInt64(8, cachedPosition->timestamp()); + + if (!statement.executeCommand()) + return; + + transaction.commit(); +} + +} // namespace WebCore + +#endif // ENABLE(GEOLOCATION) |