diff options
Diffstat (limited to 'Source/WebCore/storage')
176 files changed, 18742 insertions, 0 deletions
diff --git a/Source/WebCore/storage/AbstractDatabase.cpp b/Source/WebCore/storage/AbstractDatabase.cpp new file mode 100644 index 0000000..951b621 --- /dev/null +++ b/Source/WebCore/storage/AbstractDatabase.cpp @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "AbstractDatabase.h" + +#if ENABLE(DATABASE) +#include "DatabaseAuthorizer.h" +#include "DatabaseTracker.h" +#include "Logging.h" +#include "SQLiteStatement.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString) +{ + SQLiteStatement statement(db, query); + int result = statement.prepare(); + + if (result != SQLResultOk) { + LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data()); + return false; + } + + result = statement.step(); + if (result == SQLResultRow) { + resultString = statement.getColumnText(0); + return true; + } + if (result == SQLResultDone) { + resultString = String(); + return true; + } + + LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data()); + return false; +} + +static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value) +{ + SQLiteStatement statement(db, query); + int result = statement.prepare(); + + if (result != SQLResultOk) { + LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data()); + return false; + } + + statement.bindText(1, value); + + result = statement.step(); + if (result != SQLResultDone) { + LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data()); + return false; + } + + return true; +} + +// FIXME: move all guid-related functions to a DatabaseVersionTracker class. +static Mutex& guidMutex() +{ + // Note: We don't have to use AtomicallyInitializedStatic here because + // this function is called once in the constructor on the main thread + // before any other threads that call this function are used. + DEFINE_STATIC_LOCAL(Mutex, mutex, ()); + return mutex; +} + +typedef HashMap<int, String> GuidVersionMap; +static GuidVersionMap& guidToVersionMap() +{ + DEFINE_STATIC_LOCAL(GuidVersionMap, map, ()); + return map; +} + +// NOTE: Caller must lock guidMutex(). +static inline void updateGuidVersionMap(int guid, String newVersion) +{ + // Ensure the the mutex is locked. + ASSERT(!guidMutex().tryLock()); + + // Note: It is not safe to put an empty string into the guidToVersionMap() map. + // That's because the map is cross-thread, but empty strings are per-thread. + // The copy() function makes a version of the string you can use on the current + // thread, but we need a string we can keep in a cross-thread data structure. + // FIXME: This is a quite-awkward restriction to have to program with. + + // Map null string to empty string (see comment above). + guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.threadsafeCopy()); +} + +typedef HashMap<int, HashSet<AbstractDatabase*>*> GuidDatabaseMap; +static GuidDatabaseMap& guidToDatabaseMap() +{ + DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ()); + return map; +} + +static int guidForOriginAndName(const String& origin, const String& name) +{ + String stringID = origin + "/" + name; + + // Note: We don't have to use AtomicallyInitializedStatic here because + // this function is called once in the constructor on the main thread + // before any other threads that call this function are used. + DEFINE_STATIC_LOCAL(Mutex, stringIdentifierMutex, ()); + MutexLocker locker(stringIdentifierMutex); + typedef HashMap<String, int> IDGuidMap; + DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ()); + int guid = stringIdentifierToGUIDMap.get(stringID); + if (!guid) { + static int currentNewGUID = 1; + guid = currentNewGUID++; + stringIdentifierToGUIDMap.set(stringID, guid); + } + + return guid; +} + +static bool isDatabaseAvailable = true; + +bool AbstractDatabase::isAvailable() +{ + return isDatabaseAvailable; +} + +void AbstractDatabase::setIsAvailable(bool available) +{ + isDatabaseAvailable = available; +} + +// static +const String& AbstractDatabase::databaseInfoTableName() +{ + DEFINE_STATIC_LOCAL(String, name, ("__WebKitDatabaseInfoTable__")); + return name; +} + +AbstractDatabase::AbstractDatabase(ScriptExecutionContext* context, const String& name, const String& expectedVersion, + const String& displayName, unsigned long estimatedSize) + : m_scriptExecutionContext(context) + , m_name(name.crossThreadString()) + , m_expectedVersion(expectedVersion.crossThreadString()) + , m_displayName(displayName.crossThreadString()) + , m_estimatedSize(estimatedSize) + , m_guid(0) + , m_opened(false) + , m_new(false) +{ + ASSERT(context->isContextThread()); + m_contextThreadSecurityOrigin = m_scriptExecutionContext->securityOrigin(); + + m_databaseAuthorizer = DatabaseAuthorizer::create(databaseInfoTableName()); + + if (m_name.isNull()) + m_name = ""; + + m_guid = guidForOriginAndName(securityOrigin()->toString(), name); + { + MutexLocker locker(guidMutex()); + + HashSet<AbstractDatabase*>* hashSet = guidToDatabaseMap().get(m_guid); + if (!hashSet) { + hashSet = new HashSet<AbstractDatabase*>; + guidToDatabaseMap().set(m_guid, hashSet); + } + + hashSet->add(this); + } + + m_filename = DatabaseTracker::tracker().fullPathForDatabase(securityOrigin(), m_name); + DatabaseTracker::tracker().addOpenDatabase(this); +} + +AbstractDatabase::~AbstractDatabase() +{ +} + +void AbstractDatabase::closeDatabase() +{ + if (!m_opened) + return; + + m_sqliteDatabase.close(); + m_opened = false; + { + MutexLocker locker(guidMutex()); + + HashSet<AbstractDatabase*>* hashSet = guidToDatabaseMap().get(m_guid); + ASSERT(hashSet); + ASSERT(hashSet->contains(this)); + hashSet->remove(this); + if (hashSet->isEmpty()) { + guidToDatabaseMap().remove(m_guid); + delete hashSet; + guidToVersionMap().remove(m_guid); + } + } +} + +String AbstractDatabase::version() const +{ + MutexLocker locker(guidMutex()); + return guidToVersionMap().get(m_guid).threadsafeCopy(); +} + +static const int maxSqliteBusyWaitTime = 30000; +bool AbstractDatabase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, ExceptionCode& ec) +{ + if (!m_sqliteDatabase.open(m_filename, true)) { + LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data()); + ec = INVALID_STATE_ERR; + return false; + } + if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) + LOG_ERROR("Unable to turn on incremental auto-vacuum for database %s", m_filename.ascii().data()); + + ASSERT(m_databaseAuthorizer); + m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); + m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); + + String currentVersion; + { + MutexLocker locker(guidMutex()); + + GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); + if (entry != guidToVersionMap().end()) { + // Map null string to empty string (see updateGuidVersionMap()). + currentVersion = entry->second.isNull() ? String("") : entry->second; + LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); + } else { + LOG(StorageAPI, "No cached version for guid %i", m_guid); + + if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) { + m_new = true; + + if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseInfoTableName() + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { + LOG_ERROR("Unable to create table %s in database %s", databaseInfoTableName().ascii().data(), databaseDebugName().ascii().data()); + ec = INVALID_STATE_ERR; + // Close the handle to the database file. + m_sqliteDatabase.close(); + return false; + } + } + + if (!getVersionFromDatabase(currentVersion)) { + LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data()); + ec = INVALID_STATE_ERR; + // Close the handle to the database file. + m_sqliteDatabase.close(); + return false; + } + if (currentVersion.length()) { + LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); + } else if (!m_new || shouldSetVersionInNewDatabase) { + LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); + if (!setVersionInDatabase(m_expectedVersion)) { + LOG_ERROR("Failed to set version %s in database %s", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); + ec = INVALID_STATE_ERR; + // Close the handle to the database file. + m_sqliteDatabase.close(); + return false; + } + currentVersion = m_expectedVersion; + } + + updateGuidVersionMap(m_guid, currentVersion); + } + } + + if (currentVersion.isNull()) { + LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); + currentVersion = ""; + } + + // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. + // If the expected version is the empty string, then we always return with whatever version of the database we have. + if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { + LOG(StorageAPI, "page expects version %s from database %s, which actually has version name %s - openDatabase() call will fail", m_expectedVersion.ascii().data(), + databaseDebugName().ascii().data(), currentVersion.ascii().data()); + ec = INVALID_STATE_ERR; + // Close the handle to the database file. + m_sqliteDatabase.close(); + return false; + } + + m_opened = true; + + return true; +} + +ScriptExecutionContext* AbstractDatabase::scriptExecutionContext() const +{ + return m_scriptExecutionContext.get(); +} + +SecurityOrigin* AbstractDatabase::securityOrigin() const +{ + return m_contextThreadSecurityOrigin.get(); +} + +String AbstractDatabase::stringIdentifier() const +{ + // Return a deep copy for ref counting thread safety + return m_name.threadsafeCopy(); +} + +String AbstractDatabase::displayName() const +{ + // Return a deep copy for ref counting thread safety + return m_displayName.threadsafeCopy(); +} + +unsigned long AbstractDatabase::estimatedSize() const +{ + return m_estimatedSize; +} + +String AbstractDatabase::fileName() const +{ + // Return a deep copy for ref counting thread safety + return m_filename.threadsafeCopy(); +} + +// static +const String& AbstractDatabase::databaseVersionKey() +{ + DEFINE_STATIC_LOCAL(String, key, ("WebKitDatabaseVersionKey")); + return key; +} + +bool AbstractDatabase::getVersionFromDatabase(String& version) +{ + DEFINE_STATIC_LOCAL(String, getVersionQuery, ("SELECT value FROM " + databaseInfoTableName() + " WHERE key = '" + databaseVersionKey() + "';")); + + m_databaseAuthorizer->disable(); + + bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQuery.threadsafeCopy(), version); + if (!result) + LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); + + m_databaseAuthorizer->enable(); + + return result; +} + +bool AbstractDatabase::setVersionInDatabase(const String& version) +{ + // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE + // clause in the CREATE statement (see Database::performOpenAndVerify()). + DEFINE_STATIC_LOCAL(String, setVersionQuery, ("INSERT INTO " + databaseInfoTableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);")); + + m_databaseAuthorizer->disable(); + + bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.threadsafeCopy(), version); + if (!result) + LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), setVersionQuery.ascii().data()); + + m_databaseAuthorizer->enable(); + + return result; +} + +bool AbstractDatabase::versionMatchesExpected() const +{ + if (!m_expectedVersion.isEmpty()) { + MutexLocker locker(guidMutex()); + return m_expectedVersion == guidToVersionMap().get(m_guid); + } + + return true; +} + +void AbstractDatabase::setExpectedVersion(const String& version) +{ + m_expectedVersion = version.threadsafeCopy(); + // Update the in memory database version map. + MutexLocker locker(guidMutex()); + updateGuidVersionMap(m_guid, version); +} + +void AbstractDatabase::disableAuthorizer() +{ + ASSERT(m_databaseAuthorizer); + m_databaseAuthorizer->disable(); +} + +void AbstractDatabase::enableAuthorizer() +{ + ASSERT(m_databaseAuthorizer); + m_databaseAuthorizer->enable(); +} + +void AbstractDatabase::setAuthorizerReadOnly() +{ + ASSERT(m_databaseAuthorizer); + m_databaseAuthorizer->setReadOnly(); +} + +void AbstractDatabase::setAuthorizerPermissions(int permissions) +{ + ASSERT(m_databaseAuthorizer); + m_databaseAuthorizer->setPermissions(permissions); +} + +bool AbstractDatabase::lastActionChangedDatabase() +{ + ASSERT(m_databaseAuthorizer); + return m_databaseAuthorizer->lastActionChangedDatabase(); +} + +bool AbstractDatabase::lastActionWasInsert() +{ + ASSERT(m_databaseAuthorizer); + return m_databaseAuthorizer->lastActionWasInsert(); +} + +void AbstractDatabase::resetDeletes() +{ + ASSERT(m_databaseAuthorizer); + m_databaseAuthorizer->resetDeletes(); +} + +bool AbstractDatabase::hadDeletes() +{ + ASSERT(m_databaseAuthorizer); + return m_databaseAuthorizer->hadDeletes(); +} + +void AbstractDatabase::resetAuthorizer() +{ + if (m_databaseAuthorizer) + m_databaseAuthorizer->reset(); +} + +unsigned long long AbstractDatabase::maximumSize() const +{ + return DatabaseTracker::tracker().getMaxSizeForDatabase(this); +} + +void AbstractDatabase::incrementalVacuumIfNeeded() +{ + int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize(); + int64_t totalSize = m_sqliteDatabase.totalSize(); + if (totalSize <= 10 * freeSpaceSize) + m_sqliteDatabase.runIncrementalVacuumCommand(); +} + +void AbstractDatabase::interrupt() +{ + m_sqliteDatabase.interrupt(); +} + +bool AbstractDatabase::isInterrupted() +{ + MutexLocker locker(m_sqliteDatabase.databaseMutex()); + return m_sqliteDatabase.isInterrupted(); +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/AbstractDatabase.h b/Source/WebCore/storage/AbstractDatabase.h new file mode 100644 index 0000000..bd5d0db --- /dev/null +++ b/Source/WebCore/storage/AbstractDatabase.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef AbstractDatabase_h +#define AbstractDatabase_h + +#if ENABLE(DATABASE) + +#include "ExceptionCode.h" +#include "PlatformString.h" +#include "SQLiteDatabase.h" +#include <wtf/Forward.h> +#include <wtf/ThreadSafeShared.h> +#ifndef NDEBUG +#include "SecurityOrigin.h" +#endif + +namespace WebCore { + +class DatabaseAuthorizer; +class ScriptExecutionContext; +class SecurityOrigin; + +class AbstractDatabase : public ThreadSafeShared<AbstractDatabase> { +public: + static bool isAvailable(); + static void setIsAvailable(bool available); + + virtual ~AbstractDatabase(); + + virtual String version() const; + + bool opened() const { return m_opened; } + bool isNew() const { return m_new; } + + virtual ScriptExecutionContext* scriptExecutionContext() const; + virtual SecurityOrigin* securityOrigin() const; + virtual String stringIdentifier() const; + virtual String displayName() const; + virtual unsigned long estimatedSize() const; + virtual String fileName() const; + SQLiteDatabase& sqliteDatabase() { return m_sqliteDatabase; } + + unsigned long long maximumSize() const; + void incrementalVacuumIfNeeded(); + void interrupt(); + bool isInterrupted(); + + // FIXME: move all version-related methods to a DatabaseVersionTracker class + bool versionMatchesExpected() const; + void setExpectedVersion(const String& version); + bool getVersionFromDatabase(String& version); + bool setVersionInDatabase(const String& version); + + void disableAuthorizer(); + void enableAuthorizer(); + void setAuthorizerReadOnly(); + void setAuthorizerPermissions(int permissions); + bool lastActionChangedDatabase(); + bool lastActionWasInsert(); + void resetDeletes(); + bool hadDeletes(); + void resetAuthorizer(); + + virtual void markAsDeletedAndClose() = 0; + virtual void closeImmediately() = 0; + +protected: + AbstractDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion, + const String& displayName, unsigned long estimatedSize); + + void closeDatabase(); + + virtual bool performOpenAndVerify(bool shouldSetVersionInNewDatabase, ExceptionCode& ec); + + static const String& databaseInfoTableName(); + + RefPtr<ScriptExecutionContext> m_scriptExecutionContext; + RefPtr<SecurityOrigin> m_contextThreadSecurityOrigin; + + String m_name; + String m_expectedVersion; + String m_displayName; + unsigned long m_estimatedSize; + String m_filename; + +#ifndef NDEBUG + String databaseDebugName() const { return m_contextThreadSecurityOrigin->toString() + "::" + m_name; } +#endif + +private: + static const String& databaseVersionKey(); + + int m_guid; + bool m_opened; + bool m_new; + + SQLiteDatabase m_sqliteDatabase; + + RefPtr<DatabaseAuthorizer> m_databaseAuthorizer; +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) + +#endif // AbstractDatabase_h diff --git a/Source/WebCore/storage/ChangeVersionWrapper.cpp b/Source/WebCore/storage/ChangeVersionWrapper.cpp new file mode 100644 index 0000000..17be716 --- /dev/null +++ b/Source/WebCore/storage/ChangeVersionWrapper.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "ChangeVersionWrapper.h" + +#if ENABLE(DATABASE) +#include "Database.h" +#include "SQLError.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +ChangeVersionWrapper::ChangeVersionWrapper(const String& oldVersion, const String& newVersion) + : m_oldVersion(oldVersion.crossThreadString()) + , m_newVersion(newVersion.crossThreadString()) +{ +} + +bool ChangeVersionWrapper::performPreflight(SQLTransaction* transaction) +{ + ASSERT(transaction && transaction->database()); + + String actualVersion; + + if (!transaction->database()->getVersionFromDatabase(actualVersion)) { + LOG_ERROR("Unable to retrieve actual current version from database"); + m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to verify current version of database"); + return false; + } + + if (actualVersion != m_oldVersion) { + LOG_ERROR("Old version doesn't match actual version"); + m_sqlError = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match"); + return false; + } + + return true; +} + +bool ChangeVersionWrapper::performPostflight(SQLTransaction* transaction) +{ + ASSERT(transaction && transaction->database()); + + if (!transaction->database()->setVersionInDatabase(m_newVersion)) { + LOG_ERROR("Unable to set new version in database"); + m_sqlError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to set new version in database"); + return false; + } + + transaction->database()->setExpectedVersion(m_newVersion); + + return true; +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/ChangeVersionWrapper.h b/Source/WebCore/storage/ChangeVersionWrapper.h new file mode 100644 index 0000000..86cc182 --- /dev/null +++ b/Source/WebCore/storage/ChangeVersionWrapper.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef ChangeVersionWrapper_h +#define ChangeVersionWrapper_h + +#if ENABLE(DATABASE) + +#include "PlatformString.h" +#include "SQLTransaction.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class SQLError; + +class ChangeVersionWrapper : public SQLTransactionWrapper { +public: + static PassRefPtr<ChangeVersionWrapper> create(const String& oldVersion, const String& newVersion) { return adoptRef(new ChangeVersionWrapper(oldVersion, newVersion)); } + + virtual bool performPreflight(SQLTransaction*); + virtual bool performPostflight(SQLTransaction*); + + virtual SQLError* sqlError() const { return m_sqlError.get(); } + +private: + ChangeVersionWrapper(const String& oldVersion, const String& newVersion); + + String m_oldVersion; + String m_newVersion; + RefPtr<SQLError> m_sqlError; +}; + +} // namespace WebCore + +#endif + +#endif // ChangeVersionWrapper_h diff --git a/Source/WebCore/storage/Database.cpp b/Source/WebCore/storage/Database.cpp new file mode 100644 index 0000000..920f75b --- /dev/null +++ b/Source/WebCore/storage/Database.cpp @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "Database.h" + +#if ENABLE(DATABASE) +#include "ChangeVersionWrapper.h" +#include "DatabaseCallback.h" +#include "DatabaseTask.h" +#include "DatabaseThread.h" +#include "DatabaseTracker.h" +#include "Document.h" +#include "InspectorController.h" +#include "Logging.h" +#include "NotImplemented.h" +#include "Page.h" +#include "SQLTransactionCallback.h" +#include "SQLTransactionClient.h" +#include "SQLTransactionCoordinator.h" +#include "SQLTransactionErrorCallback.h" +#include "SQLiteStatement.h" +#include "ScriptController.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include "VoidCallback.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/CString.h> + +#if USE(JSC) +#include "JSDOMWindow.h" +#endif + +namespace WebCore { + +class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback) + { + return adoptPtr(new DatabaseCreationCallbackTask(database, creationCallback)); + } + + virtual void performTask(ScriptExecutionContext*) + { + m_creationCallback->handleEvent(m_database.get()); + } + +private: + DatabaseCreationCallbackTask(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> callback) + : m_database(database) + , m_creationCallback(callback) + { + } + + RefPtr<Database> m_database; + RefPtr<DatabaseCallback> m_creationCallback; +}; + +PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, const String& name, + const String& expectedVersion, const String& displayName, + unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, + ExceptionCode& e) +{ + if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) { + LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data()); + return 0; + } + + RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize)); + + if (!database->openAndVerifyVersion(!creationCallback, e)) { + LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data()); + DatabaseTracker::tracker().removeOpenDatabase(database.get()); + return 0; + } + + DatabaseTracker::tracker().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize); + + context->setHasOpenDatabases(); +#if ENABLE(INSPECTOR) + if (context->isDocument()) { + Document* document = static_cast<Document*>(context); + if (Page* page = document->page()) + page->inspectorController()->didOpenDatabase(database, context->securityOrigin()->host(), name, expectedVersion); + } +#endif + + // If it's a new database and a creation callback was provided, reset the expected + // version to "" and schedule the creation callback. Because of some subtle String + // implementation issues, we have to reset m_expectedVersion here instead of doing + // it inside performOpenAndVerify() which is run on the DB thread. + if (database->isNew() && creationCallback.get()) { + database->m_expectedVersion = ""; + LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get()); + database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback)); + } + + return database; +} + +Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) + : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize) + , m_transactionInProgress(false) + , m_isTransactionQueueEnabled(true) + , m_deleted(false) +{ + m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->threadsafeCopy(); + + ScriptController::initializeThreading(); + ASSERT(m_scriptExecutionContext->databaseThread()); +} + +class DerefContextTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<DerefContextTask> create(PassRefPtr<ScriptExecutionContext> context) + { + return adoptPtr(new DerefContextTask(context)); + } + + virtual void performTask(ScriptExecutionContext* context) + { + ASSERT_UNUSED(context, context == m_context); + m_context.clear(); + } + + virtual bool isCleanupTask() const { return true; } + +private: + DerefContextTask(PassRefPtr<ScriptExecutionContext> context) + : m_context(context) + { + } + + RefPtr<ScriptExecutionContext> m_context; +}; + +Database::~Database() +{ + // The reference to the ScriptExecutionContext needs to be cleared on the JavaScript thread. If we're on that thread already, we can just let the RefPtr's destruction do the dereffing. + if (!m_scriptExecutionContext->isContextThread()) { + // Grab a pointer to the script execution here because we're releasing it when we pass it to + // DerefContextTask::create. + ScriptExecutionContext* scriptExecutionContext = m_scriptExecutionContext.get(); + + scriptExecutionContext->postTask(DerefContextTask::create(m_scriptExecutionContext.release())); + } +} + +String Database::version() const +{ + if (m_deleted) + return String(); + return AbstractDatabase::version(); +} + +bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, ExceptionCode& e) +{ + DatabaseTaskSynchronizer synchronizer; + if (!m_scriptExecutionContext->databaseThread() || m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer)) + return false; + + bool success = false; + OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, e, success); + m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); + synchronizer.waitForTaskCompletion(); + + return success; +} + +void Database::markAsDeletedAndClose() +{ + if (m_deleted || !m_scriptExecutionContext->databaseThread()) + return; + + LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this); + m_deleted = true; + + DatabaseTaskSynchronizer synchronizer; + if (m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer)) { + LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); + return; + } + + OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer); + m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); + synchronizer.waitForTaskCompletion(); +} + +void Database::close() +{ + ASSERT(m_scriptExecutionContext->databaseThread()); + ASSERT(currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID()); + + { + MutexLocker locker(m_transactionInProgressMutex); + m_isTransactionQueueEnabled = false; + m_transactionInProgress = false; + } + + closeDatabase(); + + // Must ref() before calling databaseThread()->recordDatabaseClosed(). + RefPtr<Database> protect = this; + m_scriptExecutionContext->databaseThread()->recordDatabaseClosed(this); + m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this); + DatabaseTracker::tracker().removeOpenDatabase(this); +} + +void Database::closeImmediately() +{ + DatabaseThread* databaseThread = scriptExecutionContext()->databaseThread(); + if (databaseThread && !databaseThread->terminationRequested() && opened()) + databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0)); +} + +unsigned long long Database::maximumSize() const +{ + return DatabaseTracker::tracker().getMaxSizeForDatabase(this); +} + +bool Database::performOpenAndVerify(bool setVersionInNewDatabase, ExceptionCode& e) +{ + if (AbstractDatabase::performOpenAndVerify(setVersionInNewDatabase, e)) { + if (m_scriptExecutionContext->databaseThread()) + m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this); + + return true; + } + + return false; +} + +void Database::changeVersion(const String& oldVersion, const String& newVersion, + PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, + PassRefPtr<VoidCallback> successCallback) +{ + m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion))); + MutexLocker locker(m_transactionInProgressMutex); + if (!m_transactionInProgress) + scheduleTransaction(); +} + +void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) +{ + runTransaction(callback, errorCallback, successCallback, false); +} + +void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) +{ + runTransaction(callback, errorCallback, successCallback, true); +} + +void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, + PassRefPtr<VoidCallback> successCallback, bool readOnly) +{ + m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly)); + MutexLocker locker(m_transactionInProgressMutex); + if (!m_transactionInProgress) + scheduleTransaction(); +} + +void Database::inProgressTransactionCompleted() +{ + MutexLocker locker(m_transactionInProgressMutex); + m_transactionInProgress = false; + scheduleTransaction(); +} + +void Database::scheduleTransaction() +{ + ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller. + RefPtr<SQLTransaction> transaction; + + if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty()) { + transaction = m_transactionQueue.takeFirst(); + } + + if (transaction && m_scriptExecutionContext->databaseThread()) { + OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); + LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction()); + m_transactionInProgress = true; + m_scriptExecutionContext->databaseThread()->scheduleTask(task.release()); + } else + m_transactionInProgress = false; +} + +void Database::scheduleTransactionStep(SQLTransaction* transaction, bool immediately) +{ + if (!m_scriptExecutionContext->databaseThread()) + return; + + OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); + LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get()); + if (immediately) + m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); + else + m_scriptExecutionContext->databaseThread()->scheduleTask(task.release()); +} + +class DeliverPendingCallbackTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction) + { + return adoptPtr(new DeliverPendingCallbackTask(transaction)); + } + + virtual void performTask(ScriptExecutionContext*) + { + m_transaction->performPendingCallback(); + } + +private: + DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction) + : m_transaction(transaction) + { + } + + RefPtr<SQLTransaction> m_transaction; +}; + +void Database::scheduleTransactionCallback(SQLTransaction* transaction) +{ + m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction)); +} + +Vector<String> Database::performGetTableNames() +{ + disableAuthorizer(); + + SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';"); + if (statement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data()); + enableAuthorizer(); + return Vector<String>(); + } + + Vector<String> tableNames; + int result; + while ((result = statement.step()) == SQLResultRow) { + String name = statement.getColumnText(0); + if (name != databaseInfoTableName()) + tableNames.append(name); + } + + enableAuthorizer(); + + if (result != SQLResultDone) { + LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data()); + return Vector<String>(); + } + + return tableNames; +} + +SQLTransactionClient* Database::transactionClient() const +{ + return m_scriptExecutionContext->databaseThread()->transactionClient(); +} + +SQLTransactionCoordinator* Database::transactionCoordinator() const +{ + return m_scriptExecutionContext->databaseThread()->transactionCoordinator(); +} + +Vector<String> Database::tableNames() +{ + // FIXME: Not using threadsafeCopy on these strings looks ok since threads take strict turns + // in dealing with them. However, if the code changes, this may not be true anymore. + Vector<String> result; + DatabaseTaskSynchronizer synchronizer; + if (!m_scriptExecutionContext->databaseThread() || m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer)) + return result; + + OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result); + m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); + synchronizer.waitForTaskCompletion(); + + return result; +} + +SecurityOrigin* Database::securityOrigin() const +{ + if (m_scriptExecutionContext->isContextThread()) + return m_contextThreadSecurityOrigin.get(); + if (currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID()) + return m_databaseThreadSecurityOrigin.get(); + return 0; +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/Database.h b/Source/WebCore/storage/Database.h new file mode 100644 index 0000000..47001a4 --- /dev/null +++ b/Source/WebCore/storage/Database.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef Database_h +#define Database_h + +#if ENABLE(DATABASE) +#include "AbstractDatabase.h" +#include "ExceptionCode.h" +#include "PlatformString.h" + +#include <wtf/Deque.h> +#include <wtf/Forward.h> + +namespace WebCore { + +class DatabaseCallback; +class ScriptExecutionContext; +class SecurityOrigin; +class SQLTransaction; +class SQLTransactionCallback; +class SQLTransactionClient; +class SQLTransactionCoordinator; +class SQLTransactionErrorCallback; +class VoidCallback; + +class Database : public AbstractDatabase { +public: + virtual ~Database(); + + // Direct support for the DOM API + static PassRefPtr<Database> openDatabase(ScriptExecutionContext*, const String& name, const String& expectedVersion, const String& displayName, + unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, ExceptionCode&); + virtual String version() const; + void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionCallback>, + PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback); + void transaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback); + void readTransaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback> successCallback); + + // Internal engine support + Vector<String> tableNames(); + + virtual SecurityOrigin* securityOrigin() const; + + virtual void markAsDeletedAndClose(); + bool deleted() const { return m_deleted; } + + void close(); + virtual void closeImmediately(); + + unsigned long long databaseSize() const; + unsigned long long maximumSize() const; + + void scheduleTransactionCallback(SQLTransaction*); + void scheduleTransactionStep(SQLTransaction*, bool immediately = false); + + SQLTransactionClient* transactionClient() const; + SQLTransactionCoordinator* transactionCoordinator() const; + +private: + class DatabaseOpenTask; + class DatabaseCloseTask; + class DatabaseTransactionTask; + class DatabaseTableNamesTask; + + Database(ScriptExecutionContext*, const String& name, const String& expectedVersion, + const String& displayName, unsigned long estimatedSize); + void runTransaction(PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, + PassRefPtr<VoidCallback> successCallback, bool readOnly); + + bool openAndVerifyVersion(bool setVersionInNewDatabase, ExceptionCode&); + virtual bool performOpenAndVerify(bool setVersionInNewDatabase, ExceptionCode&); + + void inProgressTransactionCompleted(); + void scheduleTransaction(); + + Vector<String> performGetTableNames(); + + static void deliverPendingCallback(void*); + + Deque<RefPtr<SQLTransaction> > m_transactionQueue; + Mutex m_transactionInProgressMutex; + bool m_transactionInProgress; + bool m_isTransactionQueueEnabled; + + RefPtr<SecurityOrigin> m_databaseThreadSecurityOrigin; + + bool m_deleted; +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) + +#endif // Database_h diff --git a/Source/WebCore/storage/Database.idl b/Source/WebCore/storage/Database.idl new file mode 100644 index 0000000..20c2fa1 --- /dev/null +++ b/Source/WebCore/storage/Database.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=DATABASE, + OmitConstructor, + NoStaticTables + ] Database { + readonly attribute DOMString version; + [RequiresAllArguments=Raise] void changeVersion(in DOMString oldVersion, in DOMString newVersion, in [Callback, Optional] SQLTransactionCallback callback, in [Callback, Optional] SQLTransactionErrorCallback errorCallback, in [Callback, Optional] VoidCallback successCallback); + [RequiresAllArguments=Raise] void transaction(in [Callback] SQLTransactionCallback callback, in [Callback, Optional] SQLTransactionErrorCallback errorCallback, in [Callback, Optional] VoidCallback successCallback); + [RequiresAllArguments=Raise] void readTransaction(in [Callback] SQLTransactionCallback callback, in [Callback, Optional] SQLTransactionErrorCallback errorCallback, in [Callback, Optional] VoidCallback successCallback); + }; + +} diff --git a/Source/WebCore/storage/DatabaseAuthorizer.cpp b/Source/WebCore/storage/DatabaseAuthorizer.cpp new file mode 100644 index 0000000..b90565c --- /dev/null +++ b/Source/WebCore/storage/DatabaseAuthorizer.cpp @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "DatabaseAuthorizer.h" + +#if ENABLE(DATABASE) + +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +PassRefPtr<DatabaseAuthorizer> DatabaseAuthorizer::create(const String& databaseInfoTableName) +{ + return adoptRef(new DatabaseAuthorizer(databaseInfoTableName)); +} + +DatabaseAuthorizer::DatabaseAuthorizer(const String& databaseInfoTableName) + : m_securityEnabled(false) + , m_databaseInfoTableName(databaseInfoTableName) +{ + reset(); + addWhitelistedFunctions(); +} + +void DatabaseAuthorizer::reset() +{ + m_lastActionWasInsert = false; + m_lastActionChangedDatabase = false; + m_permissions = ReadWriteMask; +} + +void DatabaseAuthorizer::resetDeletes() +{ + m_hadDeletes = false; +} + +void DatabaseAuthorizer::addWhitelistedFunctions() +{ + // SQLite functions used to help implement some operations + // ALTER TABLE helpers + m_whitelistedFunctions.add("sqlite_rename_table"); + m_whitelistedFunctions.add("sqlite_rename_trigger"); + // GLOB helpers + m_whitelistedFunctions.add("glob"); + + // SQLite core functions + m_whitelistedFunctions.add("abs"); + m_whitelistedFunctions.add("changes"); + m_whitelistedFunctions.add("coalesce"); + m_whitelistedFunctions.add("glob"); + m_whitelistedFunctions.add("ifnull"); + m_whitelistedFunctions.add("hex"); + m_whitelistedFunctions.add("last_insert_rowid"); + m_whitelistedFunctions.add("length"); + m_whitelistedFunctions.add("like"); + m_whitelistedFunctions.add("lower"); + m_whitelistedFunctions.add("ltrim"); + m_whitelistedFunctions.add("max"); + m_whitelistedFunctions.add("min"); + m_whitelistedFunctions.add("nullif"); + m_whitelistedFunctions.add("quote"); + m_whitelistedFunctions.add("replace"); + m_whitelistedFunctions.add("round"); + m_whitelistedFunctions.add("rtrim"); + m_whitelistedFunctions.add("soundex"); + m_whitelistedFunctions.add("sqlite_source_id"); + m_whitelistedFunctions.add("sqlite_version"); + m_whitelistedFunctions.add("substr"); + m_whitelistedFunctions.add("total_changes"); + m_whitelistedFunctions.add("trim"); + m_whitelistedFunctions.add("typeof"); + m_whitelistedFunctions.add("upper"); + m_whitelistedFunctions.add("zeroblob"); + + // SQLite date and time functions + m_whitelistedFunctions.add("date"); + m_whitelistedFunctions.add("time"); + m_whitelistedFunctions.add("datetime"); + m_whitelistedFunctions.add("julianday"); + m_whitelistedFunctions.add("strftime"); + + // SQLite aggregate functions + // max() and min() are already in the list + m_whitelistedFunctions.add("avg"); + m_whitelistedFunctions.add("count"); + m_whitelistedFunctions.add("group_concat"); + m_whitelistedFunctions.add("sum"); + m_whitelistedFunctions.add("total"); + + // SQLite FTS functions + m_whitelistedFunctions.add("match"); + m_whitelistedFunctions.add("snippet"); + m_whitelistedFunctions.add("offsets"); + m_whitelistedFunctions.add("optimize"); + + // SQLite ICU functions + // like(), lower() and upper() are already in the list + m_whitelistedFunctions.add("regexp"); +} + +int DatabaseAuthorizer::createTable(const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + m_lastActionChangedDatabase = true; + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::createTempTable(const String& tableName) +{ + // SQLITE_CREATE_TEMP_TABLE results in a UPDATE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_CREATE_TEMP_TABLE in these cases + if (!allowWrite()) + return SQLAuthDeny; + + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::dropTable(const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + return updateDeletesBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::dropTempTable(const String& tableName) +{ + // SQLITE_DROP_TEMP_TABLE results in a DELETE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_DROP_TEMP_TABLE in these cases + if (!allowWrite()) + return SQLAuthDeny; + + return updateDeletesBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::allowAlterTable(const String&, const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + m_lastActionChangedDatabase = true; + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::createIndex(const String&, const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + m_lastActionChangedDatabase = true; + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::createTempIndex(const String&, const String& tableName) +{ + // SQLITE_CREATE_TEMP_INDEX should result in a UPDATE or INSERT operation, + // which is not allowed in read-only transactions or private browsing, + // so we might as well disallow SQLITE_CREATE_TEMP_INDEX in these cases + if (!allowWrite()) + return SQLAuthDeny; + + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::dropIndex(const String&, const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + return updateDeletesBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::dropTempIndex(const String&, const String& tableName) +{ + // SQLITE_DROP_TEMP_INDEX should result in a DELETE operation, which is + // not allowed in read-only transactions or private browsing, so we might + // as well disallow SQLITE_DROP_TEMP_INDEX in these cases + if (!allowWrite()) + return SQLAuthDeny; + + return updateDeletesBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::createTrigger(const String&, const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + m_lastActionChangedDatabase = true; + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::createTempTrigger(const String&, const String& tableName) +{ + // SQLITE_CREATE_TEMP_TRIGGER results in a INSERT operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_CREATE_TEMP_TRIGGER in these cases + if (!allowWrite()) + return SQLAuthDeny; + + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::dropTrigger(const String&, const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + return updateDeletesBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::dropTempTrigger(const String&, const String& tableName) +{ + // SQLITE_DROP_TEMP_TRIGGER results in a DELETE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_DROP_TEMP_TRIGGER in these cases + if (!allowWrite()) + return SQLAuthDeny; + + return updateDeletesBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::createView(const String&) +{ + return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow); +} + +int DatabaseAuthorizer::createTempView(const String&) +{ + // SQLITE_CREATE_TEMP_VIEW results in a UPDATE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_CREATE_TEMP_VIEW in these cases + return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow); +} + +int DatabaseAuthorizer::dropView(const String&) +{ + if (!allowWrite()) + return SQLAuthDeny; + + m_hadDeletes = true; + return SQLAuthAllow; +} + +int DatabaseAuthorizer::dropTempView(const String&) +{ + // SQLITE_DROP_TEMP_VIEW results in a DELETE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_DROP_TEMP_VIEW in these cases + if (!allowWrite()) + return SQLAuthDeny; + + m_hadDeletes = true; + return SQLAuthAllow; +} + +int DatabaseAuthorizer::createVTable(const String& tableName, const String& moduleName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + // Allow only the FTS3 extension + if (!equalIgnoringCase(moduleName, "fts3")) + return SQLAuthDeny; + + m_lastActionChangedDatabase = true; + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::dropVTable(const String& tableName, const String& moduleName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + // Allow only the FTS3 extension + if (!equalIgnoringCase(moduleName, "fts3")) + return SQLAuthDeny; + + return updateDeletesBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::allowDelete(const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + return updateDeletesBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::allowInsert(const String& tableName) +{ + if (!allowWrite()) + return SQLAuthDeny; + + m_lastActionChangedDatabase = true; + m_lastActionWasInsert = true; + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::allowUpdate(const String& tableName, const String&) +{ + if (!allowWrite()) + return SQLAuthDeny; + + m_lastActionChangedDatabase = true; + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::allowTransaction() +{ + return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow; +} + +int DatabaseAuthorizer::allowRead(const String& tableName, const String&) +{ + if (m_permissions & NoAccessMask && m_securityEnabled) + return SQLAuthDeny; + + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::allowReindex(const String&) +{ + return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow); +} + +int DatabaseAuthorizer::allowAnalyze(const String& tableName) +{ + return denyBasedOnTableName(tableName); +} + +int DatabaseAuthorizer::allowPragma(const String&, const String&) +{ + return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow; +} + +int DatabaseAuthorizer::allowAttach(const String&) +{ + return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow; +} + +int DatabaseAuthorizer::allowDetach(const String&) +{ + return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow; +} + +int DatabaseAuthorizer::allowFunction(const String& functionName) +{ + if (m_securityEnabled && !m_whitelistedFunctions.contains(functionName)) + return SQLAuthDeny; + + return SQLAuthAllow; +} + +void DatabaseAuthorizer::disable() +{ + m_securityEnabled = false; +} + +void DatabaseAuthorizer::enable() +{ + m_securityEnabled = true; +} + +bool DatabaseAuthorizer::allowWrite() +{ + return !(m_securityEnabled && (m_permissions & ReadOnlyMask || m_permissions & NoAccessMask)); +} + +void DatabaseAuthorizer::setReadOnly() +{ + m_permissions |= ReadOnlyMask; +} + +void DatabaseAuthorizer::setPermissions(int permissions) +{ + m_permissions = permissions; +} + +int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) const +{ + if (!m_securityEnabled) + return SQLAuthAllow; + + // Sadly, normal creates and drops end up affecting sqlite_master in an authorizer callback, so + // it will be tough to enforce all of the following policies + //if (equalIgnoringCase(tableName, "sqlite_master") || equalIgnoringCase(tableName, "sqlite_temp_master") || + // equalIgnoringCase(tableName, "sqlite_sequence") || equalIgnoringCase(tableName, Database::databaseInfoTableName())) + // return SQLAuthDeny; + + if (equalIgnoringCase(tableName, m_databaseInfoTableName)) + return SQLAuthDeny; + + return SQLAuthAllow; +} + +int DatabaseAuthorizer::updateDeletesBasedOnTableName(const String& tableName) +{ + int allow = denyBasedOnTableName(tableName); + if (allow) + m_hadDeletes = true; + return allow; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/DatabaseAuthorizer.h b/Source/WebCore/storage/DatabaseAuthorizer.h new file mode 100644 index 0000000..da7761d --- /dev/null +++ b/Source/WebCore/storage/DatabaseAuthorizer.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef DatabaseAuthorizer_h +#define DatabaseAuthorizer_h + +#include "PlatformString.h" +#include <wtf/Forward.h> +#include <wtf/HashSet.h> +#include <wtf/ThreadSafeShared.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +extern const int SQLAuthAllow; +extern const int SQLAuthIgnore; +extern const int SQLAuthDeny; + +class DatabaseAuthorizer : public ThreadSafeShared<DatabaseAuthorizer> { +public: + + enum Permissions { + ReadWriteMask = 0, + ReadOnlyMask = 1 << 1, + NoAccessMask = 1 << 2 + }; + + static PassRefPtr<DatabaseAuthorizer> create(const String& databaseInfoTableName); + + int createTable(const String& tableName); + int createTempTable(const String& tableName); + int dropTable(const String& tableName); + int dropTempTable(const String& tableName); + int allowAlterTable(const String& databaseName, const String& tableName); + + int createIndex(const String& indexName, const String& tableName); + int createTempIndex(const String& indexName, const String& tableName); + int dropIndex(const String& indexName, const String& tableName); + int dropTempIndex(const String& indexName, const String& tableName); + + int createTrigger(const String& triggerName, const String& tableName); + int createTempTrigger(const String& triggerName, const String& tableName); + int dropTrigger(const String& triggerName, const String& tableName); + int dropTempTrigger(const String& triggerName, const String& tableName); + + int createView(const String& viewName); + int createTempView(const String& viewName); + int dropView(const String& viewName); + int dropTempView(const String& viewName); + + int createVTable(const String& tableName, const String& moduleName); + int dropVTable(const String& tableName, const String& moduleName); + + int allowDelete(const String& tableName); + int allowInsert(const String& tableName); + int allowUpdate(const String& tableName, const String& columnName); + int allowTransaction(); + + int allowSelect() { return SQLAuthAllow; } + int allowRead(const String& tableName, const String& columnName); + + int allowReindex(const String& indexName); + int allowAnalyze(const String& tableName); + int allowFunction(const String& functionName); + int allowPragma(const String& pragmaName, const String& firstArgument); + + int allowAttach(const String& filename); + int allowDetach(const String& databaseName); + + void disable(); + void enable(); + void setReadOnly(); + void setPermissions(int permissions); + + void reset(); + void resetDeletes(); + + bool lastActionWasInsert() const { return m_lastActionWasInsert; } + bool lastActionChangedDatabase() const { return m_lastActionChangedDatabase; } + bool hadDeletes() const { return m_hadDeletes; } + +private: + DatabaseAuthorizer(const String& databaseInfoTableName); + void addWhitelistedFunctions(); + int denyBasedOnTableName(const String&) const; + int updateDeletesBasedOnTableName(const String&); + bool allowWrite(); + + int m_permissions; + bool m_securityEnabled : 1; + bool m_lastActionWasInsert : 1; + bool m_lastActionChangedDatabase : 1; + bool m_hadDeletes : 1; + + const String m_databaseInfoTableName; + + HashSet<String, CaseFoldingHash> m_whitelistedFunctions; +}; + +} // namespace WebCore + +#endif // DatabaseAuthorizer_h diff --git a/Source/WebCore/storage/DatabaseCallback.h b/Source/WebCore/storage/DatabaseCallback.h new file mode 100644 index 0000000..8ad56ed --- /dev/null +++ b/Source/WebCore/storage/DatabaseCallback.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef DatabaseCallback_h +#define DatabaseCallback_h + +#if ENABLE(DATABASE) + +#include <wtf/ThreadSafeShared.h> + +namespace WebCore { + +class Database; +class DatabaseSync; + +class DatabaseCallback : public ThreadSafeShared<DatabaseCallback> { +public: + virtual ~DatabaseCallback() { } + virtual bool handleEvent(Database*) = 0; + virtual bool handleEvent(DatabaseSync*) = 0; +}; + +} + +#endif + +#endif // DatabaseCallback_h diff --git a/Source/WebCore/storage/DatabaseCallback.idl b/Source/WebCore/storage/DatabaseCallback.idl new file mode 100644 index 0000000..c218680 --- /dev/null +++ b/Source/WebCore/storage/DatabaseCallback.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + interface [ + Conditional=DATABASE, + Callback + ] DatabaseCallback { + boolean handleEvent(in Database database); + boolean handleEvent(in DatabaseSync database); + }; +} diff --git a/Source/WebCore/storage/DatabaseDetails.h b/Source/WebCore/storage/DatabaseDetails.h new file mode 100644 index 0000000..c2217af --- /dev/null +++ b/Source/WebCore/storage/DatabaseDetails.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef DatabaseDetails_h +#define DatabaseDetails_h + +#if ENABLE(DATABASE) + +#include "PlatformString.h" + +namespace WebCore { + +class DatabaseDetails { +public: + DatabaseDetails() + : m_expectedUsage(0) + , m_currentUsage(0) + { +#ifndef NDEBUG + m_thread = currentThread(); +#endif + } + + DatabaseDetails(const String& databaseName, const String& displayName, unsigned long long expectedUsage, unsigned long long currentUsage) + : m_name(databaseName) + , m_displayName(displayName) + , m_expectedUsage(expectedUsage) + , m_currentUsage(currentUsage) + { +#ifndef NDEBUG + m_thread = currentThread(); +#endif + } + + const String& name() const { return m_name; } + const String& displayName() const { return m_displayName; } + uint64_t expectedUsage() const { return m_expectedUsage; } + uint64_t currentUsage() const { return m_currentUsage; } +#ifndef NDEBUG + ThreadIdentifier thread() const { return m_thread; } +#endif + +private: + String m_name; + String m_displayName; + uint64_t m_expectedUsage; + uint64_t m_currentUsage; +#ifndef NDEBUG + ThreadIdentifier m_thread; +#endif +}; + +} // namespace WebCore + +#endif + +#endif // DatabaseDetails_h diff --git a/Source/WebCore/storage/DatabaseSync.cpp b/Source/WebCore/storage/DatabaseSync.cpp new file mode 100644 index 0000000..5634a60 --- /dev/null +++ b/Source/WebCore/storage/DatabaseSync.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 "DatabaseSync.h" + +#if ENABLE(DATABASE) +#include "DatabaseCallback.h" +#include "DatabaseTracker.h" +#include "Logging.h" +#include "SQLException.h" +#include "SQLTransactionSync.h" +#include "SQLTransactionSyncCallback.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +PassRefPtr<DatabaseSync> DatabaseSync::openDatabaseSync(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, + unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec) +{ + ASSERT(context->isContextThread()); + + if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) { + LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data()); + return 0; + } + + RefPtr<DatabaseSync> database = adoptRef(new DatabaseSync(context, name, expectedVersion, displayName, estimatedSize)); + + if (!database->performOpenAndVerify(!creationCallback, ec)) { + LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data()); + DatabaseTracker::tracker().removeOpenDatabase(database.get()); + return 0; + } + + DatabaseTracker::tracker().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize); + + if (database->isNew() && creationCallback.get()) { + database->m_expectedVersion = ""; + LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get()); + creationCallback->handleEvent(database.get()); + } + + return database; +} + +DatabaseSync::DatabaseSync(ScriptExecutionContext* context, const String& name, const String& expectedVersion, + const String& displayName, unsigned long estimatedSize) + : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize) +{ +} + +DatabaseSync::~DatabaseSync() +{ + ASSERT(m_scriptExecutionContext->isContextThread()); + + if (opened()) { + DatabaseTracker::tracker().removeOpenDatabase(this); + closeDatabase(); + } +} + +void DatabaseSync::changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback> changeVersionCallback, ExceptionCode& ec) +{ + ASSERT(m_scriptExecutionContext->isContextThread()); + + if (sqliteDatabase().transactionInProgress()) { + ec = SQLException::DATABASE_ERR; + return; + } + + RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, changeVersionCallback, false); + if ((ec = transaction->begin())) + return; + + String actualVersion; + if (!getVersionFromDatabase(actualVersion)) { + ec = SQLException::UNKNOWN_ERR; + return; + } + + if (actualVersion != oldVersion) { + ec = SQLException::VERSION_ERR; + return; + } + + if ((ec = transaction->execute())) + return; + + if (!setVersionInDatabase(newVersion)) { + ec = SQLException::UNKNOWN_ERR; + return; + } + + if ((ec = transaction->commit())) + return; + + setExpectedVersion(newVersion); +} + +void DatabaseSync::transaction(PassRefPtr<SQLTransactionSyncCallback> callback, ExceptionCode& ec) +{ + runTransaction(callback, false, ec); +} + +void DatabaseSync::readTransaction(PassRefPtr<SQLTransactionSyncCallback> callback, ExceptionCode& ec) +{ + runTransaction(callback, true, ec); +} + +void DatabaseSync::runTransaction(PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly, ExceptionCode& ec) +{ + ASSERT(m_scriptExecutionContext->isContextThread()); + + if (sqliteDatabase().transactionInProgress()) { + ec = SQLException::DATABASE_ERR; + return; + } + + RefPtr<SQLTransactionSync> transaction = SQLTransactionSync::create(this, callback, readOnly); + if ((ec = transaction->begin()) || (ec = transaction->execute()) || (ec = transaction->commit())) + transaction->rollback(); +} + +void DatabaseSync::markAsDeletedAndClose() +{ + // FIXME: need to do something similar to closeImmediately(), but in a sync way +} + +class CloseSyncDatabaseOnContextThreadTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<CloseSyncDatabaseOnContextThreadTask> create(PassRefPtr<DatabaseSync> database) + { + return adoptPtr(new CloseSyncDatabaseOnContextThreadTask(database)); + } + + virtual void performTask(ScriptExecutionContext*) + { + m_database->closeImmediately(); + } + +private: + CloseSyncDatabaseOnContextThreadTask(PassRefPtr<DatabaseSync> database) + : m_database(database) + { + } + + RefPtr<DatabaseSync> m_database; +}; + +void DatabaseSync::closeImmediately() +{ + if (!m_scriptExecutionContext->isContextThread()) { + m_scriptExecutionContext->postTask(CloseSyncDatabaseOnContextThreadTask::create(this)); + return; + } + + if (!opened()) + return; + + DatabaseTracker::tracker().removeOpenDatabase(this); + + closeDatabase(); +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/DatabaseSync.h b/Source/WebCore/storage/DatabaseSync.h new file mode 100644 index 0000000..6563b23 --- /dev/null +++ b/Source/WebCore/storage/DatabaseSync.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef DatabaseSync_h +#define DatabaseSync_h + +#if ENABLE(DATABASE) +#include "AbstractDatabase.h" +#include "ExceptionCode.h" +#include "PlatformString.h" +#include <wtf/Forward.h> +#ifndef NDEBUG +#include "SecurityOrigin.h" +#endif + +namespace WebCore { + +class DatabaseCallback; +class SQLTransactionSync; +class SQLTransactionSyncCallback; +class ScriptExecutionContext; +class SecurityOrigin; + +// Instances of this class should be created and used only on the worker's context thread. +class DatabaseSync : public AbstractDatabase { +public: + virtual ~DatabaseSync(); + + static PassRefPtr<DatabaseSync> openDatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion, + const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, ExceptionCode&); + void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&); + void transaction(PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&); + void readTransaction(PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&); + + virtual void markAsDeletedAndClose(); + virtual void closeImmediately(); + +private: + DatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion, + const String& displayName, unsigned long estimatedSize); + void runTransaction(PassRefPtr<SQLTransactionSyncCallback>, bool readOnly, ExceptionCode&); +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) + +#endif // DatabaseSync_h diff --git a/Source/WebCore/storage/DatabaseSync.idl b/Source/WebCore/storage/DatabaseSync.idl new file mode 100644 index 0000000..0064991 --- /dev/null +++ b/Source/WebCore/storage/DatabaseSync.idl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +module storage { + + interface [ + Conditional=DATABASE, + OmitConstructor, + NoStaticTables + ] DatabaseSync { + readonly attribute DOMString version; + [RequiresAllArguments=Raise] void changeVersion(in DOMString oldVersion, in DOMString newVersion, in [Callback, Optional] SQLTransactionSyncCallback callback) raises(DOMException); + [RequiresAllArguments=Raise] void transaction(in [Callback] SQLTransactionSyncCallback callback) raises(DOMException); + [RequiresAllArguments=Raise] void readTransaction(in [Callback] SQLTransactionSyncCallback callback) raises(DOMException); + }; + +} diff --git a/Source/WebCore/storage/DatabaseTask.cpp b/Source/WebCore/storage/DatabaseTask.cpp new file mode 100644 index 0000000..343ae1e --- /dev/null +++ b/Source/WebCore/storage/DatabaseTask.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "DatabaseTask.h" + +#if ENABLE(DATABASE) + +#include "Database.h" +#include "Logging.h" + +namespace WebCore { + +DatabaseTaskSynchronizer::DatabaseTaskSynchronizer() + : m_taskCompleted(false) +#ifndef NDEBUG + , m_hasCheckedForTermination(false) +#endif +{ +} + +void DatabaseTaskSynchronizer::waitForTaskCompletion() +{ + m_synchronousMutex.lock(); + if (!m_taskCompleted) + m_synchronousCondition.wait(m_synchronousMutex); + m_synchronousMutex.unlock(); +} + +void DatabaseTaskSynchronizer::taskCompleted() +{ + m_synchronousMutex.lock(); + m_taskCompleted = true; + m_synchronousCondition.signal(); + m_synchronousMutex.unlock(); +} + +DatabaseTask::DatabaseTask(Database* database, DatabaseTaskSynchronizer* synchronizer) + : m_database(database) + , m_synchronizer(synchronizer) +#ifndef NDEBUG + , m_complete(false) +#endif +{ +} + +DatabaseTask::~DatabaseTask() +{ + ASSERT(m_complete || !m_synchronizer); +} + +void DatabaseTask::performTask() +{ + // Database tasks are meant to be used only once, so make sure this one hasn't been performed before. + ASSERT(!m_complete); + + LOG(StorageAPI, "Performing %s %p\n", debugTaskName(), this); + + m_database->resetAuthorizer(); + doPerformTask(); + + if (m_synchronizer) + m_synchronizer->taskCompleted(); + +#ifndef NDEBUG + m_complete = true; +#endif +} + +// *** DatabaseOpenTask *** +// Opens the database file and verifies the version matches the expected version. + +Database::DatabaseOpenTask::DatabaseOpenTask(Database* database, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success) + : DatabaseTask(database, synchronizer) + , m_setVersionInNewDatabase(setVersionInNewDatabase) + , m_code(code) + , m_success(success) +{ + ASSERT(synchronizer); // A task with output parameters is supposed to be synchronous. +} + +void Database::DatabaseOpenTask::doPerformTask() +{ + m_success = database()->performOpenAndVerify(m_setVersionInNewDatabase, m_code); +} + +#ifndef NDEBUG +const char* Database::DatabaseOpenTask::debugTaskName() const +{ + return "DatabaseOpenTask"; +} +#endif + +// *** DatabaseCloseTask *** +// Closes the database. + +Database::DatabaseCloseTask::DatabaseCloseTask(Database* database, DatabaseTaskSynchronizer* synchronizer) + : DatabaseTask(database, synchronizer) +{ +} + +void Database::DatabaseCloseTask::doPerformTask() +{ + database()->close(); +} + +#ifndef NDEBUG +const char* Database::DatabaseCloseTask::debugTaskName() const +{ + return "DatabaseCloseTask"; +} +#endif + +// *** DatabaseTransactionTask *** +// Starts a transaction that will report its results via a callback. + +Database::DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransaction> transaction) + : DatabaseTask(transaction->database(), 0) + , m_transaction(transaction) +{ +} + +void Database::DatabaseTransactionTask::doPerformTask() +{ + if (m_transaction->performNextStep()) + m_transaction->database()->inProgressTransactionCompleted(); +} + +#ifndef NDEBUG +const char* Database::DatabaseTransactionTask::debugTaskName() const +{ + return "DatabaseTransactionTask"; +} +#endif + +// *** DatabaseTableNamesTask *** +// Retrieves a list of all tables in the database - for WebInspector support. + +Database::DatabaseTableNamesTask::DatabaseTableNamesTask(Database* database, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names) + : DatabaseTask(database, synchronizer) + , m_tableNames(names) +{ + ASSERT(synchronizer); // A task with output parameters is supposed to be synchronous. +} + +void Database::DatabaseTableNamesTask::doPerformTask() +{ + m_tableNames = database()->performGetTableNames(); +} + +#ifndef NDEBUG +const char* Database::DatabaseTableNamesTask::debugTaskName() const +{ + return "DatabaseTableNamesTask"; +} +#endif + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/DatabaseTask.h b/Source/WebCore/storage/DatabaseTask.h new file mode 100644 index 0000000..b61e465 --- /dev/null +++ b/Source/WebCore/storage/DatabaseTask.h @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef DatabaseTask_h +#define DatabaseTask_h + +#if ENABLE(DATABASE) +#include "Database.h" +#include "ExceptionCode.h" +#include "PlatformString.h" +#include "SQLTransaction.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// Can be used to wait until DatabaseTask is completed. +// Has to be passed into DatabaseTask::create to be associated with the task. +class DatabaseTaskSynchronizer : public Noncopyable { +public: + DatabaseTaskSynchronizer(); + + // Called from main thread to wait until task is completed. + void waitForTaskCompletion(); + + // Called by the task. + void taskCompleted(); + +#ifndef NDEBUG + bool hasCheckedForTermination() const { return m_hasCheckedForTermination; } + void setHasCheckedForTermination() { m_hasCheckedForTermination = true; } +#endif + +private: + bool m_taskCompleted; + Mutex m_synchronousMutex; + ThreadCondition m_synchronousCondition; +#ifndef NDEBUG + bool m_hasCheckedForTermination; +#endif +}; + +class DatabaseTask : public Noncopyable { +public: + virtual ~DatabaseTask(); + + void performTask(); + + Database* database() const { return m_database; } +#ifndef NDEBUG + bool hasSynchronizer() const { return m_synchronizer; } + bool hasCheckedForTermination() const { return m_synchronizer->hasCheckedForTermination(); } +#endif + +protected: + DatabaseTask(Database*, DatabaseTaskSynchronizer*); + +private: + virtual void doPerformTask() = 0; + + Database* m_database; + DatabaseTaskSynchronizer* m_synchronizer; + +#ifndef NDEBUG + virtual const char* debugTaskName() const = 0; + bool m_complete; +#endif +}; + +class Database::DatabaseOpenTask : public DatabaseTask { +public: + static PassOwnPtr<DatabaseOpenTask> create(Database* db, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success) + { + return adoptPtr(new DatabaseOpenTask(db, setVersionInNewDatabase, synchronizer, code, success)); + } + +private: + DatabaseOpenTask(Database*, bool setVersionInNewDatabase, DatabaseTaskSynchronizer*, ExceptionCode&, bool& success); + + virtual void doPerformTask(); +#ifndef NDEBUG + virtual const char* debugTaskName() const; +#endif + + bool m_setVersionInNewDatabase; + ExceptionCode& m_code; + bool& m_success; +}; + +class Database::DatabaseCloseTask : public DatabaseTask { +public: + static PassOwnPtr<DatabaseCloseTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer) + { + return adoptPtr(new DatabaseCloseTask(db, synchronizer)); + } + +private: + DatabaseCloseTask(Database*, DatabaseTaskSynchronizer*); + + virtual void doPerformTask(); +#ifndef NDEBUG + virtual const char* debugTaskName() const; +#endif +}; + +class Database::DatabaseTransactionTask : public DatabaseTask { +public: + // Transaction task is never synchronous, so no 'synchronizer' parameter. + static PassOwnPtr<DatabaseTransactionTask> create(PassRefPtr<SQLTransaction> transaction) + { + return adoptPtr(new DatabaseTransactionTask(transaction)); + } + + SQLTransaction* transaction() const { return m_transaction.get(); } + +private: + DatabaseTransactionTask(PassRefPtr<SQLTransaction>); + + virtual void doPerformTask(); +#ifndef NDEBUG + virtual const char* debugTaskName() const; +#endif + + RefPtr<SQLTransaction> m_transaction; +}; + +class Database::DatabaseTableNamesTask : public DatabaseTask { +public: + static PassOwnPtr<DatabaseTableNamesTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names) + { + return adoptPtr(new DatabaseTableNamesTask(db, synchronizer, names)); + } + +private: + DatabaseTableNamesTask(Database*, DatabaseTaskSynchronizer*, Vector<String>& names); + + virtual void doPerformTask(); +#ifndef NDEBUG + virtual const char* debugTaskName() const; +#endif + + Vector<String>& m_tableNames; +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) +#endif // DatabaseTask_h diff --git a/Source/WebCore/storage/DatabaseThread.cpp b/Source/WebCore/storage/DatabaseThread.cpp new file mode 100644 index 0000000..3b790ee --- /dev/null +++ b/Source/WebCore/storage/DatabaseThread.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "DatabaseThread.h" + +#if ENABLE(DATABASE) + +#include "AutodrainedPool.h" +#include "Database.h" +#include "DatabaseTask.h" +#include "Logging.h" +#include "SQLTransactionClient.h" +#include "SQLTransactionCoordinator.h" +#include <wtf/UnusedParam.h> + +namespace WebCore { + +DatabaseThread::DatabaseThread() + : m_threadID(0) + , m_transactionClient(adoptPtr(new SQLTransactionClient())) + , m_transactionCoordinator(adoptPtr(new SQLTransactionCoordinator())) + , m_cleanupSync(0) +{ + m_selfRef = this; +} + +DatabaseThread::~DatabaseThread() +{ + // FIXME: Any cleanup required here? Since the thread deletes itself after running its detached course, I don't think so. Lets be sure. + ASSERT(terminationRequested()); +} + +bool DatabaseThread::start() +{ + MutexLocker lock(m_threadCreationMutex); + + if (m_threadID) + return true; + + m_threadID = createThread(DatabaseThread::databaseThreadStart, this, "WebCore: Database"); + + return m_threadID; +} + +void DatabaseThread::requestTermination(DatabaseTaskSynchronizer *cleanupSync) +{ + ASSERT(!m_cleanupSync); + m_cleanupSync = cleanupSync; + LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this); + m_queue.kill(); +} + +bool DatabaseThread::terminationRequested(DatabaseTaskSynchronizer* taskSynchronizer) const +{ +#ifndef NDEBUG + if (taskSynchronizer) + taskSynchronizer->setHasCheckedForTermination(); +#else + UNUSED_PARAM(taskSynchronizer); +#endif + + return m_queue.killed(); +} + +void* DatabaseThread::databaseThreadStart(void* vDatabaseThread) +{ + DatabaseThread* dbThread = static_cast<DatabaseThread*>(vDatabaseThread); + return dbThread->databaseThread(); +} + +void* DatabaseThread::databaseThread() +{ + { + // Wait for DatabaseThread::start() to complete. + MutexLocker lock(m_threadCreationMutex); + LOG(StorageAPI, "Started DatabaseThread %p", this); + } + + AutodrainedPool pool; + while (OwnPtr<DatabaseTask> task = m_queue.waitForMessage()) { + task->performTask(); + pool.cycle(); + } + + // Clean up the list of all pending transactions on this database thread + m_transactionCoordinator->shutdown(); + + LOG(StorageAPI, "About to detach thread %i and clear the ref to DatabaseThread %p, which currently has %i ref(s)", m_threadID, this, refCount()); + + // Close the databases that we ran transactions on. This ensures that if any transactions are still open, they are rolled back and we don't leave the database in an + // inconsistent or locked state. + if (m_openDatabaseSet.size() > 0) { + // As the call to close will modify the original set, we must take a copy to iterate over. + DatabaseSet openSetCopy; + openSetCopy.swap(m_openDatabaseSet); + DatabaseSet::iterator end = openSetCopy.end(); + for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it) + (*it)->close(); + } + + // Detach the thread so its resources are no longer of any concern to anyone else + detachThread(m_threadID); + + DatabaseTaskSynchronizer* cleanupSync = m_cleanupSync; + + // Clear the self refptr, possibly resulting in deletion + m_selfRef = 0; + + if (cleanupSync) // Someone wanted to know when we were done cleaning up. + cleanupSync->taskCompleted(); + + return 0; +} + +void DatabaseThread::recordDatabaseOpen(Database* database) +{ + ASSERT(currentThread() == m_threadID); + ASSERT(database); + ASSERT(!m_openDatabaseSet.contains(database)); + m_openDatabaseSet.add(database); +} + +void DatabaseThread::recordDatabaseClosed(Database* database) +{ + ASSERT(currentThread() == m_threadID); + ASSERT(database); + ASSERT(m_queue.killed() || m_openDatabaseSet.contains(database)); + m_openDatabaseSet.remove(database); +} + +void DatabaseThread::scheduleTask(PassOwnPtr<DatabaseTask> task) +{ + ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination()); + m_queue.append(task); +} + +void DatabaseThread::scheduleImmediateTask(PassOwnPtr<DatabaseTask> task) +{ + ASSERT(!task->hasSynchronizer() || task->hasCheckedForTermination()); + m_queue.prepend(task); +} + +class SameDatabasePredicate { +public: + SameDatabasePredicate(const Database* database) : m_database(database) { } + bool operator()(DatabaseTask* task) const { return task->database() == m_database; } +private: + const Database* m_database; +}; + +void DatabaseThread::unscheduleDatabaseTasks(Database* database) +{ + // Note that the thread loop is running, so some tasks for the database + // may still be executed. This is unavoidable. + SameDatabasePredicate predicate(database); + m_queue.removeIf(predicate); +} +} // namespace WebCore +#endif diff --git a/Source/WebCore/storage/DatabaseThread.h b/Source/WebCore/storage/DatabaseThread.h new file mode 100644 index 0000000..c81e376 --- /dev/null +++ b/Source/WebCore/storage/DatabaseThread.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef DatabaseThread_h +#define DatabaseThread_h + +#if ENABLE(DATABASE) +#include <wtf/Deque.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/MessageQueue.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + +class Database; +class DatabaseTask; +class DatabaseTaskSynchronizer; +class Document; +class SQLTransactionClient; +class SQLTransactionCoordinator; + +class DatabaseThread : public ThreadSafeShared<DatabaseThread> { +public: + static PassRefPtr<DatabaseThread> create() { return adoptRef(new DatabaseThread); } + ~DatabaseThread(); + + bool start(); + void requestTermination(DatabaseTaskSynchronizer* cleanupSync); + bool terminationRequested(DatabaseTaskSynchronizer* taskSynchronizer = 0) const; + + void scheduleTask(PassOwnPtr<DatabaseTask>); + void scheduleImmediateTask(PassOwnPtr<DatabaseTask>); // This just adds the task to the front of the queue - the caller needs to be extremely careful not to create deadlocks when waiting for completion. + void unscheduleDatabaseTasks(Database*); + + void recordDatabaseOpen(Database*); + void recordDatabaseClosed(Database*); + ThreadIdentifier getThreadID() { return m_threadID; } + + SQLTransactionClient* transactionClient() { return m_transactionClient.get(); } + SQLTransactionCoordinator* transactionCoordinator() { return m_transactionCoordinator.get(); } + +private: + DatabaseThread(); + + static void* databaseThreadStart(void*); + void* databaseThread(); + + Mutex m_threadCreationMutex; + ThreadIdentifier m_threadID; + RefPtr<DatabaseThread> m_selfRef; + + MessageQueue<DatabaseTask> m_queue; + + // This set keeps track of the open databases that have been used on this thread. + typedef HashSet<RefPtr<Database> > DatabaseSet; + DatabaseSet m_openDatabaseSet; + + OwnPtr<SQLTransactionClient> m_transactionClient; + OwnPtr<SQLTransactionCoordinator> m_transactionCoordinator; + DatabaseTaskSynchronizer* m_cleanupSync; +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) +#endif // DatabaseThread_h diff --git a/Source/WebCore/storage/DatabaseTracker.cpp b/Source/WebCore/storage/DatabaseTracker.cpp new file mode 100644 index 0000000..0b47842 --- /dev/null +++ b/Source/WebCore/storage/DatabaseTracker.cpp @@ -0,0 +1,1108 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "DatabaseTracker.h" + +#if ENABLE(DATABASE) + +#include "AbstractDatabase.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "DatabaseThread.h" +#include "DatabaseTrackerClient.h" +#include "Logging.h" +#include "OriginQuotaManager.h" +#include "Page.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include "SecurityOriginHash.h" +#include "SQLiteFileSystem.h" +#include "SQLiteStatement.h" +#include <wtf/MainThread.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/CString.h> + +using namespace std; + +static WebCore::OriginQuotaManager& originQuotaManager() +{ + DEFINE_STATIC_LOCAL(WebCore::OriginQuotaManager, quotaManager, ()); + return quotaManager; +} + +namespace WebCore { + +static DatabaseTracker* staticTracker = 0; + +void DatabaseTracker::initializeTracker(const String& databasePath) +{ + ASSERT(!staticTracker); + if (staticTracker) + return; + + staticTracker = new DatabaseTracker(databasePath); +} + +DatabaseTracker& DatabaseTracker::tracker() +{ + if (!staticTracker) + staticTracker = new DatabaseTracker(""); + + return *staticTracker; +} + +DatabaseTracker::DatabaseTracker(const String& databasePath) + : m_client(0) +{ + setDatabaseDirectoryPath(databasePath); + + SQLiteFileSystem::registerSQLiteVFS(); + + MutexLocker lockDatabase(m_databaseGuard); + populateOrigins(); +} + +void DatabaseTracker::setDatabaseDirectoryPath(const String& path) +{ + MutexLocker lockDatabase(m_databaseGuard); + ASSERT(!m_database.isOpen()); + m_databaseDirectoryPath = path.threadsafeCopy(); +} + +String DatabaseTracker::databaseDirectoryPath() const +{ + return m_databaseDirectoryPath.threadsafeCopy(); +} + +String DatabaseTracker::trackerDatabasePath() const +{ + return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath, "Databases.db"); +} + +void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist) +{ + ASSERT(!m_databaseGuard.tryLock()); + + if (m_database.isOpen()) + return; + + String databasePath = trackerDatabasePath(); + if (!SQLiteFileSystem::ensureDatabaseFileExists(databasePath, createIfDoesNotExist)) + return; + + if (!m_database.open(databasePath)) { + // FIXME: What do do here? + LOG_ERROR("Failed to open databasePath %s.", databasePath.ascii().data()); + return; + } + m_database.disableThreadingChecks(); + if (!m_database.tableExists("Origins")) { + if (!m_database.executeCommand("CREATE TABLE Origins (origin TEXT UNIQUE ON CONFLICT REPLACE, quota INTEGER NOT NULL ON CONFLICT FAIL);")) { + // FIXME: and here + LOG_ERROR("Failed to create Origins table"); + } + } + if (!m_database.tableExists("Databases")) { + if (!m_database.executeCommand("CREATE TABLE Databases (guid INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT, name TEXT, displayName TEXT, estimatedSize INTEGER, path TEXT);")) { + // FIXME: and here + LOG_ERROR("Failed to create Databases table"); + } + } +} + +bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext* context, const String& name, const String& displayName, unsigned long estimatedSize) +{ + SecurityOrigin* origin = context->securityOrigin(); + ProposedDatabase details; + + unsigned long long requirement; + { + MutexLocker lockDatabase(m_databaseGuard); + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + + if (!canCreateDatabase(origin, name)) + return false; + + recordCreatingDatabase(origin, name); + + // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker + // by fetching its current usage now. + unsigned long long usage = usageForOriginNoLock(origin); + + // If a database already exists, ignore the passed-in estimated size and say it's OK. + if (hasEntryForDatabase(origin, name)) + return true; + + // If the database will fit, allow its creation. + requirement = usage + max(1UL, estimatedSize); + if (requirement < usage) { + doneCreatingDatabase(origin, name); + return false; // If the estimated size is so big it causes an overflow, don't allow creation. + } + if (requirement <= quotaForOriginNoLock(origin)) + return true; + + // Give the chrome client a chance to increase the quota. + // Temporarily make the details of the proposed database available, so the client can get at them. + // FIXME: We should really just pass the details into this call, rather than using m_proposedDatabases. + details = ProposedDatabase(origin->threadsafeCopy(), DatabaseDetails(name.threadsafeCopy(), displayName.threadsafeCopy(), estimatedSize, 0)); + m_proposedDatabases.add(&details); + } + // Drop all locks before calling out; we don't know what they'll do. + context->databaseExceededQuota(name); + + MutexLocker lockDatabase(m_databaseGuard); + + m_proposedDatabases.remove(&details); + + // If the database will fit now, allow its creation. + if (requirement <= quotaForOriginNoLock(origin)) + return true; + + doneCreatingDatabase(origin, name); + + return false; +} + +bool DatabaseTracker::hasEntryForOriginNoLock(SecurityOrigin* origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(m_quotaMap); + return m_quotaMap->contains(origin); +} + +bool DatabaseTracker::hasEntryForOrigin(SecurityOrigin* origin) +{ + MutexLocker lockDatabase(m_databaseGuard); + return hasEntryForOriginNoLock(origin); +} + +bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& databaseIdentifier) +{ + ASSERT(!m_databaseGuard.tryLock()); + openTrackerDatabase(false); + if (!m_database.isOpen()) + return false; + SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?;"); + + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindText(1, origin->databaseIdentifier()); + statement.bindText(2, databaseIdentifier); + + return statement.step() == SQLResultRow; +} + +unsigned long long DatabaseTracker::getMaxSizeForDatabase(const AbstractDatabase* database) +{ + // The maximum size for a database is the full quota for its origin, minus the current usage within the origin, + // plus the current usage of the given database + MutexLocker lockDatabase(m_databaseGuard); + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + SecurityOrigin* origin = database->securityOrigin(); + return quotaForOriginNoLock(origin) - originQuotaManager().diskUsage(origin) + SQLiteFileSystem::getDatabaseFileSize(database->fileName()); +} + +void DatabaseTracker::databaseChanged(AbstractDatabase* database) +{ + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + originQuotaManager().markDatabase(database); +} + +void DatabaseTracker::interruptAllDatabasesForContext(const ScriptExecutionContext* context) +{ + Vector<RefPtr<AbstractDatabase> > openDatabases; + { + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + + if (!m_openDatabaseMap) + return; + + DatabaseNameMap* nameMap = m_openDatabaseMap->get(context->securityOrigin()); + if (!nameMap) + return; + + DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end(); + for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) { + DatabaseSet* databaseSet = dbNameMapIt->second; + DatabaseSet::const_iterator dbSetEndIt = databaseSet->end(); + for (DatabaseSet::const_iterator dbSetIt = databaseSet->begin(); dbSetIt != dbSetEndIt; ++dbSetIt) { + if ((*dbSetIt)->scriptExecutionContext() == context) + openDatabases.append(*dbSetIt); + } + } + } + + Vector<RefPtr<AbstractDatabase> >::const_iterator openDatabasesEndIt = openDatabases.end(); + for (Vector<RefPtr<AbstractDatabase> >::const_iterator openDatabasesIt = openDatabases.begin(); openDatabasesIt != openDatabasesEndIt; ++openDatabasesIt) + (*openDatabasesIt)->interrupt(); +} + +String DatabaseTracker::originPath(SecurityOrigin* origin) const +{ + return SQLiteFileSystem::appendDatabaseFileNameToPath(m_databaseDirectoryPath.threadsafeCopy(), origin->databaseIdentifier()); +} + +String DatabaseTracker::fullPathForDatabaseNoLock(SecurityOrigin* origin, const String& name, bool createIfNotExists) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(!originQuotaManager().tryLock()); + + for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter) + if ((*iter)->second.name() == name && (*iter)->first->equal(origin)) + return String(); + + String originIdentifier = origin->databaseIdentifier(); + String originPath = this->originPath(origin); + + // Make sure the path for this SecurityOrigin exists + if (createIfNotExists && !SQLiteFileSystem::ensureDatabaseDirectoryExists(originPath)) + return String(); + + // See if we have a path for this database yet + if (!m_database.isOpen()) + return String(); + SQLiteStatement statement(m_database, "SELECT path FROM Databases WHERE origin=? AND name=?;"); + + if (statement.prepare() != SQLResultOk) + return String(); + + statement.bindText(1, originIdentifier); + statement.bindText(2, name); + + int result = statement.step(); + + if (result == SQLResultRow) + return SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, statement.getColumnText(0)); + if (!createIfNotExists) + return String(); + + if (result != SQLResultDone) { + LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", originIdentifier.ascii().data(), name.ascii().data()); + return String(); + } + statement.finalize(); + + String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, originIdentifier, &m_database); + if (!addDatabase(origin, name, fileName)) + return String(); + + // If this origin's quota is being tracked (open handle to a database in this origin), add this new database + // to the quota manager now + String fullFilePath = SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, fileName); + if (originQuotaManager().tracksOrigin(origin)) + originQuotaManager().addDatabase(origin, name, fullFilePath); + + return fullFilePath; +} + +String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool createIfNotExists) +{ + MutexLocker lockDatabase(m_databaseGuard); + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + + return fullPathForDatabaseNoLock(origin, name, createIfNotExists).threadsafeCopy(); +} + +void DatabaseTracker::populateOrigins() +{ + ASSERT(!m_databaseGuard.tryLock()); + if (m_quotaMap) + return; + + m_quotaMap = adoptPtr(new QuotaMap); + + openTrackerDatabase(false); + if (!m_database.isOpen()) + return; + + SQLiteStatement statement(m_database, "SELECT origin, quota FROM Origins"); + + if (statement.prepare() != SQLResultOk) { + LOG_ERROR("Failed to prepare statement."); + return; + } + + int result; + while ((result = statement.step()) == SQLResultRow) { + RefPtr<SecurityOrigin> origin = SecurityOrigin::createFromDatabaseIdentifier(statement.getColumnText(0)); + m_quotaMap->set(origin.get()->threadsafeCopy(), statement.getColumnInt64(1)); + } + + if (result != SQLResultDone) + LOG_ERROR("Failed to read in all origins from the database."); +} + +void DatabaseTracker::origins(Vector<RefPtr<SecurityOrigin> >& result) +{ + MutexLocker lockDatabase(m_databaseGuard); + ASSERT(m_quotaMap); + copyKeysToVector(*m_quotaMap, result); +} + +bool DatabaseTracker::databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector) +{ + ASSERT(!m_databaseGuard.tryLock()); + openTrackerDatabase(false); + if (!m_database.isOpen()) + return false; + + SQLiteStatement statement(m_database, "SELECT name FROM Databases where origin=?;"); + + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindText(1, origin->databaseIdentifier()); + + int result; + while ((result = statement.step()) == SQLResultRow) + resultVector.append(statement.getColumnText(0)); + + if (result != SQLResultDone) { + LOG_ERROR("Failed to retrieve all database names for origin %s", origin->databaseIdentifier().ascii().data()); + return false; + } + + return true; +} + +bool DatabaseTracker::databaseNamesForOrigin(SecurityOrigin* origin, Vector<String>& resultVector) +{ + Vector<String> temp; + { + MutexLocker lockDatabase(m_databaseGuard); + if (!databaseNamesForOriginNoLock(origin, temp)) + return false; + } + + for (Vector<String>::iterator iter = temp.begin(); iter != temp.end(); ++iter) + resultVector.append(iter->threadsafeCopy()); + return true; +} + +DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, SecurityOrigin* origin) +{ + String originIdentifier = origin->databaseIdentifier(); + String displayName; + int64_t expectedUsage; + + { + MutexLocker lockDatabase(m_databaseGuard); + + for (HashSet<ProposedDatabase*>::iterator iter = m_proposedDatabases.begin(); iter != m_proposedDatabases.end(); ++iter) + if ((*iter)->second.name() == name && (*iter)->first->equal(origin)) { + ASSERT((*iter)->second.thread() == currentThread()); + return (*iter)->second; + } + + openTrackerDatabase(false); + if (!m_database.isOpen()) + return DatabaseDetails(); + SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?"); + if (statement.prepare() != SQLResultOk) + return DatabaseDetails(); + + statement.bindText(1, originIdentifier); + statement.bindText(2, name); + + int result = statement.step(); + if (result == SQLResultDone) + return DatabaseDetails(); + + if (result != SQLResultRow) { + LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data()); + return DatabaseDetails(); + } + displayName = statement.getColumnText(0); + expectedUsage = statement.getColumnInt64(1); + } + + return DatabaseDetails(name, displayName, expectedUsage, usageForDatabase(name, origin)); +} + +void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& name, const String& displayName, unsigned long estimatedSize) +{ + String originIdentifier = origin->databaseIdentifier(); + int64_t guid = 0; + + MutexLocker lockDatabase(m_databaseGuard); + + openTrackerDatabase(true); + if (!m_database.isOpen()) + return; + SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?"); + if (statement.prepare() != SQLResultOk) + return; + + statement.bindText(1, originIdentifier); + statement.bindText(2, name); + + int result = statement.step(); + if (result == SQLResultRow) + guid = statement.getColumnInt64(0); + statement.finalize(); + + if (guid == 0) { + if (result != SQLResultDone) + LOG_ERROR("Error to determing existence of database %s in origin %s in tracker database", name.ascii().data(), originIdentifier.ascii().data()); + else { + // This case should never occur - we should never be setting database details for a database that doesn't already exist in the tracker + // But since the tracker file is an external resource not under complete control of our code, it's somewhat invalid to make this an ASSERT case + // So we'll print an error instead + LOG_ERROR("Could not retrieve guid for database %s in origin %s from the tracker database - it is invalid to set database details on a database that doesn't already exist in the tracker", + name.ascii().data(), originIdentifier.ascii().data()); + } + return; + } + + SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?"); + if (updateStatement.prepare() != SQLResultOk) + return; + + updateStatement.bindText(1, displayName); + updateStatement.bindInt64(2, estimatedSize); + updateStatement.bindInt64(3, guid); + + if (updateStatement.step() != SQLResultDone) { + LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data()); + return; + } + + if (m_client) + m_client->dispatchDidModifyDatabase(origin, name); +} + +unsigned long long DatabaseTracker::usageForDatabase(const String& name, SecurityOrigin* origin) +{ + String path = fullPathForDatabase(origin, name, false); + if (path.isEmpty()) + return 0; + + return SQLiteFileSystem::getDatabaseFileSize(path); +} + +void DatabaseTracker::addOpenDatabase(AbstractDatabase* database) +{ + if (!database) + return; + + { + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + + if (!m_openDatabaseMap) + m_openDatabaseMap = adoptPtr(new DatabaseOriginMap); + + String name(database->stringIdentifier()); + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); + if (!nameMap) { + nameMap = new DatabaseNameMap; + m_openDatabaseMap->set(database->securityOrigin()->threadsafeCopy(), nameMap); + } + + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) { + databaseSet = new DatabaseSet; + nameMap->set(name.threadsafeCopy(), databaseSet); + } + + databaseSet->add(database); + + LOG(StorageAPI, "Added open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); + + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + if (!originQuotaManager().tracksOrigin(database->securityOrigin())) { + originQuotaManager().trackOrigin(database->securityOrigin()); + originQuotaManager().addDatabase(database->securityOrigin(), database->stringIdentifier(), database->fileName()); + } + } + + MutexLocker lockDatabase(m_databaseGuard); + doneCreatingDatabase(database->securityOrigin(), database->stringIdentifier()); +} + +void DatabaseTracker::removeOpenDatabase(AbstractDatabase* database) +{ + if (!database) + return; + + { + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + + if (!m_openDatabaseMap) { + ASSERT_NOT_REACHED(); + return; + } + + String name(database->stringIdentifier()); + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); + if (!nameMap) { + ASSERT_NOT_REACHED(); + return; + } + + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) { + ASSERT_NOT_REACHED(); + return; + } + + databaseSet->remove(database); + + LOG(StorageAPI, "Removed open Database %s (%p)\n", database->stringIdentifier().ascii().data(), database); + + if (!databaseSet->isEmpty()) + return; + + nameMap->remove(name); + delete databaseSet; + + if (!nameMap->isEmpty()) + return; + + m_openDatabaseMap->remove(database->securityOrigin()); + delete nameMap; + + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + originQuotaManager().removeOrigin(database->securityOrigin()); + } +} + +void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<AbstractDatabase> >* databases) +{ + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + if (!m_openDatabaseMap) + return; + + DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); + if (!nameMap) + return; + + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) + return; + + for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) + databases->add(*it); +} + +unsigned long long DatabaseTracker::usageForOriginNoLock(SecurityOrigin* origin) +{ + ASSERT(!originQuotaManager().tryLock()); + + // Use the OriginQuotaManager mechanism to calculate the usage + if (originQuotaManager().tracksOrigin(origin)) + return originQuotaManager().diskUsage(origin); + + // If the OriginQuotaManager doesn't track this origin already, prime it to do so + originQuotaManager().trackOrigin(origin); + + Vector<String> names; + databaseNamesForOriginNoLock(origin, names); + + for (unsigned i = 0; i < names.size(); ++i) + originQuotaManager().addDatabase(origin, names[i], fullPathForDatabaseNoLock(origin, names[i], false)); + + if (!originQuotaManager().tracksOrigin(origin)) + return 0; + return originQuotaManager().diskUsage(origin); +} + +unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin) +{ + MutexLocker lockDatabase(m_databaseGuard); + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + return usageForOriginNoLock(origin); +} + +unsigned long long DatabaseTracker::quotaForOriginNoLock(SecurityOrigin* origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(m_quotaMap); + return m_quotaMap->get(origin); +} + +unsigned long long DatabaseTracker::quotaForOrigin(SecurityOrigin* origin) +{ + MutexLocker lockDatabase(m_databaseGuard); + return quotaForOriginNoLock(origin); +} + +void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota) +{ + MutexLocker lockDatabase(m_databaseGuard); + + if (quotaForOriginNoLock(origin) == quota) + return; + + openTrackerDatabase(true); + if (!m_database.isOpen()) + return; + + if (!m_quotaMap->contains(origin)) { + SQLiteStatement statement(m_database, "INSERT INTO Origins VALUES (?, ?)"); + if (statement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); + } else { + statement.bindText(1, origin->databaseIdentifier()); + statement.bindInt64(2, quota); + + if (statement.step() != SQLResultDone) + LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); + } + } else { + SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); + bool error = statement.prepare() != SQLResultOk; + if (!error) { + statement.bindInt64(1, quota); + statement.bindText(2, origin->databaseIdentifier()); + + error = !statement.executeCommand(); + } + + if (error) +#if OS(WINDOWS) + LOG_ERROR("Failed to set quota %I64u in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data()); +#else + LOG_ERROR("Failed to set quota %llu in tracker database for origin %s", quota, origin->databaseIdentifier().ascii().data()); +#endif + } + + // FIXME: Is it really OK to update the quota in memory if we failed to update it on disk? + m_quotaMap->set(origin->threadsafeCopy(), quota); + + if (m_client) + m_client->dispatchDidModifyOrigin(origin); +} + +bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, const String& path) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(m_quotaMap); + openTrackerDatabase(true); + if (!m_database.isOpen()) + return false; + + // New database should never be added until the origin has been established + ASSERT(hasEntryForOriginNoLock(origin)); + + SQLiteStatement statement(m_database, "INSERT INTO Databases (origin, name, path) VALUES (?, ?, ?);"); + + if (statement.prepare() != SQLResultOk) + return false; + + statement.bindText(1, origin->databaseIdentifier()); + statement.bindText(2, name); + statement.bindText(3, path); + + if (!statement.executeCommand()) { + LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg()); + return false; + } + + if (m_client) + m_client->dispatchDidModifyOrigin(origin); + + return true; +} + +void DatabaseTracker::deleteAllDatabases() +{ + Vector<RefPtr<SecurityOrigin> > originsCopy; + origins(originsCopy); + + for (unsigned i = 0; i < originsCopy.size(); ++i) + deleteOrigin(originsCopy[i].get()); +} + +// It is the caller's responsibility to make sure that nobody is trying to create, delete, open, or close databases in this origin while the deletion is +// taking place. +bool DatabaseTracker::deleteOrigin(SecurityOrigin* origin) +{ + Vector<String> databaseNames; + { + MutexLocker lockDatabase(m_databaseGuard); + openTrackerDatabase(false); + if (!m_database.isOpen()) + return false; + + if (!databaseNamesForOriginNoLock(origin, databaseNames)) { + LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data()); + return false; + } + if (!canDeleteOrigin(origin)) { + LOG_ERROR("Tried to delete an origin (%s) while either creating database in it or already deleting it", origin->databaseIdentifier().ascii().data()); + ASSERT(false); + return false; + } + recordDeletingOrigin(origin); + } + + // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock. + for (unsigned i = 0; i < databaseNames.size(); ++i) { + if (!deleteDatabaseFile(origin, databaseNames[i])) { + // Even if the file can't be deleted, we want to try and delete the rest, don't return early here. + LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data()); + } + } + + { + MutexLocker lockDatabase(m_databaseGuard); + doneDeletingOrigin(origin); + + SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?"); + if (statement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); + return false; + } + + statement.bindText(1, origin->databaseIdentifier()); + + if (!statement.executeCommand()) { + LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); + return false; + } + + SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?"); + if (originStatement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data()); + return false; + } + + originStatement.bindText(1, origin->databaseIdentifier()); + + if (!originStatement.executeCommand()) { + LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); + return false; + } + + SQLiteFileSystem::deleteEmptyDatabaseDirectory(originPath(origin)); + + RefPtr<SecurityOrigin> originPossiblyLastReference = origin; + m_quotaMap->remove(origin); + + { + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + originQuotaManager().removeOrigin(origin); + } + + // If we removed the last origin, do some additional deletion. + if (m_quotaMap->isEmpty()) { + if (m_database.isOpen()) + m_database.close(); + SQLiteFileSystem::deleteDatabaseFile(trackerDatabasePath()); + SQLiteFileSystem::deleteEmptyDatabaseDirectory(m_databaseDirectoryPath); + } + + if (m_client) { + m_client->dispatchDidModifyOrigin(origin); + for (unsigned i = 0; i < databaseNames.size(); ++i) + m_client->dispatchDidModifyDatabase(origin, databaseNames[i]); + } + } + return true; +} + +bool DatabaseTracker::canCreateDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + // Can't create a database while someone else is deleting it; there's a risk of leaving untracked database debris on the disk. + return !deletingDatabase(origin, name) && !deletingOrigin(origin); +} + +void DatabaseTracker::recordCreatingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameCountMap* nameMap = m_beingCreated.get(origin); + if (!nameMap) { + nameMap = new NameCountMap(); + m_beingCreated.set(origin->threadsafeCopy(), nameMap); + } + long count = nameMap->get(name); + nameMap->set(name.threadsafeCopy(), count + 1); +} + +void DatabaseTracker::doneCreatingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameCountMap* nameMap = m_beingCreated.get(origin); + if (nameMap) { + long count = nameMap->get(name); + ASSERT(count > 0); + if (count <= 1) { + nameMap->remove(name); + if (nameMap->isEmpty()) { + m_beingCreated.remove(origin); + delete nameMap; + } + } else + nameMap->set(name, count - 1); + } else + ASSERT(false); +} + +bool DatabaseTracker::creatingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameCountMap* nameMap = m_beingCreated.get(origin); + return nameMap && nameMap->get(name); +} + +bool DatabaseTracker::canDeleteDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + return !creatingDatabase(origin, name) && !deletingDatabase(origin, name); +} + +void DatabaseTracker::recordDeletingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(canDeleteDatabase(origin, name)); + NameSet* nameSet = m_beingDeleted.get(origin); + if (!nameSet) { + nameSet = new NameSet(); + m_beingDeleted.set(origin->threadsafeCopy(), nameSet); + } + ASSERT(!nameSet->contains(name)); + nameSet->add(name.threadsafeCopy()); +} + +void DatabaseTracker::doneDeletingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameSet* nameSet = m_beingDeleted.get(origin); + if (nameSet) { + ASSERT(nameSet->contains(name)); + nameSet->remove(name); + if (nameSet->isEmpty()) { + m_beingDeleted.remove(origin); + delete nameSet; + } + } else { + ASSERT(false); + } +} + +bool DatabaseTracker::deletingDatabase(SecurityOrigin *origin, const String& name) +{ + ASSERT(!m_databaseGuard.tryLock()); + NameSet* nameSet = m_beingDeleted.get(origin); + return nameSet && nameSet->contains(name); +} + +bool DatabaseTracker::canDeleteOrigin(SecurityOrigin *origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + return !(deletingOrigin(origin) || m_beingCreated.get(origin)); +} + +bool DatabaseTracker::deletingOrigin(SecurityOrigin *origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + return m_originsBeingDeleted.contains(origin); +} + +void DatabaseTracker::recordDeletingOrigin(SecurityOrigin *origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(!deletingOrigin(origin)); + m_originsBeingDeleted.add(origin->threadsafeCopy()); +} + +void DatabaseTracker::doneDeletingOrigin(SecurityOrigin *origin) +{ + ASSERT(!m_databaseGuard.tryLock()); + ASSERT(deletingOrigin(origin)); + m_originsBeingDeleted.remove(origin); +} + +bool DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name) +{ + { + MutexLocker lockDatabase(m_databaseGuard); + openTrackerDatabase(false); + if (!m_database.isOpen()) + return false; + + if (!canDeleteDatabase(origin, name)) { + ASSERT(FALSE); + return false; + } + recordDeletingDatabase(origin, name); + } + + // We drop the lock here because holding locks during a call to deleteDatabaseFile will deadlock. + if (!deleteDatabaseFile(origin, name)) { + LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data()); + MutexLocker lockDatabase(m_databaseGuard); + doneDeletingDatabase(origin, name); + return false; + } + + MutexLocker lockDatabase(m_databaseGuard); + + SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?"); + if (statement.prepare() != SQLResultOk) { + LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); + doneDeletingDatabase(origin, name); + return false; + } + + statement.bindText(1, origin->databaseIdentifier()); + statement.bindText(2, name); + + if (!statement.executeCommand()) { + LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); + doneDeletingDatabase(origin, name); + return false; + } + + { + Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); + originQuotaManager().removeDatabase(origin, name); + } + + if (m_client) { + m_client->dispatchDidModifyOrigin(origin); + m_client->dispatchDidModifyDatabase(origin, name); + } + doneDeletingDatabase(origin, name); + + return true; +} + +// deleteDatabaseFile has to release locks between looking up the list of databases to close and closing them. While this is in progress, the caller +// is responsible for making sure no new databases are opened in the file to be deleted. +bool DatabaseTracker::deleteDatabaseFile(SecurityOrigin* origin, const String& name) +{ + String fullPath = fullPathForDatabase(origin, name, false); + if (fullPath.isEmpty()) + return true; + +#ifndef NDEBUG + { + MutexLocker lockDatabase(m_databaseGuard); + ASSERT(deletingDatabase(origin, name) || deletingOrigin(origin)); + } +#endif + + Vector<RefPtr<AbstractDatabase> > deletedDatabases; + + // Make sure not to hold the any locks when calling + // Database::markAsDeletedAndClose(), since that can cause a deadlock + // during the synchronous DatabaseThread call it triggers. + { + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + if (m_openDatabaseMap) { + // There are some open databases, lets check if they are for this origin. + DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); + if (nameMap && nameMap->size()) { + // There are some open databases for this origin, let's check + // if they are this database by name. + DatabaseSet* databaseSet = nameMap->get(name); + if (databaseSet && databaseSet->size()) { + // We have some database open with this name. Mark them as deleted. + DatabaseSet::const_iterator end = databaseSet->end(); + for (DatabaseSet::const_iterator it = databaseSet->begin(); it != end; ++it) + deletedDatabases.append(*it); + } + } + } + } + + for (unsigned i = 0; i < deletedDatabases.size(); ++i) + deletedDatabases[i]->markAsDeletedAndClose(); + + return SQLiteFileSystem::deleteDatabaseFile(fullPath); +} + +void DatabaseTracker::setClient(DatabaseTrackerClient* client) +{ + m_client = client; +} + +static Mutex& notificationMutex() +{ + DEFINE_STATIC_LOCAL(Mutex, mutex, ()); + return mutex; +} + +typedef Vector<pair<RefPtr<SecurityOrigin>, String> > NotificationQueue; + +static NotificationQueue& notificationQueue() +{ + DEFINE_STATIC_LOCAL(NotificationQueue, queue, ()); + return queue; +} + +void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, const String& name) +{ + MutexLocker locker(notificationMutex()); + + notificationQueue().append(pair<RefPtr<SecurityOrigin>, String>(origin->threadsafeCopy(), name.crossThreadString())); + scheduleForNotification(); +} + +static bool notificationScheduled = false; + +void DatabaseTracker::scheduleForNotification() +{ + ASSERT(!notificationMutex().tryLock()); + + if (!notificationScheduled) { + callOnMainThread(DatabaseTracker::notifyDatabasesChanged, 0); + notificationScheduled = true; + } +} + +void DatabaseTracker::notifyDatabasesChanged(void*) +{ + // Note that if DatabaseTracker ever becomes non-singleton, we'll have to amend this notification + // mechanism to include which tracker the notification goes out on as well. + DatabaseTracker& theTracker(tracker()); + + NotificationQueue notifications; + { + MutexLocker locker(notificationMutex()); + + notifications.swap(notificationQueue()); + + notificationScheduled = false; + } + + if (!theTracker.m_client) + return; + + for (unsigned i = 0; i < notifications.size(); ++i) + theTracker.m_client->dispatchDidModifyDatabase(notifications[i].first.get(), notifications[i].second); +} + + +} // namespace WebCore +#endif diff --git a/Source/WebCore/storage/DatabaseTracker.h b/Source/WebCore/storage/DatabaseTracker.h new file mode 100644 index 0000000..7145e6b --- /dev/null +++ b/Source/WebCore/storage/DatabaseTracker.h @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef DatabaseTracker_h +#define DatabaseTracker_h + +#if ENABLE(DATABASE) + +#include "PlatformString.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/text/StringHash.h> + +#if !PLATFORM(CHROMIUM) +#include "DatabaseDetails.h" +#include "SQLiteDatabase.h" +#include <wtf/OwnPtr.h> +#endif // !PLATFORM(CHROMIUM) + +namespace WebCore { + +class AbstractDatabase; +class ScriptExecutionContext; +class SecurityOrigin; + +struct SecurityOriginHash; + +#if !PLATFORM(CHROMIUM) +class DatabaseTrackerClient; + +struct SecurityOriginTraits; +#endif // !PLATFORM(CHROMIUM) + +class DatabaseTracker : public Noncopyable { +public: + static void initializeTracker(const String& databasePath); + static DatabaseTracker& tracker(); + // This singleton will potentially be used from multiple worker threads and the page's context thread simultaneously. To keep this safe, it's + // currently using 4 locks. In order to avoid deadlock when taking multiple locks, you must take them in the correct order: + // m_databaseGuard before quotaManager if both locks are needed. + // m_openDatabaseMapGuard before quotaManager if both locks are needed. + // m_databaseGuard and m_openDatabaseMapGuard currently don't overlap. + // notificationMutex() is currently independent of the other locks. + + bool canEstablishDatabase(ScriptExecutionContext*, const String& name, const String& displayName, unsigned long estimatedSize); + void setDatabaseDetails(SecurityOrigin*, const String& name, const String& displayName, unsigned long estimatedSize); + String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true); + + void addOpenDatabase(AbstractDatabase*); + void removeOpenDatabase(AbstractDatabase*); + void getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<AbstractDatabase> >* databases); + + unsigned long long getMaxSizeForDatabase(const AbstractDatabase*); + void databaseChanged(AbstractDatabase*); + + void interruptAllDatabasesForContext(const ScriptExecutionContext*); + +private: + DatabaseTracker(const String& databasePath); + + typedef HashSet<AbstractDatabase*> DatabaseSet; + typedef HashMap<String, DatabaseSet*> DatabaseNameMap; + typedef HashMap<RefPtr<SecurityOrigin>, DatabaseNameMap*, SecurityOriginHash> DatabaseOriginMap; + + Mutex m_openDatabaseMapGuard; + mutable OwnPtr<DatabaseOriginMap> m_openDatabaseMap; + +#if !PLATFORM(CHROMIUM) +public: + void setDatabaseDirectoryPath(const String&); + String databaseDirectoryPath() const; + + void origins(Vector<RefPtr<SecurityOrigin> >& result); + bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result); + + DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*); + + unsigned long long usageForDatabase(const String&, SecurityOrigin*); + unsigned long long usageForOrigin(SecurityOrigin*); + unsigned long long quotaForOrigin(SecurityOrigin*); + void setQuota(SecurityOrigin*, unsigned long long); + + void deleteAllDatabases(); + bool deleteOrigin(SecurityOrigin*); + bool deleteDatabase(SecurityOrigin*, const String& name); + + void setClient(DatabaseTrackerClient*); + + // From a secondary thread, must be thread safe with its data + void scheduleNotifyDatabaseChanged(SecurityOrigin*, const String& name); + + bool hasEntryForOrigin(SecurityOrigin*); + +private: + bool hasEntryForOriginNoLock(SecurityOrigin* origin); + String fullPathForDatabaseNoLock(SecurityOrigin*, const String& name, bool createIfDoesNotExist); + bool databaseNamesForOriginNoLock(SecurityOrigin* origin, Vector<String>& resultVector); + unsigned long long usageForOriginNoLock(SecurityOrigin* origin); + unsigned long long quotaForOriginNoLock(SecurityOrigin* origin); + + String trackerDatabasePath() const; + void openTrackerDatabase(bool createIfDoesNotExist); + + String originPath(SecurityOrigin*) const; + + bool hasEntryForDatabase(SecurityOrigin*, const String& databaseIdentifier); + + bool addDatabase(SecurityOrigin*, const String& name, const String& path); + void populateOrigins(); + + bool deleteDatabaseFile(SecurityOrigin*, const String& name); + + // This lock protects m_database, m_quotaMap, m_proposedDatabases, m_databaseDirectoryPath, m_originsBeingDeleted, m_beingCreated, and m_beingDeleted. + Mutex m_databaseGuard; + SQLiteDatabase m_database; + + typedef HashMap<RefPtr<SecurityOrigin>, unsigned long long, SecurityOriginHash> QuotaMap; + mutable OwnPtr<QuotaMap> m_quotaMap; + + String m_databaseDirectoryPath; + + DatabaseTrackerClient* m_client; + + typedef std::pair<RefPtr<SecurityOrigin>, DatabaseDetails> ProposedDatabase; + HashSet<ProposedDatabase*> m_proposedDatabases; + + typedef HashMap<String, long> NameCountMap; + typedef HashMap<RefPtr<SecurityOrigin>, NameCountMap*, SecurityOriginHash> CreateSet; + CreateSet m_beingCreated; + typedef HashSet<String> NameSet; + HashMap<RefPtr<SecurityOrigin>, NameSet*, SecurityOriginHash> m_beingDeleted; + HashSet<RefPtr<SecurityOrigin>, SecurityOriginHash> m_originsBeingDeleted; + bool canCreateDatabase(SecurityOrigin *origin, const String& name); + void recordCreatingDatabase(SecurityOrigin *origin, const String& name); + void doneCreatingDatabase(SecurityOrigin *origin, const String& name); + bool creatingDatabase(SecurityOrigin *origin, const String& name); + bool canDeleteDatabase(SecurityOrigin *origin, const String& name); + void recordDeletingDatabase(SecurityOrigin *origin, const String& name); + void doneDeletingDatabase(SecurityOrigin *origin, const String& name); + bool deletingDatabase(SecurityOrigin *origin, const String& name); + bool canDeleteOrigin(SecurityOrigin *origin); + bool deletingOrigin(SecurityOrigin *origin); + void recordDeletingOrigin(SecurityOrigin *origin); + void doneDeletingOrigin(SecurityOrigin *origin); + + static void scheduleForNotification(); + static void notifyDatabasesChanged(void*); +#endif // !PLATFORM(CHROMIUM) +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) +#endif // DatabaseTracker_h diff --git a/Source/WebCore/storage/DatabaseTrackerClient.h b/Source/WebCore/storage/DatabaseTrackerClient.h new file mode 100644 index 0000000..2e0497f --- /dev/null +++ b/Source/WebCore/storage/DatabaseTrackerClient.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef DatabaseTrackerClient_h +#define DatabaseTrackerClient_h + +#if ENABLE(DATABASE) + +#include <wtf/Forward.h> + +namespace WebCore { + +class SecurityOrigin; + +class DatabaseTrackerClient { +public: + virtual ~DatabaseTrackerClient() { } + virtual void dispatchDidModifyOrigin(SecurityOrigin*) = 0; + virtual void dispatchDidModifyDatabase(SecurityOrigin*, const String& databaseName) = 0; +}; + +} // namespace WebCore + +#endif + +#endif // DatabaseTrackerClient_h diff --git a/Source/WebCore/storage/IDBAbortEvent.cpp b/Source/WebCore/storage/IDBAbortEvent.cpp new file mode 100644 index 0000000..21760f8 --- /dev/null +++ b/Source/WebCore/storage/IDBAbortEvent.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBAbortEvent.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "EventNames.h" +#include "IDBAny.h" + +namespace WebCore { + +PassRefPtr<IDBAbortEvent> IDBAbortEvent::create() +{ + return adoptRef(new IDBAbortEvent()); +} + +IDBAbortEvent::IDBAbortEvent() + : IDBEvent(eventNames().abortEvent, 0) // FIXME: set the source to the transaction +{ +} + +IDBAbortEvent::~IDBAbortEvent() +{ +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBAbortEvent.h b/Source/WebCore/storage/IDBAbortEvent.h new file mode 100644 index 0000000..bdc2202 --- /dev/null +++ b/Source/WebCore/storage/IDBAbortEvent.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBAbortEvent_h +#define IDBAbortEvent_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBEvent.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBAbortEvent : public IDBEvent { +public: + static PassRefPtr<IDBAbortEvent> create(); + // FIXME: Need to allow creation of these events from JS. + virtual ~IDBAbortEvent(); + + virtual bool isIDBAbortEvent() const { return true; } + +private: + IDBAbortEvent(); +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBAbortEvent_h diff --git a/Source/WebCore/storage/IDBAny.cpp b/Source/WebCore/storage/IDBAny.cpp new file mode 100644 index 0000000..3a049c0 --- /dev/null +++ b/Source/WebCore/storage/IDBAny.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBAny.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursor.h" +#include "IDBDatabase.h" +#include "IDBFactory.h" +#include "IDBIndex.h" +#include "IDBObjectStore.h" +#include "SerializedScriptValue.h" + +namespace WebCore { + +PassRefPtr<IDBAny> IDBAny::createInvalid() +{ + return adoptRef(new IDBAny()); +} + +PassRefPtr<IDBAny> IDBAny::createNull() +{ + RefPtr<IDBAny> idbAny = adoptRef(new IDBAny()); + idbAny->setNull(); + return idbAny.release(); +} + +IDBAny::IDBAny() + : m_type(UndefinedType) +{ +} + +IDBAny::~IDBAny() +{ +} + +PassRefPtr<IDBCursor> IDBAny::idbCursor() +{ + ASSERT(m_type == IDBCursorType); + return m_idbCursor; +} + +PassRefPtr<IDBDatabase> IDBAny::idbDatabase() +{ + ASSERT(m_type == IDBDatabaseType); + return m_idbDatabase; +} + +PassRefPtr<IDBFactory> IDBAny::idbFactory() +{ + ASSERT(m_type == IDBFactoryType); + return m_idbFactory; +} + +PassRefPtr<IDBIndex> IDBAny::idbIndex() +{ + ASSERT(m_type == IDBIndexType); + return m_idbIndex; +} + +PassRefPtr<IDBKey> IDBAny::idbKey() +{ + ASSERT(m_type == IDBKeyType); + return m_idbKey; +} + +PassRefPtr<IDBObjectStore> IDBAny::idbObjectStore() +{ + ASSERT(m_type == IDBObjectStoreType); + return m_idbObjectStore; +} + +PassRefPtr<IDBTransaction> IDBAny::idbTransaction() +{ + ASSERT(m_type == IDBTransactionType); + return m_idbTransaction; +} + +PassRefPtr<SerializedScriptValue> IDBAny::serializedScriptValue() +{ + ASSERT(m_type == SerializedScriptValueType); + return m_serializedScriptValue; +} + +void IDBAny::setNull() +{ + ASSERT(m_type == UndefinedType); + m_type = NullType; +} + +void IDBAny::set(PassRefPtr<IDBCursor> value) +{ + ASSERT(m_type == UndefinedType); + m_type = IDBCursorType; + m_idbCursor = value; +} + +void IDBAny::set(PassRefPtr<IDBDatabase> value) +{ + ASSERT(m_type == UndefinedType); + m_type = IDBDatabaseType; + m_idbDatabase = value; +} + +void IDBAny::set(PassRefPtr<IDBFactory> value) +{ + ASSERT(m_type == UndefinedType); + m_type = IDBFactoryType; + m_idbFactory = value; +} + +void IDBAny::set(PassRefPtr<IDBIndex> value) +{ + ASSERT(m_type == UndefinedType); + m_type = IDBIndexType; + m_idbIndex = value; +} + +void IDBAny::set(PassRefPtr<IDBKey> value) +{ + ASSERT(m_type == UndefinedType); + m_type = IDBKeyType; + m_idbKey = value; +} + +void IDBAny::set(PassRefPtr<IDBTransaction> value) +{ + ASSERT(m_type == UndefinedType); + m_type = IDBTransactionType; + m_idbTransaction = value; +} + +void IDBAny::set(PassRefPtr<IDBObjectStore> value) +{ + ASSERT(m_type == UndefinedType); + m_type = IDBObjectStoreType; + m_idbObjectStore = value; +} + +void IDBAny::set(PassRefPtr<SerializedScriptValue> value) +{ + ASSERT(m_type == UndefinedType); + m_type = SerializedScriptValueType; + m_serializedScriptValue = value; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBAny.h b/Source/WebCore/storage/IDBAny.h new file mode 100644 index 0000000..8e3f83e --- /dev/null +++ b/Source/WebCore/storage/IDBAny.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBAny_h +#define IDBAny_h + +#if ENABLE(INDEXED_DATABASE) + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBCursor; +class IDBDatabase; +class IDBFactory; +class IDBIndex; +class IDBKey; +class IDBObjectStore; +class IDBTransaction; +class SerializedScriptValue; + +class IDBAny : public RefCounted<IDBAny> { +public: + static PassRefPtr<IDBAny> createInvalid(); + static PassRefPtr<IDBAny> createNull(); + template<typename T> + static PassRefPtr<IDBAny> create(T* idbObject) + { + RefPtr<IDBAny> any = IDBAny::createInvalid(); + any->set(idbObject); + return any.release(); + } + template<typename T> + static PassRefPtr<IDBAny> create(PassRefPtr<T> idbObject) + { + RefPtr<IDBAny> any = IDBAny::createInvalid(); + any->set(idbObject); + return any.release(); + } + ~IDBAny(); + + enum Type { + UndefinedType = 0, + NullType, + IDBCursorType, + IDBDatabaseType, + IDBFactoryType, + IDBIndexType, + IDBKeyType, + IDBObjectStoreType, + IDBTransactionType, + SerializedScriptValueType + }; + + Type type() const { return m_type; } + // Use type() to figure out which one of these you're allowed to call. + PassRefPtr<IDBCursor> idbCursor(); + PassRefPtr<IDBDatabase> idbDatabase(); + PassRefPtr<IDBFactory> idbFactory(); + PassRefPtr<IDBIndex> idbIndex(); + PassRefPtr<IDBKey> idbKey(); + PassRefPtr<IDBObjectStore> idbObjectStore(); + PassRefPtr<IDBTransaction> idbTransaction(); + PassRefPtr<SerializedScriptValue> serializedScriptValue(); + + // Set can only be called once. + void setNull(); + void set(PassRefPtr<IDBCursor>); + void set(PassRefPtr<IDBDatabase>); + void set(PassRefPtr<IDBFactory>); + void set(PassRefPtr<IDBIndex>); + void set(PassRefPtr<IDBKey>); + void set(PassRefPtr<IDBObjectStore>); + void set(PassRefPtr<IDBTransaction>); + void set(PassRefPtr<SerializedScriptValue>); + +private: + IDBAny(); + + Type m_type; + + // Only one of the following should ever be in use at any given time. + RefPtr<IDBCursor> m_idbCursor; + RefPtr<IDBDatabase> m_idbDatabase; + RefPtr<IDBFactory> m_idbFactory; + RefPtr<IDBIndex> m_idbIndex; + RefPtr<IDBKey> m_idbKey; + RefPtr<IDBObjectStore> m_idbObjectStore; + RefPtr<IDBTransaction> m_idbTransaction; + RefPtr<SerializedScriptValue> m_serializedScriptValue; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBAny_h diff --git a/Source/WebCore/storage/IDBAny.idl b/Source/WebCore/storage/IDBAny.idl new file mode 100644 index 0000000..19d8424 --- /dev/null +++ b/Source/WebCore/storage/IDBAny.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE, + CustomToJS + ] IDBAny { + // This space is intentionally left blank. + }; +} diff --git a/Source/WebCore/storage/IDBCallbacks.h b/Source/WebCore/storage/IDBCallbacks.h new file mode 100644 index 0000000..e62c085 --- /dev/null +++ b/Source/WebCore/storage/IDBCallbacks.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBCallbacks_h +#define IDBCallbacks_h + +#include "IDBCursorBackendInterface.h" +#include "IDBDatabaseBackendInterface.h" +#include "IDBDatabaseError.h" +#include "IDBIndexBackendInterface.h" +#include "IDBKey.h" +#include "IDBObjectStoreBackendInterface.h" +#include "IDBTransactionBackendInterface.h" +#include "SerializedScriptValue.h" +#include <wtf/Threading.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +// FIXME: All child classes need to be made threadsafe. +class IDBCallbacks : public ThreadSafeShared<IDBCallbacks> { +public: + virtual ~IDBCallbacks() { } + + virtual void onError(PassRefPtr<IDBDatabaseError>) = 0; + virtual void onSuccess() = 0; // For "null". + virtual void onSuccess(PassRefPtr<IDBCursorBackendInterface>) = 0; + virtual void onSuccess(PassRefPtr<IDBDatabaseBackendInterface>) = 0; + virtual void onSuccess(PassRefPtr<IDBIndexBackendInterface>) = 0; + virtual void onSuccess(PassRefPtr<IDBKey>) = 0; + virtual void onSuccess(PassRefPtr<IDBObjectStoreBackendInterface>) = 0; + virtual void onSuccess(PassRefPtr<IDBTransactionBackendInterface>) = 0; + virtual void onSuccess(PassRefPtr<SerializedScriptValue>) = 0; +}; + +} // namespace WebCore + +#endif + +#endif // IDBCallbacks_h diff --git a/Source/WebCore/storage/IDBCompleteEvent.cpp b/Source/WebCore/storage/IDBCompleteEvent.cpp new file mode 100644 index 0000000..f0ad9fc --- /dev/null +++ b/Source/WebCore/storage/IDBCompleteEvent.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBCompleteEvent.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "EventNames.h" +#include "IDBAny.h" + +namespace WebCore { + +PassRefPtr<IDBCompleteEvent> IDBCompleteEvent::create() +{ + return adoptRef(new IDBCompleteEvent()); +} + +IDBCompleteEvent::IDBCompleteEvent() + : IDBEvent(eventNames().completeEvent, 0) // FIXME: set the source to the transaction +{ +} + +IDBCompleteEvent::~IDBCompleteEvent() +{ +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBCompleteEvent.h b/Source/WebCore/storage/IDBCompleteEvent.h new file mode 100644 index 0000000..c407096 --- /dev/null +++ b/Source/WebCore/storage/IDBCompleteEvent.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBCompleteEvent_h +#define IDBCompleteEvent_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBEvent.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBCompleteEvent : public IDBEvent { +public: + static PassRefPtr<IDBCompleteEvent> create(); + // FIXME: Need to allow creation of these events from JS. + virtual ~IDBCompleteEvent(); + + virtual bool isIDBCompleteEvent() const { return true; } + +private: + IDBCompleteEvent(); +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBCompleteEvent_h diff --git a/Source/WebCore/storage/IDBCursor.cpp b/Source/WebCore/storage/IDBCursor.cpp new file mode 100644 index 0000000..444c109 --- /dev/null +++ b/Source/WebCore/storage/IDBCursor.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBCursor.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBAny.h" +#include "IDBCallbacks.h" +#include "IDBCursorBackendInterface.h" +#include "IDBKey.h" +#include "IDBRequest.h" +#include "IDBTransactionBackendInterface.h" +#include "ScriptExecutionContext.h" +#include "SerializedScriptValue.h" + +namespace WebCore { + +IDBCursor::IDBCursor(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransactionBackendInterface* transaction) + : m_backend(backend) + , m_request(request) + , m_transaction(transaction) +{ + ASSERT(m_backend); + ASSERT(m_request); + ASSERT(m_transaction); +} + +IDBCursor::~IDBCursor() +{ +} + +unsigned short IDBCursor::direction() const +{ + return m_backend->direction(); +} + +PassRefPtr<IDBKey> IDBCursor::key() const +{ + return m_backend->key(); +} + +PassRefPtr<IDBAny> IDBCursor::value() const +{ + return m_backend->value(); +} + +PassRefPtr<IDBRequest> IDBCursor::update(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_backend->update(value, request, ec); + if (ec) + return 0; + return request.release(); +} + +void IDBCursor::continueFunction(PassRefPtr<IDBKey> key, ExceptionCode& ec) +{ + // FIXME: We're not using the context from when continue was called, which means the callback + // will be on the original context openCursor was called on. Is this right? + if (m_request->resetReadyState(m_transaction.get())) + m_backend->continueFunction(key, m_request, ec); + else + ASSERT_NOT_REACHED(); +} + +PassRefPtr<IDBRequest> IDBCursor::deleteFunction(ScriptExecutionContext* context, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_backend->deleteFunction(request, ec); + if (ec) + return 0; + return request.release(); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBCursor.h b/Source/WebCore/storage/IDBCursor.h new file mode 100644 index 0000000..54bf51a --- /dev/null +++ b/Source/WebCore/storage/IDBCursor.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBCursor_h +#define IDBCursor_h + +#if ENABLE(INDEXED_DATABASE) + +#include "ExceptionCode.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBAny; +class IDBCallbacks; +class IDBCursorBackendInterface; +class IDBKey; +class IDBRequest; +class ScriptExecutionContext; +class SerializedScriptValue; +class IDBTransactionBackendInterface; + +class IDBCursor : public RefCounted<IDBCursor> { +public: + enum Direction { + NEXT = 0, + NEXT_NO_DUPLICATE = 1, + PREV = 2, + PREV_NO_DUPLICATE = 3, + }; + static PassRefPtr<IDBCursor> create(PassRefPtr<IDBCursorBackendInterface> backend, IDBRequest* request, IDBTransactionBackendInterface* transaction) + { + return adoptRef(new IDBCursor(backend, request, transaction)); + } + ~IDBCursor(); + + // FIXME: Try to modify the code generator so this is unneeded. + void continueFunction(ExceptionCode& ec) { continueFunction(0, ec); } + + // Implement the IDL + unsigned short direction() const; + PassRefPtr<IDBKey> key() const; + PassRefPtr<IDBAny> value() const; + PassRefPtr<IDBRequest> update(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>, ExceptionCode&); + void continueFunction(PassRefPtr<IDBKey>, ExceptionCode&); + PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, ExceptionCode&); + +private: + explicit IDBCursor(PassRefPtr<IDBCursorBackendInterface>, IDBRequest*, IDBTransactionBackendInterface*); + + RefPtr<IDBCursorBackendInterface> m_backend; + RefPtr<IDBRequest> m_request; + RefPtr<IDBTransactionBackendInterface> m_transaction; +}; + +} // namespace WebCore + +#endif + +#endif // IDBCursor_h diff --git a/Source/WebCore/storage/IDBCursor.idl b/Source/WebCore/storage/IDBCursor.idl new file mode 100644 index 0000000..12d0baf --- /dev/null +++ b/Source/WebCore/storage/IDBCursor.idl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBCursor { + const unsigned short NEXT = 0; + const unsigned short NEXT_NO_DUPLICATE = 1; + const unsigned short PREV = 2; + const unsigned short PREV_NO_DUPLICATE = 3; + + readonly attribute unsigned short direction; + readonly attribute IDBKey key; + readonly attribute IDBAny value; + + // FIXME: Implement. + //[CallWith=ScriptExecutionContext] IDBRequest update(in SerializedScriptValue value) + // raises (IDBDatabaseException); + [ImplementationFunction=continueFunction] void continue(in [Optional] IDBKey key) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext, ImplementationFunction=deleteFunction] IDBRequest delete() + raises (IDBDatabaseException); + }; +} diff --git a/Source/WebCore/storage/IDBCursorBackendImpl.cpp b/Source/WebCore/storage/IDBCursorBackendImpl.cpp new file mode 100644 index 0000000..9b4e4f1 --- /dev/null +++ b/Source/WebCore/storage/IDBCursorBackendImpl.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBCursorBackendImpl.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "CrossThreadTask.h" +#include "IDBCallbacks.h" +#include "IDBDatabaseBackendImpl.h" +#include "IDBDatabaseError.h" +#include "IDBDatabaseException.h" +#include "IDBIndexBackendImpl.h" +#include "IDBKeyRange.h" +#include "IDBObjectStoreBackendImpl.h" +#include "IDBRequest.h" +#include "IDBSQLiteDatabase.h" +#include "IDBTransactionBackendInterface.h" +#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" +#include "SerializedScriptValue.h" + +namespace WebCore { + +IDBCursorBackendImpl::IDBCursorBackendImpl(IDBSQLiteDatabase* database, PassRefPtr<IDBKeyRange> keyRange, IDBCursor::Direction direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore) + : m_database(database) + , m_keyRange(keyRange) + , m_direction(direction) + , m_query(query) + , m_isSerializedScriptValueCursor(isSerializedScriptValueCursor) + , m_transaction(transaction) + , m_objectStore(objectStore) +{ + loadCurrentRow(); +} + +IDBCursorBackendImpl::~IDBCursorBackendImpl() +{ +} + +unsigned short IDBCursorBackendImpl::direction() const +{ + return m_direction; +} + +PassRefPtr<IDBKey> IDBCursorBackendImpl::key() const +{ + + return m_currentKey; +} + +PassRefPtr<IDBAny> IDBCursorBackendImpl::value() const +{ + if (m_isSerializedScriptValueCursor) + return IDBAny::create(m_currentSerializedScriptValue.get()); + return IDBAny::create(m_currentIDBKeyValue.get()); +} + +void IDBCursorBackendImpl::update(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) +{ + RefPtr<IDBCursorBackendImpl> cursor = this; + RefPtr<SerializedScriptValue> value = prpValue; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + // FIXME: Throw DATA_ERR and SERIAL_ERR when appropriate. + if (!m_transaction->scheduleTask(createCallbackTask(&IDBCursorBackendImpl::updateInternal, cursor, value, callbacks))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBCursorBackendImpl::updateInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl> cursor, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBCallbacks> callbacks) +{ + // FIXME: This method doesn't update indexes. It's dangerous to call in its current state. + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Not implemented.")); + return; + + RefPtr<SerializedScriptValue> value = prpValue; + + if (!cursor->m_query || cursor->m_currentId == InvalidId) { + // FIXME: Use the proper error code when it's specced. + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Operation not possible.")); + return; + } + + String sql = "UPDATE ObjectStoreData SET value = ? WHERE id = ?"; + SQLiteStatement updateQuery(cursor->database(), sql); + + bool ok = updateQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + updateQuery.bindText(1, value->toWireString()); + updateQuery.bindInt64(2, cursor->m_currentId); + ok = updateQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + + if (cursor->m_isSerializedScriptValueCursor) + cursor->m_currentSerializedScriptValue = value.release(); + callbacks->onSuccess(); +} + +void IDBCursorBackendImpl::continueFunction(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) +{ + RefPtr<IDBCursorBackendImpl> cursor = this; + RefPtr<IDBKey> key = prpKey; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + if (!m_transaction->scheduleTask(createCallbackTask(&IDBCursorBackendImpl::continueFunctionInternal, cursor, key, callbacks))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBCursorBackendImpl::continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl> prpCursor, PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> callbacks) +{ + RefPtr<IDBCursorBackendImpl> cursor = prpCursor; + RefPtr<IDBKey> key = prpKey; + while (true) { + if (!cursor->m_query || cursor->m_query->step() != SQLResultRow) { + cursor->m_query = 0; + cursor->m_currentId = InvalidId; + cursor->m_currentKey = 0; + cursor->m_currentSerializedScriptValue = 0; + cursor->m_currentIDBKeyValue = 0; + callbacks->onSuccess(); + return; + } + + RefPtr<IDBKey> oldKey = cursor->m_currentKey; + cursor->loadCurrentRow(); + + // If a key was supplied, we must loop until we find that key (or hit the end). + if (key && !key->isEqual(cursor->m_currentKey.get())) + continue; + + // If we don't have a uniqueness constraint, we can stop now. + if (cursor->m_direction == IDBCursor::NEXT || cursor->m_direction == IDBCursor::PREV) + break; + if (!cursor->m_currentKey->isEqual(oldKey.get())) + break; + } + + callbacks->onSuccess(cursor.get()); +} + +void IDBCursorBackendImpl::deleteFunction(PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) +{ + if (!m_query || m_currentId == InvalidId || !m_isSerializedScriptValueCursor) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return; + } + + // FIXME: Check that the transaction is READ_WRITE + // if (m_transaction->mode() == IDBTransaction::READ_ONLY) { + // FIXME: We must return READ_ONLY_ERR here. Fix this when we update IDBDatabaseException to match the spec. + // ec = IDBDatabaseException::NOT_ALLOWED_ERR; + // return; + // } + RefPtr<IDBKey> key = m_currentIDBKeyValue ? m_currentIDBKeyValue : m_currentKey; + m_objectStore->deleteFunction(key.release(), prpCallbacks, m_transaction.get(), ec); +} + + +void IDBCursorBackendImpl::loadCurrentRow() +{ + // The column numbers depend on the query in IDBObjectStoreBackendImpl::openCursorInternal or + // IDBIndexBackendImpl::openCursorInternal. + m_currentId = m_query->getColumnInt64(0); + m_currentKey = IDBKey::fromQuery(*m_query, 1); + if (m_isSerializedScriptValueCursor) + m_currentSerializedScriptValue = SerializedScriptValue::createFromWire(m_query->getColumnText(4)); + + m_currentIDBKeyValue = IDBKey::fromQuery(*m_query, 5); +} + +SQLiteDatabase& IDBCursorBackendImpl::database() const +{ + return m_database->db(); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBCursorBackendImpl.h b/Source/WebCore/storage/IDBCursorBackendImpl.h new file mode 100644 index 0000000..e3a8995 --- /dev/null +++ b/Source/WebCore/storage/IDBCursorBackendImpl.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + + +#ifndef IDBCursorBackendImpl_h +#define IDBCursorBackendImpl_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursor.h" +#include "IDBCursorBackendInterface.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBDatabaseBackendImpl; +class IDBIndexBackendImpl; +class IDBKeyRange; +class IDBObjectStoreBackendInterface; +class IDBSQLiteDatabase; +class IDBTransactionBackendInterface; +class SQLiteDatabase; +class SQLiteStatement; +class SerializedScriptValue; + +class IDBCursorBackendImpl : public IDBCursorBackendInterface { +public: + static PassRefPtr<IDBCursorBackendImpl> create(IDBSQLiteDatabase* database, PassRefPtr<IDBKeyRange> keyRange, IDBCursor::Direction direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface* transaction, IDBObjectStoreBackendInterface* objectStore) + { + return adoptRef(new IDBCursorBackendImpl(database, keyRange, direction, query, isSerializedScriptValueCursor, transaction, objectStore)); + } + virtual ~IDBCursorBackendImpl(); + + virtual unsigned short direction() const; + virtual PassRefPtr<IDBKey> key() const; + virtual PassRefPtr<IDBAny> value() const; + virtual void update(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBCallbacks>, ExceptionCode&); + virtual void continueFunction(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, ExceptionCode&); + virtual void deleteFunction(PassRefPtr<IDBCallbacks>, ExceptionCode&); + +private: + IDBCursorBackendImpl(IDBSQLiteDatabase*, PassRefPtr<IDBKeyRange>, IDBCursor::Direction, PassOwnPtr<SQLiteStatement> query, bool isSerializedScriptValueCursor, IDBTransactionBackendInterface*, IDBObjectStoreBackendInterface*); + + void loadCurrentRow(); + SQLiteDatabase& database() const; + + static void updateInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl>, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBCallbacks>); + static void continueFunctionInternal(ScriptExecutionContext*, PassRefPtr<IDBCursorBackendImpl>, PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>); + + static const int64_t InvalidId = -1; + + RefPtr<IDBSQLiteDatabase> m_database; + + RefPtr<IDBKeyRange> m_keyRange; + IDBCursor::Direction m_direction; + OwnPtr<SQLiteStatement> m_query; + bool m_isSerializedScriptValueCursor; + int64_t m_currentId; + RefPtr<IDBKey> m_currentKey; + + // m_isSerializedScriptValueCursor will only be available for object cursors. + RefPtr<SerializedScriptValue> m_currentSerializedScriptValue; + // FIXME: make the primary key available via script for all types of cursors. + RefPtr<IDBKey> m_currentIDBKeyValue; + + RefPtr<IDBTransactionBackendInterface> m_transaction; + RefPtr<IDBObjectStoreBackendInterface> m_objectStore; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBCursorBackendImpl_h diff --git a/Source/WebCore/storage/IDBCursorBackendInterface.h b/Source/WebCore/storage/IDBCursorBackendInterface.h new file mode 100644 index 0000000..0d132ca --- /dev/null +++ b/Source/WebCore/storage/IDBCursorBackendInterface.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBCursorBackendInterface_h +#define IDBCursorBackendInterface_h + +#if ENABLE(INDEXED_DATABASE) + +#include "ExceptionCode.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Threading.h> + +namespace WebCore { + +class IDBAny; +class IDBCallbacks; +class IDBKey; +class IDBRequest; +class SerializedScriptValue; + +class IDBCursorBackendInterface : public ThreadSafeShared<IDBCursorBackendInterface> { +public: + virtual ~IDBCursorBackendInterface() {} + + virtual unsigned short direction() const = 0; + virtual PassRefPtr<IDBKey> key() const = 0; + virtual PassRefPtr<IDBAny> value() const = 0; + + virtual void update(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0; + virtual void continueFunction(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0; + virtual void deleteFunction(PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0; +}; + +} // namespace WebCore + +#endif + +#endif // IDBCursorBackendInterface_h diff --git a/Source/WebCore/storage/IDBDatabase.cpp b/Source/WebCore/storage/IDBDatabase.cpp new file mode 100644 index 0000000..7a9141a --- /dev/null +++ b/Source/WebCore/storage/IDBDatabase.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBDatabase.h" + +#include "IDBAny.h" +#include "IDBDatabaseError.h" +#include "IDBDatabaseException.h" +#include "IDBFactoryBackendInterface.h" +#include "IDBIndex.h" +#include "IDBObjectStore.h" +#include "IDBRequest.h" +#include "IDBTransaction.h" +#include "ScriptExecutionContext.h" +#include <limits> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +// FIXME: We need to spec this differently. +const unsigned long defaultTimeout = 0; // Infinite. + +IDBDatabase::IDBDatabase(PassRefPtr<IDBDatabaseBackendInterface> backend) + : m_backend(backend) +{ + // We pass a reference of this object before it can be adopted. + relaxAdoptionRequirement(); +} + +IDBDatabase::~IDBDatabase() +{ +} + +void IDBDatabase::setSetVersionTransaction(IDBTransactionBackendInterface* transaction) +{ + m_setVersionTransaction = transaction; +} + +PassRefPtr<IDBObjectStore> IDBDatabase::createObjectStore(const String& name, const OptionsObject& options, ExceptionCode& ec) +{ + if (!m_setVersionTransaction) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } + + String keyPath; + options.getKeyString("keyPath", keyPath); + bool autoIncrement = false; + options.getKeyBool("autoIncrement", autoIncrement); + // FIXME: Look up evictable and pass that on as well. + + if (autoIncrement) { + // FIXME: Implement support for auto increment. + ec = IDBDatabaseException::UNKNOWN_ERR; + return 0; + } + + RefPtr<IDBObjectStoreBackendInterface> objectStore = m_backend->createObjectStore(name, keyPath, autoIncrement, m_setVersionTransaction.get(), ec); + if (!objectStore) { + ASSERT(ec); + return 0; + } + return IDBObjectStore::create(objectStore.release(), m_setVersionTransaction.get()); +} + +void IDBDatabase::deleteObjectStore(const String& name, ExceptionCode& ec) +{ + if (!m_setVersionTransaction) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return; + } + + m_backend->deleteObjectStore(name, m_setVersionTransaction.get(), ec); +} + +PassRefPtr<IDBRequest> IDBDatabase::setVersion(ScriptExecutionContext* context, const String& version, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), 0); + m_backend->setVersion(version, request, ec); + return request; +} + +PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, const OptionsObject& options, ExceptionCode& ec) +{ + RefPtr<DOMStringList> storeNames = options.getKeyDOMStringList("objectStoreNames"); + if (!storeNames) { + storeNames = DOMStringList::create(); + String storeName; + if (options.getKeyString("objectStoreNames", storeName)) + storeNames->append(storeName); + } + + // Gets cast to an unsigned short. + int32_t mode = IDBTransaction::READ_ONLY; + options.getKeyInt32("mode", mode); + if (mode != IDBTransaction::READ_WRITE && mode != IDBTransaction::READ_ONLY) { + // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 + ec = IDBDatabaseException::CONSTRAINT_ERR; + return 0; + } + + // Gets cast to an unsigned long. + // FIXME: The spec needs to be updated on this. It should probably take a double. + int32_t timeout = defaultTimeout; + options.getKeyInt32("timeout", timeout); + int64_t unsignedLongMax = std::numeric_limits<unsigned long>::max(); + if (timeout < 0 || timeout > unsignedLongMax) + timeout = defaultTimeout; // Ignore illegal values. + + // We need to create a new transaction synchronously. Locks are acquired asynchronously. Operations + // can be queued against the transaction at any point. They will start executing as soon as the + // appropriate locks have been acquired. + // Also note that each backend object corresponds to exactly one IDBTransaction object. + RefPtr<IDBTransactionBackendInterface> transactionBackend = m_backend->transaction(storeNames.get(), mode, timeout, ec); + if (!transactionBackend) { + ASSERT(ec); + return 0; + } + RefPtr<IDBTransaction> transaction = IDBTransaction::create(context, transactionBackend, this); + transactionBackend->setCallbacks(transaction.get()); + return transaction.release(); +} + +void IDBDatabase::close() +{ + m_backend->close(); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBDatabase.h b/Source/WebCore/storage/IDBDatabase.h new file mode 100644 index 0000000..9ebbf00 --- /dev/null +++ b/Source/WebCore/storage/IDBDatabase.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBDatabase_h +#define IDBDatabase_h + +#include "DOMStringList.h" +#include "ExceptionCode.h" +#include "IDBDatabaseBackendInterface.h" +#include "IDBObjectStore.h" +#include "IDBTransaction.h" +#include "OptionsObject.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBAny; +class IDBRequest; +class ScriptExecutionContext; + +class IDBDatabase : public RefCounted<IDBDatabase> { +public: + static PassRefPtr<IDBDatabase> create(PassRefPtr<IDBDatabaseBackendInterface> database) + { + return adoptRef(new IDBDatabase(database)); + } + ~IDBDatabase(); + + void setSetVersionTransaction(IDBTransactionBackendInterface*); + + // Implement the IDL + String name() const { return m_backend->name(); } + String version() const { return m_backend->version(); } + PassRefPtr<DOMStringList> objectStoreNames() const { return m_backend->objectStoreNames(); } + + // FIXME: Try to modify the code generator so this is unneeded. + PassRefPtr<IDBObjectStore> createObjectStore(const String& name, ExceptionCode& ec) { return createObjectStore(name, OptionsObject(), ec); } + PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext* context, ExceptionCode& ec) { return transaction(context, OptionsObject(), ec); } + + PassRefPtr<IDBObjectStore> createObjectStore(const String& name, const OptionsObject&, ExceptionCode&); + void deleteObjectStore(const String& name, ExceptionCode&); + PassRefPtr<IDBRequest> setVersion(ScriptExecutionContext*, const String& version, ExceptionCode&); + PassRefPtr<IDBTransaction> transaction(ScriptExecutionContext*, const OptionsObject&, ExceptionCode&); + void close(); + +private: + IDBDatabase(PassRefPtr<IDBDatabaseBackendInterface>); + + RefPtr<IDBDatabaseBackendInterface> m_backend; + RefPtr<IDBTransactionBackendInterface> m_setVersionTransaction; +}; + +} // namespace WebCore + +#endif + +#endif // IDBDatabase_h diff --git a/Source/WebCore/storage/IDBDatabase.idl b/Source/WebCore/storage/IDBDatabase.idl new file mode 100644 index 0000000..c6edd48 --- /dev/null +++ b/Source/WebCore/storage/IDBDatabase.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBDatabase { + readonly attribute DOMString name; + readonly attribute DOMString version; + readonly attribute DOMStringList objectStoreNames; + + IDBObjectStore createObjectStore(in DOMString name, in [Optional] OptionsObject options) + raises (IDBDatabaseException); + void deleteObjectStore(in DOMString name) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest setVersion(in DOMString version) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBTransaction transaction (in [Optional] OptionsObject optionsObject) + raises (IDBDatabaseException); + // FIXME: Implement. + //void close(); + }; + +} diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp new file mode 100644 index 0000000..fa9a336 --- /dev/null +++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.cpp @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBDatabaseBackendImpl.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "CrossThreadTask.h" +#include "DOMStringList.h" +#include "IDBDatabaseException.h" +#include "IDBFactoryBackendImpl.h" +#include "IDBObjectStoreBackendImpl.h" +#include "IDBSQLiteDatabase.h" +#include "IDBTransactionBackendImpl.h" +#include "IDBTransactionCoordinator.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" + +namespace WebCore { + +static bool extractMetaData(SQLiteDatabase& sqliteDatabase, const String& name, String& foundVersion, int64& foundId) +{ + SQLiteStatement databaseQuery(sqliteDatabase, "SELECT id, version FROM Databases WHERE name = ?"); + if (databaseQuery.prepare() != SQLResultOk) { + ASSERT_NOT_REACHED(); + return false; + } + databaseQuery.bindText(1, name); + if (databaseQuery.step() != SQLResultRow) + return false; + + foundId = databaseQuery.getColumnInt64(0); + foundVersion = databaseQuery.getColumnText(1); + + if (databaseQuery.step() == SQLResultRow) + ASSERT_NOT_REACHED(); + return true; +} + +static bool setMetaData(SQLiteDatabase& sqliteDatabase, const String& name, const String& version, int64_t& rowId) +{ + ASSERT(!name.isNull()); + ASSERT(!version.isNull()); + + String sql = rowId != IDBDatabaseBackendImpl::InvalidId ? "UPDATE Databases SET name = ?, version = ? WHERE id = ?" + : "INSERT INTO Databases (name, description, version) VALUES (?, '', ?)"; + SQLiteStatement query(sqliteDatabase, sql); + if (query.prepare() != SQLResultOk) { + ASSERT_NOT_REACHED(); + return false; + } + + query.bindText(1, name); + query.bindText(2, version); + if (rowId != IDBDatabaseBackendImpl::InvalidId) + query.bindInt64(3, rowId); + + if (query.step() != SQLResultDone) + return false; + + if (rowId == IDBDatabaseBackendImpl::InvalidId) + rowId = sqliteDatabase.lastInsertRowID(); + + return true; +} + +IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBSQLiteDatabase* sqliteDatabase, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier) + : m_sqliteDatabase(sqliteDatabase) + , m_id(InvalidId) + , m_name(name) + , m_version("") + , m_identifier(uniqueIdentifier) + , m_factory(factory) + , m_transactionCoordinator(coordinator) +{ + ASSERT(!m_name.isNull()); + + bool success = extractMetaData(m_sqliteDatabase->db(), m_name, m_version, m_id); + ASSERT_UNUSED(success, success == (m_id != InvalidId)); + if (!setMetaData(m_sqliteDatabase->db(), m_name, m_version, m_id)) + ASSERT_NOT_REACHED(); // FIXME: Need better error handling. + loadObjectStores(); +} + +IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl() +{ + m_factory->removeIDBDatabaseBackend(m_identifier); +} + +SQLiteDatabase& IDBDatabaseBackendImpl::sqliteDatabase() const +{ + return m_sqliteDatabase->db(); +} + +PassRefPtr<DOMStringList> IDBDatabaseBackendImpl::objectStoreNames() const +{ + RefPtr<DOMStringList> objectStoreNames = DOMStringList::create(); + for (ObjectStoreMap::const_iterator it = m_objectStores.begin(); it != m_objectStores.end(); ++it) + objectStoreNames->append(it->first); + return objectStoreNames.release(); +} + +PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) +{ + ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE); + + if (m_objectStores.contains(name)) { + ec = IDBDatabaseException::CONSTRAINT_ERR; + return 0; + } + + RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), name, keyPath, autoIncrement); + ASSERT(objectStore->name() == name); + + RefPtr<IDBDatabaseBackendImpl> database = this; + RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; + if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::createObjectStoreInternal, database, objectStore, transaction), + createCallbackTask(&IDBDatabaseBackendImpl::removeObjectStoreFromMap, database, objectStore))) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } + + m_objectStores.set(name, objectStore); + return objectStore.release(); +} + +void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + SQLiteStatement insert(database->sqliteDatabase(), "INSERT INTO ObjectStores (name, keyPath, doAutoIncrement, databaseId) VALUES (?, ?, ?, ?)"); + if (insert.prepare() != SQLResultOk) { + transaction->abort(); + return; + } + insert.bindText(1, objectStore->name()); + insert.bindText(2, objectStore->keyPath()); + insert.bindInt(3, static_cast<int>(objectStore->autoIncrement())); + insert.bindInt64(4, database->id()); + if (insert.step() != SQLResultDone) { + transaction->abort(); + return; + } + int64_t id = database->sqliteDatabase().lastInsertRowID(); + objectStore->setId(id); + transaction->didCompleteTaskEvents(); +} + +PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(const String& name) +{ + return m_objectStores.get(name); +} + +static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) +{ + SQLiteStatement deleteQuery(db, sql); + bool ok = deleteQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + deleteQuery.bindInt64(1, id); + ok = deleteQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. +} + +void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) +{ + RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name); + if (!objectStore) { + ec = IDBDatabaseException::NOT_FOUND_ERR; + return; + } + RefPtr<IDBDatabaseBackendImpl> database = this; + RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; + if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::deleteObjectStoreInternal, database, objectStore, transaction), + createCallbackTask(&IDBDatabaseBackendImpl::addObjectStoreToMap, database, objectStore))) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return; + } + m_objectStores.remove(name); +} + +void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + doDelete(database->sqliteDatabase(), "DELETE FROM ObjectStores WHERE id = ?", objectStore->id()); + doDelete(database->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE objectStoreId = ?", objectStore->id()); + doDelete(database->sqliteDatabase(), "DELETE FROM IndexData WHERE indexId IN (SELECT id FROM Indexes WHERE objectStoreId = ?)", objectStore->id()); + doDelete(database->sqliteDatabase(), "DELETE FROM Indexes WHERE objectStoreId = ?", objectStore->id()); + + transaction->didCompleteTaskEvents(); +} + +void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, ExceptionCode& ec) +{ + RefPtr<IDBDatabaseBackendImpl> database = this; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + RefPtr<DOMStringList> objectStoreNames = DOMStringList::create(); + RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, 0, this); + if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::setVersionInternal, database, version, callbacks, transaction), + createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_version))) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + } +} + +void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + int64_t databaseId = database->id(); + database->m_version = version; + if (!setMetaData(database->m_sqliteDatabase->db(), database->m_name, database->m_version, databaseId)) { + // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); + transaction->abort(); + return; + } + callbacks->onSuccess(transaction); +} + +PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, unsigned long timeout, ExceptionCode& ec) +{ + for (size_t i = 0; i < objectStoreNames->length(); ++i) { + if (!m_objectStores.contains(objectStoreNames->item(i))) { + ec = IDBDatabaseException::NOT_FOUND_ERR; + return 0; + } + } + + // FIXME: Return not allowed err if close has been called. + return IDBTransactionBackendImpl::create(objectStoreNames, mode, timeout, this); +} + +void IDBDatabaseBackendImpl::close() +{ + // FIXME: Implement. +} + +void IDBDatabaseBackendImpl::loadObjectStores() +{ + SQLiteStatement objectStoresQuery(sqliteDatabase(), "SELECT id, name, keyPath, doAutoIncrement FROM ObjectStores WHERE databaseId = ?"); + bool ok = objectStoresQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + objectStoresQuery.bindInt64(1, m_id); + + while (objectStoresQuery.step() == SQLResultRow) { + int64_t id = objectStoresQuery.getColumnInt64(0); + String name = objectStoresQuery.getColumnText(1); + String keyPath = objectStoresQuery.getColumnText(2); + bool autoIncrement = !!objectStoresQuery.getColumnInt(3); + + m_objectStores.set(name, IDBObjectStoreBackendImpl::create(m_sqliteDatabase.get(), id, name, keyPath, autoIncrement)); + } +} + +void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore) +{ + ASSERT(database->m_objectStores.contains(objectStore->name())); + database->m_objectStores.remove(objectStore->name()); +} + +void IDBDatabaseBackendImpl::addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore) +{ + RefPtr<IDBObjectStoreBackendImpl> objectStorePtr = objectStore; + ASSERT(!database->m_objectStores.contains(objectStorePtr->name())); + database->m_objectStores.set(objectStorePtr->name(), objectStorePtr); +} + +void IDBDatabaseBackendImpl::resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version) +{ + database->m_version = version; +} + + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBDatabaseBackendImpl.h b/Source/WebCore/storage/IDBDatabaseBackendImpl.h new file mode 100644 index 0000000..570f6a5 --- /dev/null +++ b/Source/WebCore/storage/IDBDatabaseBackendImpl.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBDatabaseBackendImpl_h +#define IDBDatabaseBackendImpl_h + +#include "IDBCallbacks.h" +#include "IDBDatabase.h" +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBFactoryBackendImpl; +class IDBObjectStoreBackendImpl; +class IDBSQLiteDatabase; +class IDBTransactionCoordinator; +class SQLiteDatabase; + +class IDBDatabaseBackendImpl : public IDBDatabaseBackendInterface { +public: + static PassRefPtr<IDBDatabaseBackendImpl> create(const String& name, IDBSQLiteDatabase* database, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier) + { + return adoptRef(new IDBDatabaseBackendImpl(name, database, coordinator, factory, uniqueIdentifier)); + } + virtual ~IDBDatabaseBackendImpl(); + + SQLiteDatabase& sqliteDatabase() const; + + static const int64_t InvalidId = 0; + int64_t id() const { return m_id; } + + virtual String name() const { return m_name; } + virtual String version() const { return m_version; } + virtual PassRefPtr<DOMStringList> objectStoreNames() const; + + virtual PassRefPtr<IDBObjectStoreBackendInterface> createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void deleteObjectStore(const String& name, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, ExceptionCode&); + virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* objectStoreNames, unsigned short mode, unsigned long timeout, ExceptionCode&); + virtual void close(); + + PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name); + IDBTransactionCoordinator* transactionCoordinator() const { return m_transactionCoordinator.get(); } + +private: + IDBDatabaseBackendImpl(const String& name, IDBSQLiteDatabase* database, IDBTransactionCoordinator*, IDBFactoryBackendImpl*, const String& uniqueIdentifier); + + void loadObjectStores(); + + static void createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBTransactionBackendInterface>); + static void deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBTransactionBackendInterface>); + static void setVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, const String& version, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); + + // These are used as setVersion transaction abort tasks. + static void removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, PassRefPtr<IDBObjectStoreBackendImpl>); + static void addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, PassRefPtr<IDBObjectStoreBackendImpl>); + static void resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl>, const String& version); + + RefPtr<IDBSQLiteDatabase> m_sqliteDatabase; + int64 m_id; + String m_name; + String m_version; + + String m_identifier; + // This might not need to be a RefPtr since the factory's lifetime is that of the page group, but it's better to be conservitive than sorry. + RefPtr<IDBFactoryBackendImpl> m_factory; + + typedef HashMap<String, RefPtr<IDBObjectStoreBackendImpl> > ObjectStoreMap; + ObjectStoreMap m_objectStores; + + RefPtr<IDBTransactionCoordinator> m_transactionCoordinator; +}; + +} // namespace WebCore + +#endif + +#endif // IDBDatabaseBackendImpl_h diff --git a/Source/WebCore/storage/IDBDatabaseBackendInterface.h b/Source/WebCore/storage/IDBDatabaseBackendInterface.h new file mode 100644 index 0000000..0dc59b1 --- /dev/null +++ b/Source/WebCore/storage/IDBDatabaseBackendInterface.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBDatabaseBackendInterface_h +#define IDBDatabaseBackendInterface_h + +#include "ExceptionCode.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class DOMStringList; +class Frame; +class IDBCallbacks; +class IDBObjectStoreBackendInterface; +class IDBTransactionBackendInterface; +class IDBTransactionCallbacks; + +// This class is shared by IDBDatabase (async) and IDBDatabaseSync (sync). +// This is implemented by IDBDatabaseBackendImpl and optionally others (in order to proxy +// calls across process barriers). All calls to these classes should be non-blocking and +// trigger work on a background thread if necessary. +class IDBDatabaseBackendInterface : public ThreadSafeShared<IDBDatabaseBackendInterface> { +public: + virtual ~IDBDatabaseBackendInterface() { } + + virtual String name() const = 0; + virtual String version() const = 0; + virtual PassRefPtr<DOMStringList> objectStoreNames() const = 0; + + virtual PassRefPtr<IDBObjectStoreBackendInterface> createObjectStore(const String& name, const String& keyPath, bool autoIncrement, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual void deleteObjectStore(const String& name, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual void setVersion(const String& version, PassRefPtr<IDBCallbacks>, ExceptionCode&) = 0; + virtual PassRefPtr<IDBTransactionBackendInterface> transaction(DOMStringList* storeNames, unsigned short mode, unsigned long timeout, ExceptionCode&) = 0; + virtual void close() = 0; +}; + +} // namespace WebCore + +#endif + +#endif // IDBDatabaseBackendInterface_h diff --git a/Source/WebCore/storage/IDBDatabaseError.h b/Source/WebCore/storage/IDBDatabaseError.h new file mode 100644 index 0000000..8b42f17 --- /dev/null +++ b/Source/WebCore/storage/IDBDatabaseError.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBDatabaseError_h +#define IDBDatabaseError_h + +#include "IDBDatabaseException.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBDatabaseError : public RefCounted<IDBDatabaseError> { +public: + static PassRefPtr<IDBDatabaseError> create(unsigned short code, const String& message) + { + ASSERT(code >= IDBDatabaseException::IDBDatabaseExceptionOffset); + ASSERT(code < IDBDatabaseException::IDBDatabaseExceptionMax); + return adoptRef(new IDBDatabaseError(code - IDBDatabaseException::IDBDatabaseExceptionOffset, message)); + } + + static PassRefPtr<IDBDatabaseError> createWithoutOffset(unsigned short code, const String& message) + { + ASSERT(code < IDBDatabaseException::IDBDatabaseExceptionOffset); + return adoptRef(new IDBDatabaseError(code, message)); + } + + ~IDBDatabaseError() { } + + unsigned short code() const { return m_code; } + void setCode(unsigned short value) { m_code = value; } + const String& message() const { return m_message; } + void setMessage(const String& value) { m_message = value; } + +private: + IDBDatabaseError(unsigned short code, const String& message) + : m_code(code), m_message(message) { } + + unsigned short m_code; + String m_message; +}; + +} // namespace WebCore + +#endif + +#endif // IDBDatabaseError_h diff --git a/Source/WebCore/storage/IDBDatabaseError.idl b/Source/WebCore/storage/IDBDatabaseError.idl new file mode 100644 index 0000000..2912a1d --- /dev/null +++ b/Source/WebCore/storage/IDBDatabaseError.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBDatabaseError { + attribute unsigned short code; + attribute DOMString message; + }; + +} diff --git a/Source/WebCore/storage/IDBDatabaseException.h b/Source/WebCore/storage/IDBDatabaseException.h new file mode 100644 index 0000000..936b05a --- /dev/null +++ b/Source/WebCore/storage/IDBDatabaseException.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBDatabaseException_h +#define IDBDatabaseException_h + +#if ENABLE(INDEXED_DATABASE) + +#include "ExceptionBase.h" + +namespace WebCore { + +class IDBDatabaseException : public ExceptionBase { +public: + static PassRefPtr<IDBDatabaseException> create(const ExceptionCodeDescription& description) + { + return adoptRef(new IDBDatabaseException(description)); + } + + static const int IDBDatabaseExceptionOffset = 1200; + static const int IDBDatabaseExceptionMax = 1299; + + enum IDBDatabaseExceptionCode { + UNKNOWN_ERR = IDBDatabaseExceptionOffset + 1, + NON_TRANSIENT_ERR = IDBDatabaseExceptionOffset + 2, + NOT_FOUND_ERR = IDBDatabaseExceptionOffset + 3, + CONSTRAINT_ERR = IDBDatabaseExceptionOffset + 4, + DATA_ERR = IDBDatabaseExceptionOffset + 5, + NOT_ALLOWED_ERR = IDBDatabaseExceptionOffset + 6, + SERIAL_ERR = IDBDatabaseExceptionOffset + 7, + RECOVERABLE_ERR = IDBDatabaseExceptionOffset + 8, + TRANSIENT_ERR = IDBDatabaseExceptionOffset + 9, + TIMEOUT_ERR = IDBDatabaseExceptionOffset + 10, + DEADLOCK_ERR = IDBDatabaseExceptionOffset + 11 + }; + + static int ErrorCodeToExceptionCode(int errorCode) + { + if (!errorCode) + return 0; + return errorCode + IDBDatabaseExceptionOffset; + } + +private: + IDBDatabaseException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } +}; + +} // namespace WebCore + +#endif + +#endif // IDBDatabaseException_h diff --git a/Source/WebCore/storage/IDBDatabaseException.idl b/Source/WebCore/storage/IDBDatabaseException.idl new file mode 100644 index 0000000..a56f4c7 --- /dev/null +++ b/Source/WebCore/storage/IDBDatabaseException.idl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE, + DontCheckEnums + ] IDBDatabaseException { + + readonly attribute unsigned short code; + readonly attribute DOMString name; + readonly attribute DOMString message; + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + // Override in a Mozilla compatible format + [DontEnum] DOMString toString(); +#endif + + const unsigned short UNKNOWN_ERR = 1; + const unsigned short NON_TRANSIENT_ERR = 2; + const unsigned short NOT_FOUND_ERR = 3; + const unsigned short CONSTRAINT_ERR = 4; + const unsigned short DATA_ERR = 5; + const unsigned short NOT_ALLOWED_ERR = 6; + const unsigned short SERIAL_ERR = 7; + const unsigned short RECOVERABLE_ERR = 8; + const unsigned short TRANSIENT_ERR = 9; + const unsigned short TIMEOUT_ERR = 10; + const unsigned short DEADLOCK_ERR = 11; + }; + +} diff --git a/Source/WebCore/storage/IDBErrorEvent.cpp b/Source/WebCore/storage/IDBErrorEvent.cpp new file mode 100644 index 0000000..cba980d --- /dev/null +++ b/Source/WebCore/storage/IDBErrorEvent.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBErrorEvent.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "EventNames.h" +#include "IDBAny.h" +#include "IDBDatabaseError.h" + +namespace WebCore { + +PassRefPtr<IDBErrorEvent> IDBErrorEvent::create(PassRefPtr<IDBAny> source, const IDBDatabaseError& error) +{ + return adoptRef(new IDBErrorEvent(source, error)); +} + +IDBErrorEvent::IDBErrorEvent(PassRefPtr<IDBAny> source, const IDBDatabaseError& error) + : IDBEvent(eventNames().errorEvent, source) + , m_code(error.code()) + , m_message(error.message()) +{ +} + +IDBErrorEvent::~IDBErrorEvent() +{ +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBErrorEvent.h b/Source/WebCore/storage/IDBErrorEvent.h new file mode 100644 index 0000000..648da8b --- /dev/null +++ b/Source/WebCore/storage/IDBErrorEvent.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBErrorEvent_h +#define IDBErrorEvent_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBEvent.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBAny; +class IDBDatabaseError; + +class IDBErrorEvent : public IDBEvent { +public: + static PassRefPtr<IDBErrorEvent> create(PassRefPtr<IDBAny> source, const IDBDatabaseError&); + // FIXME: Need to allow creation of these events from JS. + virtual ~IDBErrorEvent(); + + unsigned short code() const { return m_code; } + String message() { return m_message; } + + virtual bool isIDBErrorEvent() const { return true; } + +private: + IDBErrorEvent(PassRefPtr<IDBAny> source, const IDBDatabaseError&); + + unsigned short m_code; + String m_message; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBEvent_h diff --git a/Source/WebCore/storage/IDBErrorEvent.idl b/Source/WebCore/storage/IDBErrorEvent.idl new file mode 100644 index 0000000..5c58f6f --- /dev/null +++ b/Source/WebCore/storage/IDBErrorEvent.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBErrorEvent : IDBEvent { + readonly attribute unsigned short code; + readonly attribute DOMString message; + }; +} diff --git a/Source/WebCore/storage/IDBEvent.cpp b/Source/WebCore/storage/IDBEvent.cpp new file mode 100644 index 0000000..f9f6060 --- /dev/null +++ b/Source/WebCore/storage/IDBEvent.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBEvent.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBAny.h" + +namespace WebCore { + +IDBEvent::IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source) + : Event(type, false, false) + , m_source(source) +{ +} + +IDBEvent::~IDBEvent() +{ +} + +PassRefPtr<IDBAny> IDBEvent::source() +{ + return m_source; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBEvent.h b/Source/WebCore/storage/IDBEvent.h new file mode 100644 index 0000000..c44e449 --- /dev/null +++ b/Source/WebCore/storage/IDBEvent.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBEvent_h +#define IDBEvent_h + +#if ENABLE(INDEXED_DATABASE) + +#include "Event.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBAny; + +class IDBEvent : public Event { +public: + virtual ~IDBEvent(); + + PassRefPtr<IDBAny> source(); + +protected: + IDBEvent(const AtomicString& type, PassRefPtr<IDBAny> source); + +private: + RefPtr<IDBAny> m_source; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBEvent_h diff --git a/Source/WebCore/storage/IDBEvent.idl b/Source/WebCore/storage/IDBEvent.idl new file mode 100644 index 0000000..4dd552e --- /dev/null +++ b/Source/WebCore/storage/IDBEvent.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBEvent : Event { + readonly attribute IDBAny source; + }; +} diff --git a/Source/WebCore/storage/IDBFactory.cpp b/Source/WebCore/storage/IDBFactory.cpp new file mode 100644 index 0000000..85e976c --- /dev/null +++ b/Source/WebCore/storage/IDBFactory.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBFactory.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "DOMStringList.h" +#include "Document.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "GroupSettings.h" +#include "IDBDatabase.h" +#include "IDBDatabaseException.h" +#include "IDBFactoryBackendInterface.h" +#include "IDBKeyRange.h" +#include "IDBRequest.h" +#include "Page.h" +#include "PageGroup.h" + +namespace WebCore { + +IDBFactory::IDBFactory(IDBFactoryBackendInterface* factory) + : m_factoryBackend(factory) +{ + // We pass a reference to this object before it can be adopted. + relaxAdoptionRequirement(); +} + +IDBFactory::~IDBFactory() +{ +} + +PassRefPtr<IDBRequest> IDBFactory::open(ScriptExecutionContext* context, const String& name, ExceptionCode& ec) +{ + if (!context->isDocument()) { + // FIXME: make this work with workers. + return 0; + } + + Document* document = static_cast<Document*>(context); + if (!document->frame() || !document->page()) + return 0; + + + // FIXME: Raise a NON_TRANSIENT_ERR if the name is invalid. + + + RefPtr<IDBRequest> request = IDBRequest::create(document, IDBAny::create(this), 0); + GroupSettings* groupSettings = document->page()->group().groupSettings(); + m_factoryBackend->open(name, request, document->securityOrigin(), document->frame(), groupSettings->indexedDBDatabasePath(), groupSettings->indexedDBQuotaBytes()); + return request; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBFactory.h b/Source/WebCore/storage/IDBFactory.h new file mode 100644 index 0000000..cd0d64f --- /dev/null +++ b/Source/WebCore/storage/IDBFactory.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef IDBFactory_h +#define IDBFactory_h + +#include "DOMStringList.h" +#include "ExceptionCode.h" +#include "IDBFactoryBackendInterface.h" +#include "IDBRequest.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBKey; +class IDBKeyRange; +class IDBFactoryBackendInterface; +class ScriptExecutionContext; + +class IDBFactory : public RefCounted<IDBFactory> { +public: + static PassRefPtr<IDBFactory> create(IDBFactoryBackendInterface* factory) + { + return adoptRef(new IDBFactory(factory)); + } + ~IDBFactory(); + + PassRefPtr<IDBRequest> open(ScriptExecutionContext*, const String& name, ExceptionCode&); + +private: + IDBFactory(IDBFactoryBackendInterface*); + + RefPtr<IDBFactoryBackendInterface> m_factoryBackend; +}; + +} // namespace WebCore + +#endif + +#endif // IDBFactory_h + diff --git a/Source/WebCore/storage/IDBFactory.idl b/Source/WebCore/storage/IDBFactory.idl new file mode 100644 index 0000000..02eed0e --- /dev/null +++ b/Source/WebCore/storage/IDBFactory.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBFactory { + [CallWith=ScriptExecutionContext] IDBRequest open(in DOMString name) + raises (IDBDatabaseException); + }; + +} diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.cpp b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp new file mode 100644 index 0000000..45cffeb --- /dev/null +++ b/Source/WebCore/storage/IDBFactoryBackendImpl.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBFactoryBackendImpl.h" + +#include "DOMStringList.h" +#include "FileSystem.h" +#include "IDBDatabaseBackendImpl.h" +#include "IDBDatabaseException.h" +#include "IDBSQLiteDatabase.h" +#include "IDBTransactionCoordinator.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" +#include "SecurityOrigin.h" +#include <wtf/Threading.h> +#include <wtf/UnusedParam.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IDBFactoryBackendImpl::IDBFactoryBackendImpl() + : m_transactionCoordinator(IDBTransactionCoordinator::create()) +{ +} + +IDBFactoryBackendImpl::~IDBFactoryBackendImpl() +{ +} + +void IDBFactoryBackendImpl::removeIDBDatabaseBackend(const String& uniqueIdentifier) +{ + ASSERT(m_databaseBackendMap.contains(uniqueIdentifier)); + m_databaseBackendMap.remove(uniqueIdentifier); +} + +void IDBFactoryBackendImpl::removeSQLiteDatabase(const String& uniqueIdentifier) +{ + ASSERT(m_sqliteDatabaseMap.contains(uniqueIdentifier)); + m_sqliteDatabaseMap.remove(uniqueIdentifier); +} + +static PassRefPtr<IDBSQLiteDatabase> openSQLiteDatabase(SecurityOrigin* securityOrigin, const String& pathBase, int64_t maximumSize, const String& fileIdentifier, IDBFactoryBackendImpl* factory) +{ + String path = ":memory:"; + if (!pathBase.isEmpty()) { + if (!makeAllDirectories(pathBase)) { + // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. + LOG_ERROR("Unabled to create LocalStorage database path %s", pathBase.utf8().data()); + return 0; + } + + path = pathByAppendingComponent(pathBase, securityOrigin->databaseIdentifier() + ".indexeddb"); + } + + RefPtr<IDBSQLiteDatabase> sqliteDatabase = IDBSQLiteDatabase::create(fileIdentifier, factory); + if (!sqliteDatabase->db().open(path)) { + // FIXME: Is there any other thing we could possibly do to recover at this point? If so, do it rather than just erroring out. + LOG_ERROR("Failed to open database file %s for IndexedDB", path.utf8().data()); + return 0; + } + + // FIXME: Error checking? + sqliteDatabase->db().setMaximumSize(maximumSize); + sqliteDatabase->db().turnOnIncrementalAutoVacuum(); + + return sqliteDatabase.release(); +} + +static bool createTables(SQLiteDatabase& sqliteDatabase) +{ + if (sqliteDatabase.tableExists("Databases")) + return true; + + static const char* commands[] = { + "CREATE TABLE Databases (id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT NOT NULL, version TEXT NOT NULL)", + "CREATE UNIQUE INDEX Databases_name ON Databases(name)", + + "CREATE TABLE ObjectStores (id INTEGER PRIMARY KEY, name TEXT NOT NULL, keyPath TEXT, doAutoIncrement INTEGER NOT NULL, databaseId INTEGER NOT NULL REFERENCES Databases(id))", + "CREATE UNIQUE INDEX ObjectStores_composit ON ObjectStores(databaseId, name)", + + "CREATE TABLE Indexes (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), name TEXT NOT NULL, keyPath TEXT, isUnique INTEGER NOT NULL)", + "CREATE UNIQUE INDEX Indexes_composit ON Indexes(objectStoreId, name)", + + "CREATE TABLE ObjectStoreData (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, value TEXT NOT NULL)", + "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", + + "CREATE TABLE IndexData (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate INTEGER, keyNumber INTEGER, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", + "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)", + "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)", + "CREATE INDEX IndexData_indexId ON IndexData(indexId)", + }; + + SQLiteTransaction transaction(sqliteDatabase, false); + transaction.begin(); + for (size_t i = 0; i < arraysize(commands); ++i) { + if (!sqliteDatabase.executeCommand(commands[i])) { + // FIXME: We should try to recover from this situation. Maybe nuke the database and start over? + LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]); + return false; + } + } + transaction.commit(); + return true; +} + +static bool createMetaDataTable(SQLiteDatabase& sqliteDatabase) +{ + static const char* commands[] = { + "CREATE TABLE MetaData (name TEXT PRIMARY KEY, value NONE)", + "INSERT INTO MetaData VALUES ('version', 1)", + }; + + SQLiteTransaction transaction(sqliteDatabase, false); + transaction.begin(); + for (size_t i = 0; i < arraysize(commands); ++i) { + if (!sqliteDatabase.executeCommand(commands[i])) + return false; + } + transaction.commit(); + return true; +} + +static bool getDatabaseVersion(SQLiteDatabase& sqliteDatabase, int* databaseVersion) +{ + SQLiteStatement query(sqliteDatabase, "SELECT value FROM MetaData WHERE name = 'version'"); + if (query.prepare() != SQLResultOk || query.step() != SQLResultRow) + return false; + + *databaseVersion = query.getColumnInt(0); + return query.finalize() == SQLResultOk; +} + +static bool migrateDatabase(SQLiteDatabase& sqliteDatabase) +{ + if (!sqliteDatabase.tableExists("MetaData")) { + if (!createMetaDataTable(sqliteDatabase)) + return false; + } + + int databaseVersion; + if (!getDatabaseVersion(sqliteDatabase, &databaseVersion)) + return false; + + if (databaseVersion == 1) { + static const char* commands[] = { + "DROP TABLE IF EXISTS ObjectStoreData2", + "CREATE TABLE ObjectStoreData2 (id INTEGER PRIMARY KEY, objectStoreId INTEGER NOT NULL REFERENCES ObjectStore(id), keyString TEXT, keyDate REAL, keyNumber REAL, value TEXT NOT NULL)", + "INSERT INTO ObjectStoreData2 SELECT * FROM ObjectStoreData", + "DROP TABLE ObjectStoreData", // This depends on SQLite not enforcing referential consistency. + "ALTER TABLE ObjectStoreData2 RENAME TO ObjectStoreData", + "CREATE UNIQUE INDEX ObjectStoreData_composit ON ObjectStoreData(keyString, keyDate, keyNumber, objectStoreId)", + "DROP TABLE IF EXISTS IndexData2", // This depends on SQLite not enforcing referential consistency. + "CREATE TABLE IndexData2 (id INTEGER PRIMARY KEY, indexId INTEGER NOT NULL REFERENCES Indexes(id), keyString TEXT, keyDate REAL, keyNumber REAL, objectStoreDataId INTEGER NOT NULL REFERENCES ObjectStoreData(id))", + "INSERT INTO IndexData2 SELECT * FROM IndexData", + "DROP TABLE IndexData", + "ALTER TABLE IndexData2 RENAME TO IndexData", + "CREATE INDEX IndexData_composit ON IndexData(keyString, keyDate, keyNumber, indexId)", + "CREATE INDEX IndexData_objectStoreDataId ON IndexData(objectStoreDataId)", + "CREATE INDEX IndexData_indexId ON IndexData(indexId)", + "UPDATE MetaData SET value = 2 WHERE name = 'version'", + }; + + SQLiteTransaction transaction(sqliteDatabase, false); + transaction.begin(); + for (size_t i = 0; i < arraysize(commands); ++i) { + if (!sqliteDatabase.executeCommand(commands[i])) { + LOG_ERROR("Failed to run the following command for IndexedDB: %s", commands[i]); + return false; + } + } + transaction.commit(); + + databaseVersion = 2; + } + + return true; +} + +void IDBFactoryBackendImpl::open(const String& name, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<SecurityOrigin> securityOrigin, Frame*, const String& dataDir, int64_t maximumSize) +{ + String fileIdentifier = securityOrigin->databaseIdentifier(); + String uniqueIdentifier = fileIdentifier + "@" + name; + IDBDatabaseBackendMap::iterator it = m_databaseBackendMap.find(uniqueIdentifier); + if (it != m_databaseBackendMap.end()) { + callbacks->onSuccess(it->second); + return; + } + + // FIXME: Everything from now on should be done on another thread. + + RefPtr<IDBSQLiteDatabase> sqliteDatabase; + SQLiteDatabaseMap::iterator it2 = m_sqliteDatabaseMap.find(fileIdentifier); + if (it2 != m_sqliteDatabaseMap.end()) + sqliteDatabase = it2->second; + else { + sqliteDatabase = openSQLiteDatabase(securityOrigin.get(), dataDir, maximumSize, fileIdentifier, this); + + if (!sqliteDatabase || !createTables(sqliteDatabase->db()) || !migrateDatabase(sqliteDatabase->db())) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error.")); + m_sqliteDatabaseMap.set(fileIdentifier, 0); + return; + } + m_sqliteDatabaseMap.set(fileIdentifier, sqliteDatabase.get()); + } + + RefPtr<IDBDatabaseBackendImpl> databaseBackend = IDBDatabaseBackendImpl::create(name, sqliteDatabase.get(), m_transactionCoordinator.get(), this, uniqueIdentifier); + callbacks->onSuccess(databaseBackend.get()); + m_databaseBackendMap.set(uniqueIdentifier, databaseBackend.get()); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBFactoryBackendImpl.h b/Source/WebCore/storage/IDBFactoryBackendImpl.h new file mode 100644 index 0000000..dcaf848 --- /dev/null +++ b/Source/WebCore/storage/IDBFactoryBackendImpl.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef IDBFactoryBackendImpl_h +#define IDBFactoryBackendImpl_h + +#include "IDBFactoryBackendInterface.h" +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class DOMStringList; + +class IDBDatabaseBackendImpl; +class IDBSQLiteDatabase; +class IDBTransactionCoordinator; + +class IDBFactoryBackendImpl : public IDBFactoryBackendInterface { +public: + static PassRefPtr<IDBFactoryBackendImpl> create() + { + return adoptRef(new IDBFactoryBackendImpl()); + } + virtual ~IDBFactoryBackendImpl(); + + // Notifications from weak pointers. + void removeIDBDatabaseBackend(const String& uniqueIdentifier); + void removeSQLiteDatabase(const String& uniqueIdentifier); + + virtual void open(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, Frame*, const String& dataDir, int64_t maximumSize); + +private: + IDBFactoryBackendImpl(); + + typedef HashMap<String, IDBDatabaseBackendImpl*> IDBDatabaseBackendMap; + IDBDatabaseBackendMap m_databaseBackendMap; + + typedef HashMap<String, IDBSQLiteDatabase*> SQLiteDatabaseMap; + SQLiteDatabaseMap m_sqliteDatabaseMap; + + RefPtr<IDBTransactionCoordinator> m_transactionCoordinator; + + // Only one instance of the factory should exist at any given time. + static IDBFactoryBackendImpl* idbFactoryBackendImpl; +}; + +} // namespace WebCore + +#endif + +#endif // IDBFactoryBackendImpl_h + diff --git a/Source/WebCore/storage/IDBFactoryBackendInterface.cpp b/Source/WebCore/storage/IDBFactoryBackendInterface.cpp new file mode 100644 index 0000000..935ccac --- /dev/null +++ b/Source/WebCore/storage/IDBFactoryBackendInterface.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBFactoryBackendInterface.h" + +#include "IDBFactoryBackendImpl.h" + +#if PLATFORM(CHROMIUM) +#error "Chromium should not compile this file and instead define its own version of this factory that navigates the multi-process boundry." +#endif + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +PassRefPtr<IDBFactoryBackendInterface> IDBFactoryBackendInterface::create() +{ + return IDBFactoryBackendImpl::create(); +} + +IDBFactoryBackendInterface::~IDBFactoryBackendInterface() +{ +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBFactoryBackendInterface.h b/Source/WebCore/storage/IDBFactoryBackendInterface.h new file mode 100644 index 0000000..166d517 --- /dev/null +++ b/Source/WebCore/storage/IDBFactoryBackendInterface.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef IDBFactoryBackendInterface_h +#define IDBFactoryBackendInterface_h + +#include "ExceptionCode.h" +#include "IDBCallbacks.h" +#include "PlatformString.h" +#include <wtf/Threading.h> +#include <wtf/Vector.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class Frame; +class IDBDatabase; +class SecurityOrigin; + +// This class is shared by IDBFactory (async) and IDBFactorySync (sync). +// This is implemented by IDBFactoryBackendImpl and optionally others (in order to proxy +// calls across process barriers). All calls to these classes should be non-blocking and +// trigger work on a background thread if necessary. +class IDBFactoryBackendInterface : public ThreadSafeShared<IDBFactoryBackendInterface> { +public: + static PassRefPtr<IDBFactoryBackendInterface> create(); + virtual ~IDBFactoryBackendInterface(); + + virtual void open(const String& name, PassRefPtr<IDBCallbacks>, PassRefPtr<SecurityOrigin>, Frame*, const String& dataDir, int64_t maximumSize) = 0; +}; + +} // namespace WebCore + +#endif + +#endif // IDBFactoryBackendInterface_h diff --git a/Source/WebCore/storage/IDBIndex.cpp b/Source/WebCore/storage/IDBIndex.cpp new file mode 100644 index 0000000..8a38a27 --- /dev/null +++ b/Source/WebCore/storage/IDBIndex.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBIndex.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBCursorBackendInterface.h" +#include "IDBDatabaseException.h" +#include "IDBIndexBackendInterface.h" +#include "IDBKey.h" +#include "IDBKeyRange.h" +#include "IDBRequest.h" +#include "IDBTransactionBackendInterface.h" + +namespace WebCore { + +static const unsigned short defaultDirection = IDBCursor::NEXT; + +IDBIndex::IDBIndex(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransactionBackendInterface* transaction) + : m_backend(backend) + , m_transaction(transaction) +{ + ASSERT(m_backend); + ASSERT(m_transaction); +} + +IDBIndex::~IDBIndex() +{ +} + +PassRefPtr<IDBRequest> IDBIndex::openCursor(ScriptExecutionContext* context, const OptionsObject& options, ExceptionCode& ec) +{ + RefPtr<IDBKeyRange> keyRange = options.getKeyKeyRange("range"); + + // Converted to an unsigned short. + int32_t direction = defaultDirection; + options.getKeyInt32("direction", direction); + if (direction != IDBCursor::NEXT && direction != IDBCursor::NEXT_NO_DUPLICATE && direction != IDBCursor::PREV && direction != IDBCursor::PREV_NO_DUPLICATE) { + // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 + ec = IDBDatabaseException::CONSTRAINT_ERR; + return 0; + } + + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_backend->openCursor(keyRange, direction, request, m_transaction.get(), ec); + if (ec) + return 0; + return request; +} + +PassRefPtr<IDBRequest> IDBIndex::openKeyCursor(ScriptExecutionContext* context, const OptionsObject& options, ExceptionCode& ec) +{ + RefPtr<IDBKeyRange> keyRange = options.getKeyKeyRange("range"); + + // Converted to an unsigned short. + int32_t direction = defaultDirection; + options.getKeyInt32("direction", direction); + if (direction != IDBCursor::NEXT && direction != IDBCursor::NEXT_NO_DUPLICATE && direction != IDBCursor::PREV && direction != IDBCursor::PREV_NO_DUPLICATE) { + // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 + ec = IDBDatabaseException::CONSTRAINT_ERR; + return 0; + } + + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_backend->openKeyCursor(keyRange, direction, request, m_transaction.get(), ec); + if (ec) + return 0; + return request; +} + +PassRefPtr<IDBRequest> IDBIndex::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_backend->get(key, request, m_transaction.get(), ec); + if (ec) + return 0; + return request; +} + +PassRefPtr<IDBRequest> IDBIndex::getKey(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_backend->getKey(key, request, m_transaction.get(), ec); + if (ec) + return 0; + return request; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBIndex.h b/Source/WebCore/storage/IDBIndex.h new file mode 100644 index 0000000..7f1aae3 --- /dev/null +++ b/Source/WebCore/storage/IDBIndex.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBIndex_h +#define IDBIndex_h + +#include "IDBCursor.h" +#include "IDBIndexBackendInterface.h" +#include "IDBKeyRange.h" +#include "IDBRequest.h" +#include "OptionsObject.h" +#include "PlatformString.h" +#include <wtf/Forward.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBIndex : public RefCounted<IDBIndex> { +public: + static PassRefPtr<IDBIndex> create(PassRefPtr<IDBIndexBackendInterface> backend, IDBTransactionBackendInterface* transaction) + { + return adoptRef(new IDBIndex(backend, transaction)); + } + ~IDBIndex(); + + // Implement the IDL + String name() const { return m_backend->name(); } + String storeName() const { return m_backend->storeName(); } + String keyPath() const { return m_backend->keyPath(); } + bool unique() const { return m_backend->unique(); } + + // FIXME: Try to modify the code generator so this is unneeded. + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, OptionsObject(), ec); } + PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openKeyCursor(context, OptionsObject(), ec); } + + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const OptionsObject&, ExceptionCode&); + PassRefPtr<IDBRequest> openKeyCursor(ScriptExecutionContext*, const OptionsObject&, ExceptionCode&); + PassRefPtr<IDBRequest> get(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&); + PassRefPtr<IDBRequest> getKey(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&); + +private: + IDBIndex(PassRefPtr<IDBIndexBackendInterface>, IDBTransactionBackendInterface* transaction); + + RefPtr<IDBIndexBackendInterface> m_backend; + RefPtr<IDBTransactionBackendInterface> m_transaction; +}; + +} // namespace WebCore + +#endif + +#endif // IDBIndex_h diff --git a/Source/WebCore/storage/IDBIndex.idl b/Source/WebCore/storage/IDBIndex.idl new file mode 100644 index 0000000..e6ba1e6 --- /dev/null +++ b/Source/WebCore/storage/IDBIndex.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBIndex { + readonly attribute DOMString name; + readonly attribute DOMString storeName; + readonly attribute DOMString keyPath; + readonly attribute boolean unique; + + [CallWith=ScriptExecutionContext] IDBRequest openCursor(in [Optional] OptionsObject options) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest openKeyCursor(in [Optional] OptionsObject options) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest get(in IDBKey key) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest getKey(in IDBKey key) + raises (IDBDatabaseException); + }; + +} diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.cpp b/Source/WebCore/storage/IDBIndexBackendImpl.cpp new file mode 100644 index 0000000..df88fdb --- /dev/null +++ b/Source/WebCore/storage/IDBIndexBackendImpl.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBIndexBackendImpl.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "CrossThreadTask.h" +#include "IDBCallbacks.h" +#include "IDBCursorBackendImpl.h" +#include "IDBDatabaseBackendImpl.h" +#include "IDBDatabaseException.h" +#include "IDBKey.h" +#include "IDBKeyRange.h" +#include "IDBObjectStoreBackendImpl.h" +#include "IDBSQLiteDatabase.h" +#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" + +namespace WebCore { + +IDBIndexBackendImpl::IDBIndexBackendImpl(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) + : m_database(database) + , m_id(id) + , m_name(name) + , m_storeName(storeName) + , m_keyPath(keyPath) + , m_unique(unique) +{ +} + +IDBIndexBackendImpl::IDBIndexBackendImpl(IDBSQLiteDatabase* database, const String& name, const String& storeName, const String& keyPath, bool unique) + : m_database(database) + , m_id(InvalidId) + , m_name(name) + , m_storeName(storeName) + , m_keyPath(keyPath) + , m_unique(unique) +{ +} + +IDBIndexBackendImpl::~IDBIndexBackendImpl() +{ +} + +void IDBIndexBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKeyRange> range, unsigned short untypedDirection, bool objectCursor, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + // Several files depend on this order of selects. + String sql = String("SELECT IndexData.id, IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, ") + + ("ObjectStoreData.value, ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ") + + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id WHERE "; + + bool lowerBound = range && range->lower(); + bool upperBound = range && range->upper(); + + if (lowerBound) + sql += range->lower()->lowerCursorWhereFragment(range->lowerWhereClauseComparisonOperator(), "IndexData."); + if (upperBound) + sql += range->upper()->upperCursorWhereFragment(range->upperWhereClauseComparisonOperator(), "IndexData."); + sql += "IndexData.indexId = ? ORDER BY "; + + IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(untypedDirection); + if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE) + sql += "IndexData.keyString, IndexData.keyDate, IndexData.keyNumber, IndexData.id"; + else + sql += "IndexData.keyString DESC, IndexData.keyDate DESC, IndexData.keyNumber DESC, IndexData.id DESC"; + + OwnPtr<SQLiteStatement> query = adoptPtr(new SQLiteStatement(index->sqliteDatabase(), sql)); + bool ok = query->prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + int indexColumn = 1; + if (lowerBound) + indexColumn += range->lower()->bind(*query, indexColumn); + if (upperBound) + indexColumn += range->upper()->bind(*query, indexColumn); + query->bindInt64(indexColumn, index->id()); + + if (query->step() != SQLResultRow) { + callbacks->onSuccess(); + return; + } + + ExceptionCode ec = 0; + RefPtr<IDBObjectStoreBackendInterface> objectStore = transaction->objectStore(index->m_storeName, ec); + ASSERT(objectStore && !ec); + + RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(index->m_database.get(), range, direction, query.release(), objectCursor, transaction.get(), objectStore.get()); + callbacks->onSuccess(cursor.release()); +} + +void IDBIndexBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpKeyRange, unsigned short direction, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) +{ + RefPtr<IDBIndexBackendImpl> index = this; + RefPtr<IDBKeyRange> keyRange = prpKeyRange; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; + if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, true, callbacks, transaction))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBIndexBackendImpl::openKeyCursor(PassRefPtr<IDBKeyRange> prpKeyRange, unsigned short direction, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) +{ + RefPtr<IDBIndexBackendImpl> index = this; + RefPtr<IDBKeyRange> keyRange = prpKeyRange; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; + if (!transaction->scheduleTask(createCallbackTask(&openCursorInternal, index, keyRange, direction, false, callbacks, transaction))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBIndexBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBKey> key, bool getObject, PassRefPtr<IDBCallbacks> callbacks) +{ + String sql = String("SELECT ") + + (getObject ? "ObjectStoreData.value " : "ObjectStoreData.keyString, ObjectStoreData.keyDate, ObjectStoreData.keyNumber ") + + "FROM IndexData INNER JOIN ObjectStoreData ON IndexData.objectStoreDataId = ObjectStoreData.id " + + "WHERE IndexData.indexId = ? AND " + key->whereSyntax("IndexData.") + + "ORDER BY IndexData.id LIMIT 1"; // Order by insertion order when all else fails. + SQLiteStatement query(index->sqliteDatabase(), sql); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + query.bindInt64(1, index->id()); + key->bind(query, 2); + if (query.step() != SQLResultRow) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the index.")); + return; + } + + if (getObject) + callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnText(0))); + else + callbacks->onSuccess(IDBKey::fromQuery(query, 0)); + ASSERT(query.step() != SQLResultRow); +} + +void IDBIndexBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) +{ + RefPtr<IDBIndexBackendImpl> index = this; + RefPtr<IDBKey> key = prpKey; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + if (!transaction->scheduleTask(createCallbackTask(&getInternal, index, key, true, callbacks))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBIndexBackendImpl::getKey(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) +{ + RefPtr<IDBIndexBackendImpl> index = this; + RefPtr<IDBKey> key = prpKey; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + if (!transaction->scheduleTask(createCallbackTask(&getInternal, index, key, false, callbacks))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +static String whereClause(IDBKey* key) +{ + return "WHERE indexId = ? AND " + key->whereSyntax(); +} + +static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key) +{ + query.bindInt64(1, id); + key->bind(query, 2); +} + +bool IDBIndexBackendImpl::addingKeyAllowed(IDBKey* key) +{ + if (!m_unique) + return true; + + SQLiteStatement query(sqliteDatabase(), "SELECT id FROM IndexData " + whereClause(key)); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + bindWhereClause(query, m_id, key); + bool existingValue = query.step() == SQLResultRow; + + return !existingValue; +} + +SQLiteDatabase& IDBIndexBackendImpl::sqliteDatabase() const +{ + return m_database->db(); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBIndexBackendImpl.h b/Source/WebCore/storage/IDBIndexBackendImpl.h new file mode 100644 index 0000000..e640b5a --- /dev/null +++ b/Source/WebCore/storage/IDBIndexBackendImpl.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBIndexBackendImpl_h +#define IDBIndexBackendImpl_h + +#include "IDBIndexBackendInterface.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBKey; +class IDBObjectStoreBackendImpl; +class IDBSQLiteDatabase; +class SQLiteDatabase; +class ScriptExecutionContext; + +class IDBIndexBackendImpl : public IDBIndexBackendInterface { +public: + static PassRefPtr<IDBIndexBackendImpl> create(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique) + { + return adoptRef(new IDBIndexBackendImpl(database, id, name, storeName, keyPath, unique)); + } + static PassRefPtr<IDBIndexBackendImpl> create(IDBSQLiteDatabase* database, const String& name, const String& storeName, const String& keyPath, bool unique) + { + return adoptRef(new IDBIndexBackendImpl(database, name, storeName, keyPath, unique)); + } + virtual ~IDBIndexBackendImpl(); + + int64_t id() const + { + ASSERT(m_id != InvalidId); + return m_id; + } + void setId(int64_t id) { m_id = id; } + + bool addingKeyAllowed(IDBKey*); + + // Implements IDBIndexBackendInterface. + virtual String name() { return m_name; } + virtual String storeName() { return m_storeName; } + virtual String keyPath() { return m_keyPath; } + virtual bool unique() { return m_unique; } + + virtual void openCursor(PassRefPtr<IDBKeyRange>, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void openKeyCursor(PassRefPtr<IDBKeyRange>, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void get(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void getKey(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + +private: + IDBIndexBackendImpl(IDBSQLiteDatabase*, int64_t id, const String& name, const String& storeName, const String& keyPath, bool unique); + IDBIndexBackendImpl(IDBSQLiteDatabase*, const String& name, const String& storeName, const String& keyPath, bool unique); + + SQLiteDatabase& sqliteDatabase() const; + + static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKeyRange>, unsigned short direction, bool objectCursor, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); + static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBKey>, bool getObject, PassRefPtr<IDBCallbacks>); + + static const int64_t InvalidId = 0; + + RefPtr<IDBSQLiteDatabase> m_database; + + int64_t m_id; + String m_name; + String m_storeName; + String m_keyPath; + bool m_unique; +}; + +} // namespace WebCore + +#endif + +#endif // IDBIndexBackendImpl_h diff --git a/Source/WebCore/storage/IDBIndexBackendInterface.h b/Source/WebCore/storage/IDBIndexBackendInterface.h new file mode 100644 index 0000000..e0e578d --- /dev/null +++ b/Source/WebCore/storage/IDBIndexBackendInterface.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBIndexBackendInterface_h +#define IDBIndexBackendInterface_h + +#include "ExceptionCode.h" +#include "PlatformString.h" +#include <wtf/Forward.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBCallbacks; +class IDBKey; +class IDBKeyRange; +class IDBTransactionBackendInterface; + +class IDBIndexBackendInterface : public ThreadSafeShared<IDBIndexBackendInterface> { +public: + virtual ~IDBIndexBackendInterface() { } + + virtual String name() = 0; + virtual String storeName() = 0; + virtual String keyPath() = 0; + virtual bool unique() = 0; + + virtual void openCursor(PassRefPtr<IDBKeyRange>, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual void openKeyCursor(PassRefPtr<IDBKeyRange>, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual void get(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual void getKey(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; +}; + +} // namespace WebCore + +#endif + +#endif // IDBIndexBackendInterface_h diff --git a/Source/WebCore/storage/IDBKey.cpp b/Source/WebCore/storage/IDBKey.cpp new file mode 100644 index 0000000..5f45543 --- /dev/null +++ b/Source/WebCore/storage/IDBKey.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBKey.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "SQLiteStatement.h" +#include "SerializedScriptValue.h" + +namespace WebCore { + +IDBKey::IDBKey() + : m_type(NullType) +{ +} + +IDBKey::~IDBKey() +{ +} + +PassRefPtr<IDBKey> IDBKey::fromQuery(SQLiteStatement& query, int baseColumn) +{ + if (query.columnCount() <= baseColumn) + return 0; + + if (!query.isColumnNull(baseColumn)) + return IDBKey::createString(query.getColumnText(baseColumn)); + + if (!query.isColumnNull(baseColumn + 1)) + return IDBKey::createDate(query.getColumnDouble(baseColumn + 1)); + + if (!query.isColumnNull(baseColumn + 2)) + return IDBKey::createNumber(query.getColumnDouble(baseColumn + 2)); + + return IDBKey::createNull(); +} + +bool IDBKey::isEqual(IDBKey* other) +{ + if (!other || other->m_type != m_type) + return false; + + switch (m_type) { + case StringType: + return other->m_string == m_string; + case DateType: + return other->m_date == m_date; + case NumberType: + return other->m_number == m_number; + case NullType: + return true; + } + + ASSERT_NOT_REACHED(); + return false; +} + +String IDBKey::whereSyntax(String qualifiedTableName) const +{ + switch (m_type) { + case IDBKey::StringType: + return qualifiedTableName + "keyString = ? AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL "; + case IDBKey::NumberType: + return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber = ? "; + case IDBKey::DateType: + return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate = ? AND " + qualifiedTableName + "keyNumber IS NULL "; + case IDBKey::NullType: + return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL "; + } + + ASSERT_NOT_REACHED(); + return ""; +} + +String IDBKey::lowerCursorWhereFragment(String comparisonOperator, String qualifiedTableName) +{ + switch (m_type) { + case StringType: + return "? " + comparisonOperator + " " + qualifiedTableName + "keyString AND "; + case DateType: + return "(? " + comparisonOperator + " " + qualifiedTableName + "keyDate OR NOT " + qualifiedTableName + "keyString IS NULL) AND "; + case NumberType: + return "(? " + comparisonOperator + " " + qualifiedTableName + "keyNumber OR NOT " + qualifiedTableName + "keyString IS NULL OR NOT " + qualifiedTableName + "keyDate IS NULL) AND "; + case NullType: + if (comparisonOperator == "<") + return "NOT(" + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL) AND "; + return ""; // If it's =, the upper bound half will do the constraining. If it's <=, then that's a no-op. + } + ASSERT_NOT_REACHED(); + return ""; +} + +String IDBKey::upperCursorWhereFragment(String comparisonOperator, String qualifiedTableName) +{ + switch (m_type) { + case StringType: + return "(" + qualifiedTableName + "keyString " + comparisonOperator + " ? OR " + qualifiedTableName + "keyString IS NULL) AND "; + case DateType: + return "(" + qualifiedTableName + "keyDate " + comparisonOperator + " ? OR " + qualifiedTableName + "keyDate IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND "; + case NumberType: + return "(" + qualifiedTableName + "keyNumber " + comparisonOperator + " ? OR " + qualifiedTableName + "keyNumber IS NULL) AND " + qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND "; + case NullType: + if (comparisonOperator == "<") + return "0 != 0 AND "; + return qualifiedTableName + "keyString IS NULL AND " + qualifiedTableName + "keyDate IS NULL AND " + qualifiedTableName + "keyNumber IS NULL AND "; + } + ASSERT_NOT_REACHED(); + return ""; +} + +// Returns the number of items bound. +int IDBKey::bind(SQLiteStatement& query, int column) const +{ + switch (m_type) { + case IDBKey::StringType: + query.bindText(column, m_string); + return 1; + case IDBKey::DateType: + query.bindDouble(column, m_date); + return 1; + case IDBKey::NumberType: + query.bindDouble(column, m_number); + return 1; + case IDBKey::NullType: + return 0; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +void IDBKey::bindWithNulls(SQLiteStatement& query, int baseColumn) const +{ + switch (m_type) { + case IDBKey::StringType: + query.bindText(baseColumn + 0, m_string); + query.bindNull(baseColumn + 1); + query.bindNull(baseColumn + 2); + break; + case IDBKey::DateType: + query.bindNull(baseColumn + 0); + query.bindDouble(baseColumn + 1, m_date); + query.bindNull(baseColumn + 2); + break; + case IDBKey::NumberType: + query.bindNull(baseColumn + 0); + query.bindNull(baseColumn + 1); + query.bindDouble(baseColumn + 2, m_number); + break; + case IDBKey::NullType: + query.bindNull(baseColumn + 0); + query.bindNull(baseColumn + 1); + query.bindNull(baseColumn + 2); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBKey.h b/Source/WebCore/storage/IDBKey.h new file mode 100644 index 0000000..9118743 --- /dev/null +++ b/Source/WebCore/storage/IDBKey.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBKey_h +#define IDBKey_h + +#if ENABLE(INDEXED_DATABASE) + +#include "PlatformString.h" +#include <wtf/Forward.h> +#include <wtf/Threading.h> + +namespace WebCore { + +class SQLiteStatement; + +class IDBKey : public ThreadSafeShared<IDBKey> { +public: + static PassRefPtr<IDBKey> createNull() + { + RefPtr<IDBKey> idbKey(new IDBKey()); + idbKey->m_type = NullType; + return idbKey.release(); + } + + static PassRefPtr<IDBKey> createNumber(double number) + { + RefPtr<IDBKey> idbKey(new IDBKey()); + idbKey->m_type = NumberType; + idbKey->m_number = number; + return idbKey.release(); + } + + static PassRefPtr<IDBKey> createString(const String& string) + { + RefPtr<IDBKey> idbKey(new IDBKey()); + idbKey->m_type = StringType; + idbKey->m_string = string; + return idbKey.release(); + } + + static PassRefPtr<IDBKey> createDate(double date) + { + RefPtr<IDBKey> idbKey(new IDBKey()); + idbKey->m_type = DateType; + idbKey->m_date = date; + return idbKey.release(); + } + + ~IDBKey(); + + // In order of the least to the highest precedent in terms of sort order. + enum Type { + NullType = 0, // FIXME: Phase out support for null keys. + StringType, + DateType, + NumberType + }; + + Type type() const { return m_type; } + + const String& string() const + { + ASSERT(m_type == StringType); + return m_string; + } + + double date() const + { + ASSERT(m_type == DateType); + return m_date; + } + + double number() const + { + ASSERT(m_type == NumberType); + return m_number; + } + + static PassRefPtr<IDBKey> fromQuery(SQLiteStatement& query, int baseColumn); + + bool isEqual(IDBKey* other); + String whereSyntax(String qualifiedTableName = "") const; + String lowerCursorWhereFragment(String comparisonOperator, String qualifiedTableName = ""); + String upperCursorWhereFragment(String comparisonOperator, String qualifiedTableName = ""); + int bind(SQLiteStatement& query, int column) const; + void bindWithNulls(SQLiteStatement& query, int baseColumn) const; + + using ThreadSafeShared<IDBKey>::ref; + using ThreadSafeShared<IDBKey>::deref; + +private: + IDBKey(); + + Type m_type; + String m_string; + double m_date; + double m_number; +}; + +} + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBKey_h diff --git a/Source/WebCore/storage/IDBKey.idl b/Source/WebCore/storage/IDBKey.idl new file mode 100644 index 0000000..04995f3 --- /dev/null +++ b/Source/WebCore/storage/IDBKey.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE, + CustomToJS + ] IDBKey { + // This space is intentionally left blank. + }; +} diff --git a/Source/WebCore/storage/IDBKeyPath.cpp b/Source/WebCore/storage/IDBKeyPath.cpp new file mode 100644 index 0000000..8833da0 --- /dev/null +++ b/Source/WebCore/storage/IDBKeyPath.cpp @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBKeyPath.h" + +#if ENABLE(INDEXED_DATABASE) + +#include <wtf/ASCIICType.h> +#include <wtf/dtoa.h> + +namespace WebCore { + +class IDBKeyPathLexer { +public: + enum TokenType { + TokenLeftBracket, + TokenRightBracket, + TokenIdentifier, + TokenNumber, + TokenDot, + TokenEnd, + TokenError + }; + + explicit IDBKeyPathLexer(const String& s) + : m_string(s) + , m_ptr(s.characters()) + , m_end(s.characters() + s.length()) + , m_currentTokenType(TokenError) + { + } + + TokenType currentTokenType() const { return m_currentTokenType; } + + TokenType nextTokenType() + { + m_currentTokenType = lex(m_currentElement); + return m_currentTokenType; + } + + const IDBKeyPathElement& currentElement() { return m_currentElement; } + +private: + TokenType lex(IDBKeyPathElement&); + TokenType lexIdentifier(IDBKeyPathElement&); + TokenType lexNumber(IDBKeyPathElement&); + IDBKeyPathElement m_currentElement; + String m_string; + const UChar* m_ptr; + const UChar* m_end; + TokenType m_currentTokenType; +}; + +IDBKeyPathLexer::TokenType IDBKeyPathLexer::lex(IDBKeyPathElement& element) +{ + while (m_ptr < m_end && isASCIISpace(*m_ptr)) + ++m_ptr; + + if (m_ptr >= m_end) + return TokenEnd; + + ASSERT(m_ptr < m_end); + switch (*m_ptr) { + case '[': + ++m_ptr; + return TokenLeftBracket; + case ']': + ++m_ptr; + return TokenRightBracket; + case '.': + ++m_ptr; + return TokenDot; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return lexNumber(element); + default: + return lexIdentifier(element); + } + return TokenError; +} + +static inline bool isSafeIdentifierStartCharacter(UChar c) +{ + return isASCIIAlpha(c) || (c == '_') || (c == '$'); +} + +static inline bool isSafeIdentifierCharacter(UChar c) +{ + return isASCIIAlphanumeric(c) || (c == '_') || (c == '$'); +} + +IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexIdentifier(IDBKeyPathElement& element) +{ + const UChar* start = m_ptr; + if (m_ptr < m_end && isSafeIdentifierStartCharacter(*m_ptr)) + ++m_ptr; + else + return TokenError; + + while (m_ptr < m_end && isSafeIdentifierCharacter(*m_ptr)) + ++m_ptr; + + element.type = IDBKeyPathElement::IsNamed; + element.identifier = String(start, m_ptr - start); + return TokenIdentifier; +} + +IDBKeyPathLexer::TokenType IDBKeyPathLexer::lexNumber(IDBKeyPathElement& element) +{ + if (m_ptr >= m_end) + return TokenError; + + const UChar* start = m_ptr; + // [0-9]* + while (m_ptr < m_end && isASCIIDigit(*m_ptr)) + ++m_ptr; + + String numberAsString; + numberAsString = String(start, m_ptr - start); + bool ok = false; + unsigned number = numberAsString.toUIntStrict(&ok); + if (!ok) + return TokenError; + + element.type = IDBKeyPathElement::IsIndexed; + element.index = number; + return TokenNumber; +} + +void IDBParseKeyPath(const String& keyPath, Vector<IDBKeyPathElement>& elements, IDBKeyPathParseError& error) +{ + // This is a simplified parser loosely based on LiteralParser. + // An IDBKeyPath is defined as a sequence of: + // identifierA{.identifierB{[numeric_value]} + // where "{}" represents an optional part + // The basic state machine is: + // Start => {Identifier, Array} + // Identifier => {Dot, Array, End} + // Array => {Start, Dot, End} + // Dot => {Identifier} + // It bails out as soon as it finds an error, but doesn't discard the bits it managed to parse. + enum ParserState { Identifier, Array, Dot, End }; + + IDBKeyPathLexer lexer(keyPath); + IDBKeyPathLexer::TokenType tokenType = lexer.nextTokenType(); + ParserState state; + if (tokenType == IDBKeyPathLexer::TokenIdentifier) + state = Identifier; + else if (tokenType == IDBKeyPathLexer::TokenLeftBracket) + state = Array; + else if (tokenType == IDBKeyPathLexer::TokenEnd) + state = End; + else { + error = IDBKeyPathParseErrorStart; + return; + } + + while (1) { + switch (state) { + case Identifier : { + IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType(); + ASSERT(tokenType == IDBKeyPathLexer::TokenIdentifier); + + IDBKeyPathElement element = lexer.currentElement(); + ASSERT(element.type == IDBKeyPathElement::IsNamed); + elements.append(element); + + tokenType = lexer.nextTokenType(); + if (tokenType == IDBKeyPathLexer::TokenDot) + state = Dot; + else if (tokenType == IDBKeyPathLexer::TokenLeftBracket) + state = Array; + else if (tokenType == IDBKeyPathLexer::TokenEnd) + state = End; + else { + error = IDBKeyPathParseErrorIdentifier; + return; + } + break; + } + case Array : { + IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType(); + ASSERT(tokenType == IDBKeyPathLexer::TokenLeftBracket); + + tokenType = lexer.nextTokenType(); + if (tokenType != IDBKeyPathLexer::TokenNumber) { + error = IDBKeyPathParseErrorArrayIndex; + return; + } + + ASSERT(tokenType == IDBKeyPathLexer::TokenNumber); + IDBKeyPathElement element = lexer.currentElement(); + ASSERT(element.type == IDBKeyPathElement::IsIndexed); + elements.append(element); + + tokenType = lexer.nextTokenType(); + if (tokenType != IDBKeyPathLexer::TokenRightBracket) { + error = IDBKeyPathParseErrorArrayIndex; + return; + } + + tokenType = lexer.nextTokenType(); + if (tokenType == IDBKeyPathLexer::TokenDot) + state = Dot; + else if (tokenType == IDBKeyPathLexer::TokenLeftBracket) + state = Array; + else if (tokenType == IDBKeyPathLexer::TokenEnd) + state = End; + else { + error = IDBKeyPathParseErrorAfterArray; + return; + } + break; + } + case Dot: { + IDBKeyPathLexer::TokenType tokenType = lexer.currentTokenType(); + ASSERT(tokenType == IDBKeyPathLexer::TokenDot); + + tokenType = lexer.nextTokenType(); + if (tokenType != IDBKeyPathLexer::TokenIdentifier) { + error = IDBKeyPathParseErrorDot; + return; + } + + state = Identifier; + break; + } + case End: { + error = IDBKeyPathParseErrorNone; + return; + } + } + } +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBKeyPath.h b/Source/WebCore/storage/IDBKeyPath.h new file mode 100644 index 0000000..7787980 --- /dev/null +++ b/Source/WebCore/storage/IDBKeyPath.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBKeyPath_h +#define IDBKeyPath_h + +#if ENABLE(INDEXED_DATABASE) + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +struct IDBKeyPathElement { + enum Type { + IsIndexed, + IsNamed, + }; + + Type type; + unsigned index; + String identifier; +}; + +enum IDBKeyPathParseError { + IDBKeyPathParseErrorNone, + IDBKeyPathParseErrorStart, + IDBKeyPathParseErrorIdentifier, + IDBKeyPathParseErrorArrayIndex, + IDBKeyPathParseErrorAfterArray, + IDBKeyPathParseErrorDot, +}; + +void IDBParseKeyPath(const String&, Vector<IDBKeyPathElement>&, IDBKeyPathParseError&); + +} // namespace WebCore + +#endif + +#endif // IDBKeyPath_h diff --git a/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp b/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp new file mode 100644 index 0000000..b7c45c3 --- /dev/null +++ b/Source/WebCore/storage/IDBKeyPathBackendImpl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBKeyPathBackendImpl.h" + +#if PLATFORM(CHROMIUM) +#error "Chromium should not compile this file and instead define its own version of this factory that navigates the multi-process boundry." +#endif + +#if ENABLE(INDEXED_DATABASE) + +void IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(const Vector<RefPtr<SerializedScriptValue>&, 0> values, const String& keyPath, Vector<RefPtr<IDBKey>, 0>& keys) +{ + // FIXME: Implement this method once JSC supports WireFormat for SerializedScriptValue. +} + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBKeyPathBackendImpl.h b/Source/WebCore/storage/IDBKeyPathBackendImpl.h new file mode 100644 index 0000000..32af5e3 --- /dev/null +++ b/Source/WebCore/storage/IDBKeyPathBackendImpl.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBKeyPathBackendImpl_h +#define IDBKeyPathBackendImpl_h + +#if ENABLE(INDEXED_DATABASE) + +#include <wtf/Forward.h> + +namespace WebCore { + +class IDBKey; +class SerializedScriptValue; + +class IDBKeyPathBackendImpl { +public: + static void createIDBKeysFromSerializedValuesAndKeyPath(const Vector<RefPtr<SerializedScriptValue>, 0>& values, const String& keyPath, Vector<RefPtr<IDBKey>, 0>& keys); +}; + +} + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBKeyPathBackendImpl_h diff --git a/Source/WebCore/storage/IDBKeyRange.cpp b/Source/WebCore/storage/IDBKeyRange.cpp new file mode 100644 index 0000000..142b3bd --- /dev/null +++ b/Source/WebCore/storage/IDBKeyRange.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBKeyRange.h" + +#include "IDBKey.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +IDBKeyRange::IDBKeyRange(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen, bool upperOpen) + : m_lower(lower) + , m_upper(upper) + , m_lowerOpen(lowerOpen) + , m_upperOpen(upperOpen) +{ +} + +PassRefPtr<IDBKeyRange> IDBKeyRange::only(PassRefPtr<IDBKey> prpValue) +{ + RefPtr<IDBKey> value = prpValue; + return IDBKeyRange::create(value, value, false, false); +} + +PassRefPtr<IDBKeyRange> IDBKeyRange::lowerBound(PassRefPtr<IDBKey> bound, bool open) +{ + return IDBKeyRange::create(bound, 0, open, false); +} + +PassRefPtr<IDBKeyRange> IDBKeyRange::upperBound(PassRefPtr<IDBKey> bound, bool open) +{ + return IDBKeyRange::create(0, bound, false, open); +} + +PassRefPtr<IDBKeyRange> IDBKeyRange::bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, const OptionsObject& options) +{ + bool lowerOpen = false; + bool upperOpen = false; + options.getKeyBool("lowerOpen", lowerOpen); + options.getKeyBool("upperOpen", upperOpen); + return IDBKeyRange::create(lower, upper, lowerOpen, upperOpen); +} + +String IDBKeyRange::lowerWhereClauseComparisonOperator() const +{ + ASSERT(m_lower); + if (m_lowerOpen) + return "<"; + return "<="; +} + +String IDBKeyRange::upperWhereClauseComparisonOperator() const +{ + ASSERT(m_upper); + if (m_upperOpen) + return "<"; + return "<="; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBKeyRange.h b/Source/WebCore/storage/IDBKeyRange.h new file mode 100644 index 0000000..8af48fe --- /dev/null +++ b/Source/WebCore/storage/IDBKeyRange.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBKeyRange_h +#define IDBKeyRange_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKey.h" +#include "OptionsObject.h" +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + +class IDBKeyRange : public ThreadSafeShared<IDBKeyRange> { +public: + static PassRefPtr<IDBKeyRange> create(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen, bool upperOpen) + { + return adoptRef(new IDBKeyRange(lower, upper, lowerOpen, upperOpen)); + } + ~IDBKeyRange() { } + + + PassRefPtr<IDBKey> lower() const { return m_lower; } + PassRefPtr<IDBKey> upper() const { return m_upper; } + bool lowerOpen() const { return m_lowerOpen; } + bool upperOpen() const { return m_upperOpen; } + + String lowerWhereClauseComparisonOperator() const; + String upperWhereClauseComparisonOperator() const; + + static PassRefPtr<IDBKeyRange> only(PassRefPtr<IDBKey> value); + static PassRefPtr<IDBKeyRange> lowerBound(PassRefPtr<IDBKey> bound, bool open = false); + static PassRefPtr<IDBKeyRange> upperBound(PassRefPtr<IDBKey> bound, bool open = false); + static PassRefPtr<IDBKeyRange> bound(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, const OptionsObject& = OptionsObject()); + +private: + IDBKeyRange(PassRefPtr<IDBKey> lower, PassRefPtr<IDBKey> upper, bool lowerOpen, bool upperOpen); + + RefPtr<IDBKey> m_lower; + RefPtr<IDBKey> m_upper; + bool m_lowerOpen; + bool m_upperOpen; +}; + +} // namespace WebCore + +#endif + +#endif // IDBKeyRange_h diff --git a/Source/WebCore/storage/IDBKeyRange.idl b/Source/WebCore/storage/IDBKeyRange.idl new file mode 100644 index 0000000..d7fa075 --- /dev/null +++ b/Source/WebCore/storage/IDBKeyRange.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBKeyRange { + readonly attribute IDBKey lower; + readonly attribute IDBKey upper; + readonly attribute boolean lowerOpen; + readonly attribute boolean upperOpen; + + // FIXME: Make ClassMethod work for JSC as well. + [ClassMethod] IDBKeyRange only(in IDBKey value); + [ClassMethod] IDBKeyRange lowerBound(in IDBKey bound, in [Optional] boolean open); + [ClassMethod] IDBKeyRange upperBound(in IDBKey bound, in [Optional] boolean open); + [ClassMethod] IDBKeyRange bound(in IDBKey lower, in IDBKey upper, in [Optional] OptionsObject options); + }; + +} diff --git a/Source/WebCore/storage/IDBObjectStore.cpp b/Source/WebCore/storage/IDBObjectStore.cpp new file mode 100644 index 0000000..ed5c96a --- /dev/null +++ b/Source/WebCore/storage/IDBObjectStore.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBObjectStore.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "DOMStringList.h" +#include "IDBAny.h" +#include "IDBDatabaseException.h" +#include "IDBIndex.h" +#include "IDBKey.h" +#include "IDBKeyRange.h" +#include "IDBTransactionBackendInterface.h" +#include "SerializedScriptValue.h" +#include <wtf/UnusedParam.h> + +namespace WebCore { + +static const unsigned short defaultDirection = IDBCursor::NEXT; + +IDBObjectStore::IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransactionBackendInterface* transaction) + : m_objectStore(idbObjectStore) + , m_transaction(transaction) +{ + ASSERT(m_objectStore); + ASSERT(m_transaction); + // We pass a reference to this object before it can be adopted. + relaxAdoptionRequirement(); +} + +String IDBObjectStore::name() const +{ + return m_objectStore->name(); +} + +String IDBObjectStore::keyPath() const +{ + return m_objectStore->keyPath(); +} + +PassRefPtr<DOMStringList> IDBObjectStore::indexNames() const +{ + return m_objectStore->indexNames(); +} + +PassRefPtr<IDBRequest> IDBObjectStore::get(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_objectStore->get(key, request, m_transaction.get(), ec); + if (ec) + return 0; + return request.release(); +} + +PassRefPtr<IDBRequest> IDBObjectStore::add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_objectStore->put(value, key, true, request, m_transaction.get(), ec); + if (ec) + return 0; + return request; +} + +PassRefPtr<IDBRequest> IDBObjectStore::put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_objectStore->put(value, key, false, request, m_transaction.get(), ec); + if (ec) + return 0; + return request; +} + +PassRefPtr<IDBRequest> IDBObjectStore::deleteFunction(ScriptExecutionContext* context, PassRefPtr<IDBKey> key, ExceptionCode& ec) +{ + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_objectStore->deleteFunction(key, request, m_transaction.get(), ec); + if (ec) + return 0; + return request; +} + +PassRefPtr<IDBIndex> IDBObjectStore::createIndex(const String& name, const String& keyPath, const OptionsObject& options, ExceptionCode& ec) +{ + bool unique = false; + options.getKeyBool("unique", unique); + + RefPtr<IDBIndexBackendInterface> index = m_objectStore->createIndex(name, keyPath, unique, m_transaction.get(), ec); + ASSERT(!index != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa. + if (!index) + return 0; + return IDBIndex::create(index.release(), m_transaction.get()); +} + +PassRefPtr<IDBIndex> IDBObjectStore::index(const String& name, ExceptionCode& ec) +{ + RefPtr<IDBIndexBackendInterface> index = m_objectStore->index(name, ec); + ASSERT(!index != !ec); // If we didn't get an index, we should have gotten an exception code. And vice versa. + if (!index) + return 0; + return IDBIndex::create(index.release(), m_transaction.get()); +} + +void IDBObjectStore::deleteIndex(const String& name, ExceptionCode& ec) +{ + m_objectStore->deleteIndex(name, m_transaction.get(), ec); +} + +PassRefPtr<IDBRequest> IDBObjectStore::openCursor(ScriptExecutionContext* context, const OptionsObject& options, ExceptionCode& ec) +{ + RefPtr<IDBKeyRange> range = options.getKeyKeyRange("range"); + + // Converted to an unsigned short. + int32_t direction = defaultDirection; + options.getKeyInt32("direction", direction); + if (direction != IDBCursor::NEXT && direction != IDBCursor::NEXT_NO_DUPLICATE && direction != IDBCursor::PREV && direction != IDBCursor::PREV_NO_DUPLICATE) { + // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 + ec = IDBDatabaseException::CONSTRAINT_ERR; + return 0; + } + + RefPtr<IDBRequest> request = IDBRequest::create(context, IDBAny::create(this), m_transaction.get()); + m_objectStore->openCursor(range, direction, request, m_transaction.get(), ec); + if (ec) + return 0; + return request.release(); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBObjectStore.h b/Source/WebCore/storage/IDBObjectStore.h new file mode 100644 index 0000000..0e9a4a9 --- /dev/null +++ b/Source/WebCore/storage/IDBObjectStore.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBObjectStore_h +#define IDBObjectStore_h + +#include "IDBCursor.h" +#include "IDBIndex.h" +#include "IDBKey.h" +#include "IDBKeyRange.h" +#include "IDBObjectStoreBackendInterface.h" +#include "IDBRequest.h" +#include "OptionsObject.h" +#include "PlatformString.h" +#include "SerializedScriptValue.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class DOMStringList; +class IDBAny; +class IDBTransactionBackendInterface; + +class IDBObjectStore : public RefCounted<IDBObjectStore> { +public: + static PassRefPtr<IDBObjectStore> create(PassRefPtr<IDBObjectStoreBackendInterface> idbObjectStore, IDBTransactionBackendInterface* transaction) + { + return adoptRef(new IDBObjectStore(idbObjectStore, transaction)); + } + ~IDBObjectStore() { } + + String name() const; + String keyPath() const; + PassRefPtr<DOMStringList> indexNames() const; + + // FIXME: Try to modify the code generator so this is unneeded. + PassRefPtr<IDBRequest> add(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, ExceptionCode& ec) { return add(context, value, 0, ec); } + PassRefPtr<IDBRequest> put(ScriptExecutionContext* context, PassRefPtr<SerializedScriptValue> value, ExceptionCode& ec) { return put(context, value, 0, ec); } + PassRefPtr<IDBIndex> createIndex(const String& name, const String& keyPath, ExceptionCode& ec) { return createIndex(name, keyPath, OptionsObject(), ec); } + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext* context, ExceptionCode& ec) { return openCursor(context, OptionsObject(), ec); } + + PassRefPtr<IDBRequest> get(ScriptExecutionContext*, PassRefPtr<IDBKey>, ExceptionCode&); + PassRefPtr<IDBRequest> add(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, ExceptionCode&); + PassRefPtr<IDBRequest> put(ScriptExecutionContext*, PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, ExceptionCode&); + PassRefPtr<IDBRequest> deleteFunction(ScriptExecutionContext*, PassRefPtr<IDBKey> key, ExceptionCode&); + + PassRefPtr<IDBIndex> createIndex(const String& name, const String& keyPath, const OptionsObject&, ExceptionCode&); + PassRefPtr<IDBIndex> index(const String& name, ExceptionCode&); + void deleteIndex(const String& name, ExceptionCode&); + + PassRefPtr<IDBRequest> openCursor(ScriptExecutionContext*, const OptionsObject&, ExceptionCode&); + +private: + IDBObjectStore(PassRefPtr<IDBObjectStoreBackendInterface>, IDBTransactionBackendInterface* transaction); + void removeTransactionFromPendingList(); + + RefPtr<IDBObjectStoreBackendInterface> m_objectStore; + RefPtr<IDBTransactionBackendInterface> m_transaction; +}; + +} // namespace WebCore + +#endif + +#endif // IDBObjectStore_h + diff --git a/Source/WebCore/storage/IDBObjectStore.idl b/Source/WebCore/storage/IDBObjectStore.idl new file mode 100644 index 0000000..f023dbe --- /dev/null +++ b/Source/WebCore/storage/IDBObjectStore.idl @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBObjectStore { + readonly attribute DOMString name; + readonly attribute [ConvertNullStringTo=Null] DOMString keyPath; + readonly attribute DOMStringList indexNames; + + [CallWith=ScriptExecutionContext] IDBRequest put(in SerializedScriptValue value, in [Optional] IDBKey key) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest add(in SerializedScriptValue value, in [Optional] IDBKey key) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext, ImplementationFunction=deleteFunction] IDBRequest delete(in IDBKey key) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest get(in IDBKey key) + raises (IDBDatabaseException); + [CallWith=ScriptExecutionContext] IDBRequest openCursor(in [Optional] OptionsObject options) + raises (IDBDatabaseException); + IDBIndex createIndex(in DOMString name, in [ConvertNullToNullString] DOMString keyPath, in [Optional] OptionsObject options) + raises (IDBDatabaseException); + IDBIndex index(in DOMString name) + raises (IDBDatabaseException); + void deleteIndex(in DOMString name) + raises (IDBDatabaseException); + }; +} diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp new file mode 100644 index 0000000..22d3b8d --- /dev/null +++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.cpp @@ -0,0 +1,501 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBObjectStoreBackendImpl.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "CrossThreadTask.h" +#include "DOMStringList.h" +#include "IDBBindingUtilities.h" +#include "IDBCallbacks.h" +#include "IDBCursorBackendImpl.h" +#include "IDBDatabaseBackendImpl.h" +#include "IDBDatabaseException.h" +#include "IDBIndexBackendImpl.h" +#include "IDBKey.h" +#include "IDBKeyPath.h" +#include "IDBKeyPathBackendImpl.h" +#include "IDBKeyRange.h" +#include "IDBSQLiteDatabase.h" +#include "IDBTransactionBackendInterface.h" +#include "ScriptExecutionContext.h" +#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" +#include "SQLiteTransaction.h" + +namespace WebCore { + +IDBObjectStoreBackendImpl::~IDBObjectStoreBackendImpl() +{ +} + +IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& keyPath, bool autoIncrement) + : m_database(database) + , m_id(id) + , m_name(name) + , m_keyPath(keyPath) + , m_autoIncrement(autoIncrement) +{ + loadIndexes(); +} + +IDBObjectStoreBackendImpl::IDBObjectStoreBackendImpl(IDBSQLiteDatabase* database, const String& name, const String& keyPath, bool autoIncrement) + : m_database(database) + , m_id(InvalidId) + , m_name(name) + , m_keyPath(keyPath) + , m_autoIncrement(autoIncrement) +{ +} + +PassRefPtr<DOMStringList> IDBObjectStoreBackendImpl::indexNames() const +{ + RefPtr<DOMStringList> indexNames = DOMStringList::create(); + for (IndexMap::const_iterator it = m_indexes.begin(); it != m_indexes.end(); ++it) + indexNames->append(it->first); + return indexNames.release(); +} + +static String whereClause(IDBKey* key) +{ + return "WHERE objectStoreId = ? AND " + key->whereSyntax(); +} + +static void bindWhereClause(SQLiteStatement& query, int64_t id, IDBKey* key) +{ + query.bindInt64(1, id); + key->bind(query, 2); +} + +void IDBObjectStoreBackendImpl::get(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) +{ + RefPtr<IDBObjectStoreBackendImpl> objectStore = this; + RefPtr<IDBKey> key = prpKey; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::getInternal, objectStore, key, callbacks))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBObjectStoreBackendImpl::getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) +{ + SQLiteStatement query(objectStore->sqliteDatabase(), "SELECT keyString, keyDate, keyNumber, value FROM ObjectStoreData " + whereClause(key.get())); + bool ok = query.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + bindWhereClause(query, objectStore->id(), key.get()); + if (query.step() != SQLResultRow) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store.")); + return; + } + + ASSERT((key->type() == IDBKey::StringType) != query.isColumnNull(0)); + // FIXME: Implement date. + ASSERT((key->type() == IDBKey::NumberType) != query.isColumnNull(2)); + + callbacks->onSuccess(SerializedScriptValue::createFromWire(query.getColumnText(3))); + ASSERT(query.step() != SQLResultRow); +} + +static PassRefPtr<IDBKey> fetchKeyFromKeyPath(SerializedScriptValue* value, const String& keyPath) +{ + Vector<RefPtr<SerializedScriptValue> > values; + values.append(value); + Vector<RefPtr<IDBKey> > keys; + IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(values, keyPath, keys); + if (keys.isEmpty()) + return 0; + ASSERT(keys.size() == 1); + return keys[0].release(); +} + +static bool putObjectStoreData(SQLiteDatabase& db, IDBKey* key, SerializedScriptValue* value, int64_t objectStoreId, int64_t& dataRowId) +{ + String sql = dataRowId != IDBObjectStoreBackendImpl::InvalidId ? "UPDATE ObjectStoreData SET keyString = ?, keyDate = ?, keyNumber = ?, value = ? WHERE id = ?" + : "INSERT INTO ObjectStoreData (keyString, keyDate, keyNumber, value, objectStoreId) VALUES (?, ?, ?, ?, ?)"; + SQLiteStatement query(db, sql); + if (query.prepare() != SQLResultOk) + return false; + key->bindWithNulls(query, 1); + query.bindText(4, value->toWireString()); + if (dataRowId != IDBDatabaseBackendImpl::InvalidId) + query.bindInt64(5, dataRowId); + else + query.bindInt64(5, objectStoreId); + + if (query.step() != SQLResultDone) + return false; + + if (dataRowId == IDBDatabaseBackendImpl::InvalidId) + dataRowId = db.lastInsertRowID(); + + return true; +} + +static bool deleteIndexData(SQLiteDatabase& db, int64_t objectStoreDataId) +{ + SQLiteStatement deleteQuery(db, "DELETE FROM IndexData WHERE objectStoreDataId = ?"); + if (deleteQuery.prepare() != SQLResultOk) + return false; + deleteQuery.bindInt64(1, objectStoreDataId); + + return deleteQuery.step() == SQLResultDone; +} + +static bool putIndexData(SQLiteDatabase& db, IDBKey* key, int64_t indexId, int64_t objectStoreDataId) +{ + SQLiteStatement putQuery(db, "INSERT INTO IndexData (keyString, keyDate, keyNumber, indexId, objectStoreDataId) VALUES (?, ?, ?, ?, ?)"); + if (putQuery.prepare() != SQLResultOk) + return false; + key->bindWithNulls(putQuery, 1); + putQuery.bindInt64(4, indexId); + putQuery.bindInt64(5, objectStoreDataId); + + return putQuery.step() == SQLResultDone; +} + +void IDBObjectStoreBackendImpl::put(PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, bool addOnly, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) +{ + RefPtr<IDBObjectStoreBackendImpl> objectStore = this; + RefPtr<SerializedScriptValue> value = prpValue; + RefPtr<IDBKey> key = prpKey; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; + // FIXME: This should throw a SERIAL_ERR on structured clone problems. + // FIXME: This should throw a DATA_ERR when the wrong key/keyPath data is supplied. + if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::putInternal, objectStore, value, key, addOnly, callbacks, transaction))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBObjectStoreBackendImpl::putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<SerializedScriptValue> prpValue, PassRefPtr<IDBKey> prpKey, bool addOnly, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + RefPtr<SerializedScriptValue> value = prpValue; + RefPtr<IDBKey> key = prpKey; + + // FIXME: Support auto-increment. + + if (!objectStore->m_keyPath.isNull()) { + if (key) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "A key was supplied for an objectStore that has a keyPath.")); + return; + } + key = fetchKeyFromKeyPath(value.get(), objectStore->m_keyPath); + if (!key) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "The key could not be fetched from the keyPath.")); + return; + } + } else if (!key) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "No key supplied.")); + return; + } + if (key->type() == IDBKey::NullType) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "NULL key is not allowed.")); + return; + } + + Vector<RefPtr<IDBKey> > indexKeys; + for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it) { + RefPtr<IDBKey> key = fetchKeyFromKeyPath(value.get(), it->second->keyPath()); + if (!key) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "The key could not be fetched from an index's keyPath.")); + return; + } + if (key->type() == IDBKey::NullType) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::DATA_ERR, "One of the derived (from a keyPath) keys for an index is NULL.")); + return; + } + if (!it->second->addingKeyAllowed(key.get())) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "One of the derived (from a keyPath) keys for an index does not satisfy its uniqueness requirements.")); + return; + } + indexKeys.append(key.release()); + } + + SQLiteStatement getQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get())); + bool ok = getQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + bindWhereClause(getQuery, objectStore->id(), key.get()); + bool isExistingValue = getQuery.step() == SQLResultRow; + if (addOnly && isExistingValue) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::CONSTRAINT_ERR, "Key already exists in the object store.")); + return; + } + + // Before this point, don't do any mutation. After this point, rollback the transaction in case of error. + + int64_t dataRowId = isExistingValue ? getQuery.getColumnInt(0) : InvalidId; + if (!putObjectStoreData(objectStore->sqliteDatabase(), key.get(), value.get(), objectStore->id(), dataRowId)) { + // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); + transaction->abort(); + return; + } + + if (!deleteIndexData(objectStore->sqliteDatabase(), dataRowId)) { + // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); + transaction->abort(); + return; + } + + int i = 0; + for (IndexMap::iterator it = objectStore->m_indexes.begin(); it != objectStore->m_indexes.end(); ++it, ++i) { + if (!putIndexData(objectStore->sqliteDatabase(), indexKeys[i].get(), it->second->id(), dataRowId)) { + // FIXME: The Indexed Database specification does not have an error code dedicated to I/O errors. + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.")); + transaction->abort(); + return; + } + } + + callbacks->onSuccess(key.get()); +} + +void IDBObjectStoreBackendImpl::deleteFunction(PassRefPtr<IDBKey> prpKey, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) +{ + RefPtr<IDBObjectStoreBackendImpl> objectStore = this; + RefPtr<IDBKey> key = prpKey; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::deleteInternal, objectStore, key, callbacks))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBObjectStoreBackendImpl::deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks> callbacks) +{ + SQLiteStatement idQuery(objectStore->sqliteDatabase(), "SELECT id FROM ObjectStoreData " + whereClause(key.get())); + bool ok = idQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + bindWhereClause(idQuery, objectStore->id(), key.get()); + if (idQuery.step() != SQLResultRow) { + callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::NOT_FOUND_ERR, "Key does not exist in the object store.")); + return; + } + + int64_t id = idQuery.getColumnInt64(0); + + SQLiteStatement osQuery(objectStore->sqliteDatabase(), "DELETE FROM ObjectStoreData WHERE id = ?"); + ok = osQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + osQuery.bindInt64(1, id); + + ok = osQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); + + SQLiteStatement indexQuery(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE objectStoreDataId = ?"); + ok = indexQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + indexQuery.bindInt64(1, id); + + ok = indexQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); + + callbacks->onSuccess(); +} + +PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::createIndex(const String& name, const String& keyPath, bool unique, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) +{ + if (m_indexes.contains(name)) { + ec = IDBDatabaseException::CONSTRAINT_ERR; + return 0; + } + if (transaction->mode() != IDBTransaction::VERSION_CHANGE) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } + + RefPtr<IDBIndexBackendImpl> index = IDBIndexBackendImpl::create(m_database.get(), name, m_name, keyPath, unique); + ASSERT(index->name() == name); + + RefPtr<IDBObjectStoreBackendImpl> objectStore = this; + RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction; + if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::createIndexInternal, objectStore, index, transaction), + createCallbackTask(&IDBObjectStoreBackendImpl::removeIndexFromMap, objectStore, index))) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } + + m_indexes.set(name, index); + return index.release(); +} + +void IDBObjectStoreBackendImpl::createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + SQLiteStatement insert(objectStore->sqliteDatabase(), "INSERT INTO Indexes (objectStoreId, name, keyPath, isUnique) VALUES (?, ?, ?, ?)"); + if (insert.prepare() != SQLResultOk) { + transaction->abort(); + return; + } + insert.bindInt64(1, objectStore->m_id); + insert.bindText(2, index->name()); + insert.bindText(3, index->keyPath()); + insert.bindInt(4, static_cast<int>(index->unique())); + if (insert.step() != SQLResultDone) { + transaction->abort(); + return; + } + int64_t id = objectStore->sqliteDatabase().lastInsertRowID(); + index->setId(id); + transaction->didCompleteTaskEvents(); +} + +PassRefPtr<IDBIndexBackendInterface> IDBObjectStoreBackendImpl::index(const String& name, ExceptionCode& ec) +{ + RefPtr<IDBIndexBackendInterface> index = m_indexes.get(name); + if (!index) { + ec = IDBDatabaseException::NOT_FOUND_ERR; + return 0; + } + return index.release(); +} + +static void doDelete(SQLiteDatabase& db, const char* sql, int64_t id) +{ + SQLiteStatement deleteQuery(db, sql); + bool ok = deleteQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. + deleteQuery.bindInt64(1, id); + ok = deleteQuery.step() == SQLResultDone; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling. +} + +void IDBObjectStoreBackendImpl::deleteIndex(const String& name, IDBTransactionBackendInterface* transaction, ExceptionCode& ec) +{ + RefPtr<IDBIndexBackendImpl> index = m_indexes.get(name); + if (!index) { + ec = IDBDatabaseException::NOT_FOUND_ERR; + return; + } + + RefPtr<IDBObjectStoreBackendImpl> objectStore = this; + RefPtr<IDBTransactionBackendInterface> transactionPtr = transaction; + if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::deleteIndexInternal, objectStore, index, transactionPtr), + createCallbackTask(&IDBObjectStoreBackendImpl::addIndexToMap, objectStore, index))) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return; + } + m_indexes.remove(name); +} + +void IDBObjectStoreBackendImpl::deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + doDelete(objectStore->sqliteDatabase(), "DELETE FROM Indexes WHERE id = ?", index->id()); + doDelete(objectStore->sqliteDatabase(), "DELETE FROM IndexData WHERE indexId = ?", index->id()); + + transaction->didCompleteTaskEvents(); +} + +void IDBObjectStoreBackendImpl::openCursor(PassRefPtr<IDBKeyRange> prpRange, unsigned short direction, PassRefPtr<IDBCallbacks> prpCallbacks, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec) +{ + RefPtr<IDBObjectStoreBackendImpl> objectStore = this; + RefPtr<IDBKeyRange> range = prpRange; + RefPtr<IDBCallbacks> callbacks = prpCallbacks; + RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr; + if (!transaction->scheduleTask(createCallbackTask(&IDBObjectStoreBackendImpl::openCursorInternal, objectStore, range, direction, callbacks, transaction))) + ec = IDBDatabaseException::NOT_ALLOWED_ERR; +} + +void IDBObjectStoreBackendImpl::openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBKeyRange> range, unsigned short tmpDirection, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction) +{ + bool lowerBound = range && range->lower(); + bool upperBound = range && range->upper(); + + // Several files depend on this order of selects. + String sql = "SELECT id, keyString, keyDate, keyNumber, value FROM ObjectStoreData WHERE "; + if (lowerBound) + sql += range->lower()->lowerCursorWhereFragment(range->lowerWhereClauseComparisonOperator()); + if (upperBound) + sql += range->upper()->upperCursorWhereFragment(range->upperWhereClauseComparisonOperator()); + sql += "objectStoreId = ? ORDER BY "; + + IDBCursor::Direction direction = static_cast<IDBCursor::Direction>(tmpDirection); + if (direction == IDBCursor::NEXT || direction == IDBCursor::NEXT_NO_DUPLICATE) + sql += "keyString, keyDate, keyNumber"; + else + sql += "keyString DESC, keyDate DESC, keyNumber DESC"; + + OwnPtr<SQLiteStatement> query = adoptPtr(new SQLiteStatement(objectStore->sqliteDatabase(), sql)); + bool ok = query->prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + int currentColumn = 1; + if (lowerBound) + currentColumn += range->lower()->bind(*query, currentColumn); + if (upperBound) + currentColumn += range->upper()->bind(*query, currentColumn); + query->bindInt64(currentColumn, objectStore->id()); + + if (query->step() != SQLResultRow) { + callbacks->onSuccess(); + return; + } + + RefPtr<IDBCursorBackendInterface> cursor = IDBCursorBackendImpl::create(objectStore->m_database.get(), range, direction, query.release(), true, transaction.get(), objectStore.get()); + callbacks->onSuccess(cursor.release()); +} + +void IDBObjectStoreBackendImpl::loadIndexes() +{ + SQLiteStatement indexQuery(sqliteDatabase(), "SELECT id, name, keyPath, isUnique FROM Indexes WHERE objectStoreId = ?"); + bool ok = indexQuery.prepare() == SQLResultOk; + ASSERT_UNUSED(ok, ok); // FIXME: Better error handling? + + indexQuery.bindInt64(1, m_id); + + while (indexQuery.step() == SQLResultRow) { + int64_t id = indexQuery.getColumnInt64(0); + String name = indexQuery.getColumnText(1); + String keyPath = indexQuery.getColumnText(2); + bool unique = !!indexQuery.getColumnInt(3); + + m_indexes.set(name, IDBIndexBackendImpl::create(m_database.get(), id, name, m_name, keyPath, unique)); + } +} + +SQLiteDatabase& IDBObjectStoreBackendImpl::sqliteDatabase() const +{ + return m_database->db(); +} + +void IDBObjectStoreBackendImpl::removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index) +{ + ASSERT(objectStore->m_indexes.contains(index->name())); + objectStore->m_indexes.remove(index->name()); +} + +void IDBObjectStoreBackendImpl::addIndexToMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBIndexBackendImpl> index) +{ + RefPtr<IDBIndexBackendImpl> indexPtr = index; + ASSERT(!objectStore->m_indexes.contains(indexPtr->name())); + objectStore->m_indexes.set(indexPtr->name(), indexPtr); +} + + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBObjectStoreBackendImpl.h b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h new file mode 100644 index 0000000..2ab42f2 --- /dev/null +++ b/Source/WebCore/storage/IDBObjectStoreBackendImpl.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBObjectStoreBackendImpl_h +#define IDBObjectStoreBackendImpl_h + +#include "IDBObjectStoreBackendInterface.h" +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBDatabaseBackendImpl; +class IDBIndexBackendImpl; +class IDBSQLiteDatabase; +class IDBTransactionBackendInterface; +class SQLiteDatabase; +class ScriptExecutionContext; + +class IDBObjectStoreBackendImpl : public IDBObjectStoreBackendInterface { +public: + static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBSQLiteDatabase* database, int64_t id, const String& name, const String& keyPath, bool autoIncrement) + { + return adoptRef(new IDBObjectStoreBackendImpl(database, id, name, keyPath, autoIncrement)); + } + static PassRefPtr<IDBObjectStoreBackendImpl> create(IDBSQLiteDatabase* database, const String& name, const String& keyPath, bool autoIncrement) + { + return adoptRef(new IDBObjectStoreBackendImpl(database, name, keyPath, autoIncrement)); + } + virtual ~IDBObjectStoreBackendImpl(); + + static const int64_t InvalidId = 0; + int64_t id() const + { + ASSERT(m_id != InvalidId); + return m_id; + } + void setId(int64_t id) { m_id = id; } + + virtual String name() const { return m_name; } + virtual String keyPath() const { return m_keyPath; } + virtual PassRefPtr<DOMStringList> indexNames() const; + virtual bool autoIncrement() const { return m_autoIncrement; } + + virtual void get(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void put(PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, bool addOnly, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + virtual void deleteFunction(PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + + virtual PassRefPtr<IDBIndexBackendInterface> createIndex(const String& name, const String& keyPath, bool unique, IDBTransactionBackendInterface*, ExceptionCode&); + virtual PassRefPtr<IDBIndexBackendInterface> index(const String& name, ExceptionCode&); + virtual void deleteIndex(const String& name, IDBTransactionBackendInterface*, ExceptionCode&); + + virtual void openCursor(PassRefPtr<IDBKeyRange> range, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&); + +private: + IDBObjectStoreBackendImpl(IDBSQLiteDatabase*, int64_t id, const String& name, const String& keyPath, bool autoIncrement); + IDBObjectStoreBackendImpl(IDBSQLiteDatabase*, const String& name, const String& keyPath, bool autoIncrement); + + void loadIndexes(); + SQLiteDatabase& sqliteDatabase() const; + + static void getInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>); + static void putInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<SerializedScriptValue> value, PassRefPtr<IDBKey> key, bool addOnly, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); + static void deleteInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBKey> key, PassRefPtr<IDBCallbacks>); + static void createIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBTransactionBackendInterface>); + static void deleteIndexInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>, PassRefPtr<IDBTransactionBackendInterface>); + static void openCursorInternal(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBKeyRange> range, unsigned short direction, PassRefPtr<IDBCallbacks>, PassRefPtr<IDBTransactionBackendInterface>); + + // These are used as setVersion transaction abort tasks. + static void removeIndexFromMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>); + static void addIndexToMap(ScriptExecutionContext*, PassRefPtr<IDBObjectStoreBackendImpl>, PassRefPtr<IDBIndexBackendImpl>); + + RefPtr<IDBSQLiteDatabase> m_database; + + int64_t m_id; + String m_name; + String m_keyPath; + bool m_autoIncrement; + + typedef HashMap<String, RefPtr<IDBIndexBackendImpl> > IndexMap; + IndexMap m_indexes; +}; + +} // namespace WebCore + +#endif + +#endif // IDBObjectStoreBackendImpl_h diff --git a/Source/WebCore/storage/IDBObjectStoreBackendInterface.h b/Source/WebCore/storage/IDBObjectStoreBackendInterface.h new file mode 100644 index 0000000..02ceb27 --- /dev/null +++ b/Source/WebCore/storage/IDBObjectStoreBackendInterface.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBObjectStoreBackendInterface_h +#define IDBObjectStoreBackendInterface_h + +#include "ExceptionCode.h" +#include "PlatformString.h" +#include <wtf/Threading.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class DOMStringList; +class IDBCallbacks; +class IDBIndexBackendInterface; +class IDBKey; +class IDBKeyRange; +class IDBTransactionBackendInterface; +class SerializedScriptValue; + +class IDBObjectStoreBackendInterface : public ThreadSafeShared<IDBObjectStoreBackendInterface> { +public: + virtual ~IDBObjectStoreBackendInterface() { } + + virtual String name() const = 0; + virtual String keyPath() const = 0; + virtual PassRefPtr<DOMStringList> indexNames() const = 0; + + virtual void get(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual void put(PassRefPtr<SerializedScriptValue>, PassRefPtr<IDBKey>, bool addOnly, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual void deleteFunction(PassRefPtr<IDBKey>, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + + virtual PassRefPtr<IDBIndexBackendInterface> createIndex(const String& name, const String& keyPath, bool unique, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + virtual PassRefPtr<IDBIndexBackendInterface> index(const String& name, ExceptionCode&) = 0; + virtual void deleteIndex(const String& name, IDBTransactionBackendInterface*, ExceptionCode&) = 0; + + virtual void openCursor(PassRefPtr<IDBKeyRange>, unsigned short direction, PassRefPtr<IDBCallbacks>, IDBTransactionBackendInterface*, ExceptionCode&) = 0; +}; + +} // namespace WebCore + +#endif + +#endif // IDBObjectStoreBackendInterface_h + diff --git a/Source/WebCore/storage/IDBPendingTransactionMonitor.cpp b/Source/WebCore/storage/IDBPendingTransactionMonitor.cpp new file mode 100644 index 0000000..799200d --- /dev/null +++ b/Source/WebCore/storage/IDBPendingTransactionMonitor.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBPendingTransactionMonitor.h" +#include "IDBTransactionBackendInterface.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +Vector<IDBTransactionBackendInterface*>* IDBPendingTransactionMonitor::m_transactions = 0; + +void IDBPendingTransactionMonitor::addPendingTransaction(IDBTransactionBackendInterface* transaction) +{ + if (!m_transactions) + m_transactions = new Vector<IDBTransactionBackendInterface*>(); + m_transactions->append(transaction); +} + +void IDBPendingTransactionMonitor::removePendingTransaction(IDBTransactionBackendInterface* transaction) +{ + if (!m_transactions) + return; + + size_t pos = m_transactions->find(transaction); + if (pos == notFound) + return; + + m_transactions->remove(pos); + + if (!m_transactions->size()) { + delete m_transactions; + m_transactions = 0; + } +} + +void IDBPendingTransactionMonitor::abortPendingTransactions() +{ + if (!m_transactions) + return; + + for (size_t i = 0; i < m_transactions->size(); ++i) + m_transactions->at(i)->abort(); + + delete m_transactions; + m_transactions = 0; +} + +}; +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBPendingTransactionMonitor.h b/Source/WebCore/storage/IDBPendingTransactionMonitor.h new file mode 100644 index 0000000..783a731 --- /dev/null +++ b/Source/WebCore/storage/IDBPendingTransactionMonitor.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBPendingTransactionMonitor_h +#define IDBPendingTransactionMonitor_h + +#if ENABLE(INDEXED_DATABASE) + +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class IDBTransactionBackendInterface; + +// This class keeps track of the transactions created during the current +// Javascript execution context. A transaction is 'pending' if no asynchronous +// operation is currently queued for it (e.g. an IDBObjectStore::put() or similar). +// All pending transactions are aborted as soon as execution returns from +// the script engine. +// +// FIXME: move the vector of transactions to TLS. Keeping it static +// will not work once we add support for workers. Another possible +// solution is to keep the vector in the ScriptExecutionContext. +class IDBPendingTransactionMonitor : public Noncopyable { +public: + static void addPendingTransaction(IDBTransactionBackendInterface*); + static void removePendingTransaction(IDBTransactionBackendInterface*); + static void abortPendingTransactions(); + +private: + IDBPendingTransactionMonitor(); + + static Vector<IDBTransactionBackendInterface*>* m_transactions; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBPendingTransactionMonitor_h diff --git a/Source/WebCore/storage/IDBRequest.cpp b/Source/WebCore/storage/IDBRequest.cpp new file mode 100644 index 0000000..cbd635c --- /dev/null +++ b/Source/WebCore/storage/IDBRequest.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBRequest.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "Event.h" +#include "EventException.h" +#include "EventListener.h" +#include "EventNames.h" +#include "IDBCursor.h" +#include "IDBDatabase.h" +#include "IDBIndex.h" +#include "IDBErrorEvent.h" +#include "IDBObjectStore.h" +#include "IDBPendingTransactionMonitor.h" +#include "IDBSuccessEvent.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction) + : ActiveDOMObject(context, this) + , m_source(source) + , m_transaction(transaction) + , m_timer(this, &IDBRequest::timerFired) + , m_readyState(LOADING) +{ + if (m_transaction) + IDBPendingTransactionMonitor::removePendingTransaction(m_transaction.get()); +} + +IDBRequest::~IDBRequest() +{ +} + +bool IDBRequest::resetReadyState(IDBTransactionBackendInterface* transaction) +{ + ASSERT(m_readyState == DONE); + m_readyState = LOADING; + ASSERT(!m_transaction); + m_transaction = transaction; + IDBPendingTransactionMonitor::removePendingTransaction(m_transaction.get()); + return true; +} + +void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error) +{ + scheduleEvent(0, error); +} + +void IDBRequest::onSuccess() +{ + scheduleEvent(IDBAny::createNull(), 0); +} + +void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend) +{ + scheduleEvent(IDBAny::create(IDBCursor::create(backend, this, m_transaction.get())), 0); +} + +void IDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackendInterface> backend) +{ + scheduleEvent(IDBAny::create(IDBDatabase::create(backend)), 0); +} + +void IDBRequest::onSuccess(PassRefPtr<IDBIndexBackendInterface> backend) +{ + scheduleEvent(IDBAny::create(IDBIndex::create(backend, m_transaction.get())), 0); +} + +void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey) +{ + scheduleEvent(IDBAny::create(idbKey), 0); +} + +void IDBRequest::onSuccess(PassRefPtr<IDBObjectStoreBackendInterface> backend) +{ + // FIXME: This function should go away once createObjectStore is sync. + scheduleEvent(IDBAny::create(IDBObjectStore::create(backend, m_transaction.get())), 0); +} + +void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend) +{ + RefPtr<IDBTransactionBackendInterface> backend = prpBackend; + // This is only used by setVersion which will always have a source that's an IDBDatabase. + m_source->idbDatabase()->setSetVersionTransaction(backend.get()); + RefPtr<IDBTransaction> frontend = IDBTransaction::create(scriptExecutionContext(), backend, m_source->idbDatabase().get()); + backend->setCallbacks(frontend.get()); + m_transaction = backend; + IDBPendingTransactionMonitor::removePendingTransaction(m_transaction.get()); + scheduleEvent(IDBAny::create(frontend.release()), 0); +} + +void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue) +{ + scheduleEvent(IDBAny::create(serializedScriptValue), 0); +} + +ScriptExecutionContext* IDBRequest::scriptExecutionContext() const +{ + return ActiveDOMObject::scriptExecutionContext(); +} + +bool IDBRequest::canSuspend() const +{ + // IDBTransactions cannot be suspended at the moment. We therefore + // disallow the back/forward cache for pages that use IndexedDatabase. + return false; +} + +EventTargetData* IDBRequest::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* IDBRequest::ensureEventTargetData() +{ + return &m_eventTargetData; +} + +void IDBRequest::timerFired(Timer<IDBRequest>*) +{ + ASSERT(m_selfRef); + ASSERT(m_pendingEvents.size()); + // FIXME: We should handle the stop event and stop any timers when we see it. We can then assert here that scriptExecutionContext is non-null. + + // We need to keep self-referencing ourself, otherwise it's possible we'll be deleted. + // But in some cases, suspend() could be called while we're dispatching an event, so we + // need to make sure that resume() doesn't re-start the timer based on m_selfRef being set. + RefPtr<IDBRequest> selfRef = m_selfRef.release(); + + // readyStateReset can be called synchronously while we're dispatching the event. + RefPtr<IDBTransactionBackendInterface> transaction = m_transaction; + m_transaction.clear(); + + Vector<PendingEvent> pendingEvents; + pendingEvents.swap(m_pendingEvents); + for (size_t i = 0; i < pendingEvents.size(); ++i) { + // It's possible we've navigated in which case we'll crash. + if (!scriptExecutionContext()) + return; + + if (pendingEvents[i].m_error) { + ASSERT(!pendingEvents[i].m_result); + dispatchEvent(IDBErrorEvent::create(m_source, *pendingEvents[i].m_error)); + } else { + ASSERT(pendingEvents[i].m_result->type() != IDBAny::UndefinedType); + dispatchEvent(IDBSuccessEvent::create(m_source, pendingEvents[i].m_result)); + } + } + if (transaction) { + // Now that we processed all pending events, let the transaction monitor check if + // it can commit the current transaction or if there's anything new pending. + // FIXME: Handle the workers case. + transaction->didCompleteTaskEvents(); + } +} + +void IDBRequest::scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabaseError> error) +{ + ASSERT(m_readyState < DONE); + ASSERT(!!m_selfRef == m_timer.isActive()); + + PendingEvent pendingEvent; + pendingEvent.m_result = result; + pendingEvent.m_error = error; + m_pendingEvents.append(pendingEvent); + + m_readyState = DONE; + if (!m_timer.isActive()) { + m_selfRef = this; + m_timer.startOneShot(0); + } +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBRequest.h b/Source/WebCore/storage/IDBRequest.h new file mode 100644 index 0000000..fa68208 --- /dev/null +++ b/Source/WebCore/storage/IDBRequest.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBRequest_h +#define IDBRequest_h + +#if ENABLE(INDEXED_DATABASE) + +#include "ActiveDOMObject.h" +#include "EventListener.h" +#include "EventNames.h" +#include "EventTarget.h" +#include "IDBAny.h" +#include "IDBCallbacks.h" +#include "Timer.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class IDBTransactionBackendInterface; + +class IDBRequest : public IDBCallbacks, public EventTarget, public ActiveDOMObject { +public: + static PassRefPtr<IDBRequest> create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction) { return adoptRef(new IDBRequest(context, source, transaction)); } + virtual ~IDBRequest(); + + // Defined in the IDL + enum ReadyState { + LOADING = 1, + DONE = 2 + }; + unsigned short readyState() const { return m_readyState; } + DEFINE_ATTRIBUTE_EVENT_LISTENER(success); + DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + + bool resetReadyState(IDBTransactionBackendInterface*); + + // IDBCallbacks + virtual void onError(PassRefPtr<IDBDatabaseError>); + virtual void onSuccess(); // For "null". + virtual void onSuccess(PassRefPtr<IDBDatabaseBackendInterface>); + virtual void onSuccess(PassRefPtr<IDBCursorBackendInterface>); + virtual void onSuccess(PassRefPtr<IDBIndexBackendInterface>); + virtual void onSuccess(PassRefPtr<IDBKey>); + virtual void onSuccess(PassRefPtr<IDBObjectStoreBackendInterface>); + virtual void onSuccess(PassRefPtr<IDBTransactionBackendInterface>); + virtual void onSuccess(PassRefPtr<SerializedScriptValue>); + + // EventTarget + virtual IDBRequest* toIDBRequest() { return this; } + + // ActiveDOMObject + virtual ScriptExecutionContext* scriptExecutionContext() const; + virtual bool canSuspend() const; + + using ThreadSafeShared<IDBCallbacks>::ref; + using ThreadSafeShared<IDBCallbacks>::deref; + +private: + IDBRequest(ScriptExecutionContext*, PassRefPtr<IDBAny> source, IDBTransactionBackendInterface* transaction); + + void timerFired(Timer<IDBRequest>*); + void scheduleEvent(PassRefPtr<IDBAny> result, PassRefPtr<IDBDatabaseError>); + + // EventTarget + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + + RefPtr<IDBAny> m_source; + RefPtr<IDBTransactionBackendInterface> m_transaction; + + struct PendingEvent { + RefPtr<IDBAny> m_result; + RefPtr<IDBDatabaseError> m_error; + }; + Vector<PendingEvent> m_pendingEvents; + + // Used to fire events asynchronously. + Timer<IDBRequest> m_timer; + RefPtr<IDBRequest> m_selfRef; // This is set to us iff there's an event pending. + + ReadyState m_readyState; + EventTargetData m_eventTargetData; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBRequest_h diff --git a/Source/WebCore/storage/IDBRequest.idl b/Source/WebCore/storage/IDBRequest.idl new file mode 100644 index 0000000..58872f0 --- /dev/null +++ b/Source/WebCore/storage/IDBRequest.idl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE, + EventTarget + ] IDBRequest { + + // States + const unsigned short LOADING = 1; + const unsigned short DONE = 2; + readonly attribute unsigned short readyState; + + // Events + attribute EventListener onsuccess; + attribute EventListener onerror; + + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; +} diff --git a/Source/WebCore/storage/IDBSQLiteDatabase.cpp b/Source/WebCore/storage/IDBSQLiteDatabase.cpp new file mode 100644 index 0000000..e881917 --- /dev/null +++ b/Source/WebCore/storage/IDBSQLiteDatabase.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBSQLiteDatabase.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBFactoryBackendImpl.h" + +namespace WebCore { + +IDBSQLiteDatabase::IDBSQLiteDatabase(String identifier, IDBFactoryBackendImpl* factory) + : m_identifier(identifier) + , m_factory(factory) +{ +} + +IDBSQLiteDatabase::~IDBSQLiteDatabase() +{ + m_factory->removeSQLiteDatabase(m_identifier); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBSQLiteDatabase.h b/Source/WebCore/storage/IDBSQLiteDatabase.h new file mode 100644 index 0000000..0556506 --- /dev/null +++ b/Source/WebCore/storage/IDBSQLiteDatabase.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBSQLiteDatabase_h +#define IDBSQLiteDatabase_h + +#if ENABLE(INDEXED_DATABASE) + +#include "SQLiteDatabase.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBFactoryBackendImpl; + +class IDBSQLiteDatabase : public RefCounted<IDBSQLiteDatabase> { +public: + static PassRefPtr<IDBSQLiteDatabase> create(String identifier, IDBFactoryBackendImpl* factory) + { + return adoptRef(new IDBSQLiteDatabase(identifier, factory)); + } + ~IDBSQLiteDatabase(); + + SQLiteDatabase& db() { return m_db; } + +private: + IDBSQLiteDatabase(String identifier, IDBFactoryBackendImpl* factory); + + SQLiteDatabase m_db; + String m_identifier; + RefPtr<IDBFactoryBackendImpl> m_factory; +}; + +} // namespace WebCore + +#endif + +#endif // IDBSQLiteDatabase_h diff --git a/Source/WebCore/storage/IDBSuccessEvent.cpp b/Source/WebCore/storage/IDBSuccessEvent.cpp new file mode 100644 index 0000000..2dcd964 --- /dev/null +++ b/Source/WebCore/storage/IDBSuccessEvent.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBSuccessEvent.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "EventNames.h" +#include "IDBAny.h" + +namespace WebCore { + +PassRefPtr<IDBSuccessEvent> IDBSuccessEvent::create(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result) +{ + return adoptRef(new IDBSuccessEvent(source, result)); +} + +IDBSuccessEvent::IDBSuccessEvent(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result) + : IDBEvent(eventNames().successEvent, source) + , m_result(result) +{ +} + +IDBSuccessEvent::~IDBSuccessEvent() +{ +} + +PassRefPtr<IDBAny> IDBSuccessEvent::result() +{ + return m_result; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBSuccessEvent.h b/Source/WebCore/storage/IDBSuccessEvent.h new file mode 100644 index 0000000..5be660a --- /dev/null +++ b/Source/WebCore/storage/IDBSuccessEvent.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBSuccessEvent_h +#define IDBSuccessEvent_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBEvent.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBAny; + +class IDBSuccessEvent : public IDBEvent { +public: + static PassRefPtr<IDBSuccessEvent> create(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result); + // FIXME: Need to allow creation of these events from JS. + virtual ~IDBSuccessEvent(); + + PassRefPtr<IDBAny> result(); + + virtual bool isIDBSuccessEvent() const { return true; } + +private: + IDBSuccessEvent(PassRefPtr<IDBAny> source, PassRefPtr<IDBAny> result); + + RefPtr<IDBAny> m_result; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBEvent_h diff --git a/Source/WebCore/storage/IDBSuccessEvent.idl b/Source/WebCore/storage/IDBSuccessEvent.idl new file mode 100644 index 0000000..b4ea7d2 --- /dev/null +++ b/Source/WebCore/storage/IDBSuccessEvent.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBSuccessEvent : IDBEvent { + readonly attribute IDBAny result; + }; +} diff --git a/Source/WebCore/storage/IDBTimeoutEvent.cpp b/Source/WebCore/storage/IDBTimeoutEvent.cpp new file mode 100644 index 0000000..b61ee47 --- /dev/null +++ b/Source/WebCore/storage/IDBTimeoutEvent.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBTimeoutEvent.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "EventNames.h" +#include "IDBAny.h" + +namespace WebCore { + +PassRefPtr<IDBTimeoutEvent> IDBTimeoutEvent::create() +{ + return adoptRef(new IDBTimeoutEvent()); +} + +IDBTimeoutEvent::IDBTimeoutEvent() + : IDBEvent(eventNames().abortEvent, 0) // FIXME: set the source to the transaction +{ +} + +IDBTimeoutEvent::~IDBTimeoutEvent() +{ +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/IDBTimeoutEvent.h b/Source/WebCore/storage/IDBTimeoutEvent.h new file mode 100644 index 0000000..afc9ba4 --- /dev/null +++ b/Source/WebCore/storage/IDBTimeoutEvent.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBTimeoutEvent_h +#define IDBTimeoutEvent_h + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBEvent.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBTimeoutEvent : public IDBEvent { +public: + static PassRefPtr<IDBTimeoutEvent> create(); + // FIXME: Need to allow creation of these events from JS. + virtual ~IDBTimeoutEvent(); + + virtual bool isIDBTimeoutEvent() const { return true; } + +private: + IDBTimeoutEvent(); +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBTimeoutEvent_h diff --git a/Source/WebCore/storage/IDBTransaction.cpp b/Source/WebCore/storage/IDBTransaction.cpp new file mode 100644 index 0000000..e3625d4 --- /dev/null +++ b/Source/WebCore/storage/IDBTransaction.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBTransaction.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "Event.h" +#include "EventException.h" +#include "IDBAbortEvent.h" +#include "IDBCompleteEvent.h" +#include "IDBDatabase.h" +#include "IDBDatabaseException.h" +#include "IDBIndex.h" +#include "IDBObjectStore.h" +#include "IDBObjectStoreBackendInterface.h" +#include "IDBPendingTransactionMonitor.h" +#include "IDBTimeoutEvent.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +IDBTransaction::IDBTransaction(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBDatabase* db) + : ActiveDOMObject(context, this) + , m_backend(backend) + , m_database(db) + , m_mode(m_backend->mode()) + , m_onAbortTimer(this, &IDBTransaction::onAbortTimerFired) + , m_onCompleteTimer(this, &IDBTransaction::onCompleteTimerFired) + , m_onTimeoutTimer(this, &IDBTransaction::onTimeoutTimerFired) +{ + IDBPendingTransactionMonitor::addPendingTransaction(m_backend.get()); +} + +IDBTransaction::~IDBTransaction() +{ +} + +unsigned short IDBTransaction::mode() const +{ + return m_mode; +} + +IDBDatabase* IDBTransaction::db() +{ + return m_database.get(); +} + +PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, ExceptionCode& ec) +{ + if (!m_backend) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } + RefPtr<IDBObjectStoreBackendInterface> objectStoreBackend = m_backend->objectStore(name, ec); + if (!objectStoreBackend) { + ASSERT(ec); + return 0; + } + RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(objectStoreBackend, m_backend.get()); + return objectStore.release(); +} + +void IDBTransaction::abort() +{ + if (m_backend) + m_backend->abort(); +} + +ScriptExecutionContext* IDBTransaction::scriptExecutionContext() const +{ + return ActiveDOMObject::scriptExecutionContext(); +} + +void IDBTransaction::onAbort() +{ + ASSERT(!m_onAbortTimer.isActive()); + ASSERT(!m_onCompleteTimer.isActive()); + ASSERT(!m_onTimeoutTimer.isActive()); + m_selfRef = this; + m_onAbortTimer.startOneShot(0); + m_backend.clear(); // Release the backend as it holds a (circular) reference back to us. +} + +void IDBTransaction::onComplete() +{ + ASSERT(!m_onAbortTimer.isActive()); + ASSERT(!m_onCompleteTimer.isActive()); + ASSERT(!m_onTimeoutTimer.isActive()); + m_selfRef = this; + m_onCompleteTimer.startOneShot(0); + m_backend.clear(); // Release the backend as it holds a (circular) reference back to us. +} + +void IDBTransaction::onTimeout() +{ + ASSERT(!m_onAbortTimer.isActive()); + ASSERT(!m_onCompleteTimer.isActive()); + ASSERT(!m_onTimeoutTimer.isActive()); + m_selfRef = this; + m_onTimeoutTimer.startOneShot(0); + m_backend.clear(); // Release the backend as it holds a (circular) reference back to us. +} + +bool IDBTransaction::canSuspend() const +{ + // We may be in the middle of a transaction so we cannot suspend our object. + // Instead, we simply don't allow the owner page to go into the back/forward cache. + return false; +} + +void IDBTransaction::stop() +{ + if (m_backend) + m_backend->abort(); +} + +EventTargetData* IDBTransaction::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* IDBTransaction::ensureEventTargetData() +{ + return &m_eventTargetData; +} + +void IDBTransaction::onAbortTimerFired(Timer<IDBTransaction>* transaction) +{ + ASSERT(m_selfRef); + RefPtr<IDBTransaction> selfRef = m_selfRef.release(); + dispatchEvent(IDBAbortEvent::create()); +} + +void IDBTransaction::onCompleteTimerFired(Timer<IDBTransaction>* transaction) +{ + ASSERT(m_selfRef); + RefPtr<IDBTransaction> selfRef = m_selfRef.release(); + dispatchEvent(IDBCompleteEvent::create()); +} + + +void IDBTransaction::onTimeoutTimerFired(Timer<IDBTransaction>* transaction) +{ + ASSERT(m_selfRef); + RefPtr<IDBTransaction> selfRef = m_selfRef.release(); + dispatchEvent(IDBTimeoutEvent::create()); +} + +} + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBTransaction.h b/Source/WebCore/storage/IDBTransaction.h new file mode 100644 index 0000000..d0a9f0f --- /dev/null +++ b/Source/WebCore/storage/IDBTransaction.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBTransaction_h +#define IDBTransaction_h + +#if ENABLE(INDEXED_DATABASE) + +#include "ActiveDOMObject.h" +#include "DOMStringList.h" +#include "EventListener.h" +#include "EventNames.h" +#include "EventTarget.h" +#include "IDBTransactionBackendInterface.h" +#include "IDBTransactionCallbacks.h" +#include "Timer.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + +class IDBDatabase; +class IDBObjectStore; + +class IDBTransaction : public IDBTransactionCallbacks, public EventTarget, public ActiveDOMObject { +public: + static PassRefPtr<IDBTransaction> create(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBDatabase* db) + { + return adoptRef(new IDBTransaction(context, backend, db)); + } + virtual ~IDBTransaction(); + + enum Mode { + READ_WRITE = 0, + READ_ONLY = 1, + VERSION_CHANGE = 2 + }; + + unsigned short mode() const; + IDBDatabase* db(); + PassRefPtr<IDBObjectStore> objectStore(const String& name, ExceptionCode&); + void abort(); + + DEFINE_ATTRIBUTE_EVENT_LISTENER(abort); + DEFINE_ATTRIBUTE_EVENT_LISTENER(complete); + DEFINE_ATTRIBUTE_EVENT_LISTENER(timeout); + + // IDBTransactionCallbacks + virtual void onAbort(); + virtual void onComplete(); + virtual void onTimeout(); + + // EventTarget + virtual IDBTransaction* toIDBTransaction() { return this; } + + // ActiveDOMObject + virtual ScriptExecutionContext* scriptExecutionContext() const; + virtual bool canSuspend() const; + virtual void stop(); + + using RefCounted<IDBTransactionCallbacks>::ref; + using RefCounted<IDBTransactionCallbacks>::deref; + +private: + IDBTransaction(ScriptExecutionContext*, PassRefPtr<IDBTransactionBackendInterface>, IDBDatabase*); + + // EventTarget + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + + void onAbortTimerFired(Timer<IDBTransaction>*); + void onCompleteTimerFired(Timer<IDBTransaction>*); + void onTimeoutTimerFired(Timer<IDBTransaction>*); + + EventTargetData m_eventTargetData; + RefPtr<IDBTransactionBackendInterface> m_backend; + RefPtr<IDBDatabase> m_database; + unsigned short m_mode; + + Timer<IDBTransaction> m_onAbortTimer; + Timer<IDBTransaction> m_onCompleteTimer; + Timer<IDBTransaction> m_onTimeoutTimer; + RefPtr<IDBTransaction> m_selfRef; // This is set to us iff there's an event pending. +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBTransaction_h diff --git a/Source/WebCore/storage/IDBTransaction.idl b/Source/WebCore/storage/IDBTransaction.idl new file mode 100644 index 0000000..b57ac4a --- /dev/null +++ b/Source/WebCore/storage/IDBTransaction.idl @@ -0,0 +1,58 @@ + /* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=INDEXED_DATABASE, + EventTarget + ] IDBTransaction { + // Modes + const unsigned short READ_WRITE = 0; + const unsigned short READ_ONLY = 1; + const unsigned short VERSION_CHANGE = 2; + + // Properties + readonly attribute unsigned short mode; + readonly attribute IDBDatabase db; + // Methods + IDBObjectStore objectStore (in DOMString name) + raises (IDBDatabaseException); + void abort (); + // Events + attribute EventListener onabort; + attribute EventListener oncomplete; + attribute EventListener ontimeout; + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; +} diff --git a/Source/WebCore/storage/IDBTransactionBackendImpl.cpp b/Source/WebCore/storage/IDBTransactionBackendImpl.cpp new file mode 100644 index 0000000..0012231 --- /dev/null +++ b/Source/WebCore/storage/IDBTransactionBackendImpl.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBTransactionBackendImpl.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabaseBackendImpl.h" +#include "IDBDatabaseException.h" +#include "IDBTransactionCoordinator.h" +#include "SQLiteDatabase.h" + +namespace WebCore { + +PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl* database) +{ + return adoptRef(new IDBTransactionBackendImpl(objectStores, mode, timeout, database)); +} + +IDBTransactionBackendImpl::IDBTransactionBackendImpl(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl* database) + : m_objectStoreNames(objectStores) + , m_mode(mode) + , m_timeout(timeout) // FIXME: Implement timeout. + , m_state(Unused) + , m_database(database) + , m_transaction(new SQLiteTransaction(database->sqliteDatabase())) + , m_taskTimer(this, &IDBTransactionBackendImpl::taskTimerFired) + , m_taskEventTimer(this, &IDBTransactionBackendImpl::taskEventTimerFired) + , m_pendingEvents(0) +{ + ASSERT(m_objectStoreNames); + m_database->transactionCoordinator()->didCreateTransaction(this); +} + +IDBTransactionBackendImpl::~IDBTransactionBackendImpl() +{ + // It shouldn't be possible for this object to get deleted until it's either complete or aborted. + ASSERT(m_state == Finished); +} + +PassRefPtr<IDBObjectStoreBackendInterface> IDBTransactionBackendImpl::objectStore(const String& name, ExceptionCode& ec) +{ + if (m_state == Finished) { + ec = IDBDatabaseException::NOT_ALLOWED_ERR; + return 0; + } + + // Does a linear search, but it really shouldn't be that slow in practice. + if (!m_objectStoreNames->isEmpty() && !m_objectStoreNames->contains(name)) { + ec = IDBDatabaseException::NOT_FOUND_ERR; + return 0; + } + + RefPtr<IDBObjectStoreBackendInterface> objectStore = m_database->objectStore(name); + // FIXME: This is only necessary right now beacuse a setVersion transaction could modify things + // between its creation (where another check occurs) and the .objectStore call. + // There's a bug to make this impossible in the spec. When we make it impossible here, we + // can remove this check. + if (!objectStore) { + ec = IDBDatabaseException::NOT_FOUND_ERR; + return 0; + } + return objectStore.release(); +} + +bool IDBTransactionBackendImpl::scheduleTask(PassOwnPtr<ScriptExecutionContext::Task> task, PassOwnPtr<ScriptExecutionContext::Task> abortTask) +{ + if (m_state == Finished) + return false; + + m_taskQueue.append(task); + if (abortTask) + m_abortTaskQueue.prepend(abortTask); + + if (m_state == Unused) + start(); + + return true; +} + +void IDBTransactionBackendImpl::abort() +{ + if (m_state == Finished) + return; + + // The last reference to this object may be released while performing the + // abort steps below. We therefore take a self reference to keep ourselves + // alive while executing this method. + RefPtr<IDBTransactionBackendImpl> self(this); + + m_state = Finished; + m_taskTimer.stop(); + m_taskEventTimer.stop(); + m_transaction->rollback(); + + // Run the abort tasks, if any. + while (!m_abortTaskQueue.isEmpty()) { + OwnPtr<ScriptExecutionContext::Task> task(m_abortTaskQueue.first().release()); + m_abortTaskQueue.removeFirst(); + task->performTask(0); + } + + m_callbacks->onAbort(); + m_database->transactionCoordinator()->didFinishTransaction(this); + ASSERT(!m_database->transactionCoordinator()->isActive(this)); + m_database = 0; +} + +void IDBTransactionBackendImpl::didCompleteTaskEvents() +{ + if (m_state == Finished) + return; + + ASSERT(m_state == Running); + ASSERT(m_pendingEvents); + m_pendingEvents--; + + if (!m_taskEventTimer.isActive()) + m_taskEventTimer.startOneShot(0); +} + +void IDBTransactionBackendImpl::run() +{ + ASSERT(m_state == StartPending || m_state == Running); + ASSERT(!m_taskTimer.isActive()); + + m_taskTimer.startOneShot(0); +} + +void IDBTransactionBackendImpl::start() +{ + ASSERT(m_state == Unused); + + m_state = StartPending; + m_database->transactionCoordinator()->didStartTransaction(this); +} + +void IDBTransactionBackendImpl::commit() +{ + // The last reference to this object may be released while performing the + // commit steps below. We therefore take a self reference to keep ourselves + // alive while executing this method. + RefPtr<IDBTransactionBackendImpl> self(this); + ASSERT(m_state == Running); + + m_state = Finished; + m_transaction->commit(); + m_callbacks->onComplete(); + m_database->transactionCoordinator()->didFinishTransaction(this); + m_database = 0; +} + +void IDBTransactionBackendImpl::taskTimerFired(Timer<IDBTransactionBackendImpl>*) +{ + ASSERT(!m_taskQueue.isEmpty()); + + if (m_state == StartPending) { + m_transaction->begin(); + m_state = Running; + } else + ASSERT(m_state == Running); + + TaskQueue queue; + queue.swap(m_taskQueue); + while (!queue.isEmpty() && m_state != Finished) { + OwnPtr<ScriptExecutionContext::Task> task(queue.first().release()); + queue.removeFirst(); + m_pendingEvents++; + task->performTask(0); + } +} + +void IDBTransactionBackendImpl::taskEventTimerFired(Timer<IDBTransactionBackendImpl>*) +{ + ASSERT(m_state == Running); + + if (!m_pendingEvents && m_taskQueue.isEmpty()) { + // The last task event has completed and the task + // queue is empty. Commit the transaction. + commit(); + return; + } + + // We are still waiting for other events to complete. However, + // the task queue is non-empty and the timer is inactive. + // We can therfore schedule the timer again. + if (!m_taskQueue.isEmpty() && !m_taskTimer.isActive()) + m_taskTimer.startOneShot(0); +} + +}; + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBTransactionBackendImpl.h b/Source/WebCore/storage/IDBTransactionBackendImpl.h new file mode 100644 index 0000000..f4dfaa8 --- /dev/null +++ b/Source/WebCore/storage/IDBTransactionBackendImpl.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBTransactionBackendImpl_h +#define IDBTransactionBackendImpl_h + +#if ENABLE(INDEXED_DATABASE) + +#include "DOMStringList.h" +#include "IDBTransactionBackendInterface.h" +#include "IDBTransactionCallbacks.h" +#include "SQLiteTransaction.h" +#include "Timer.h" +#include <wtf/Deque.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBDatabaseBackendImpl; + +class IDBTransactionBackendImpl : public IDBTransactionBackendInterface { +public: + static PassRefPtr<IDBTransactionBackendImpl> create(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl*); + virtual ~IDBTransactionBackendImpl(); + + virtual PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name, ExceptionCode&); + virtual unsigned short mode() const { return m_mode; } + virtual bool scheduleTask(PassOwnPtr<ScriptExecutionContext::Task> task, PassOwnPtr<ScriptExecutionContext::Task> abortTask); + virtual void didCompleteTaskEvents(); + virtual void abort(); + virtual void setCallbacks(IDBTransactionCallbacks* callbacks) { m_callbacks = callbacks; } + + void run(); + +private: + IDBTransactionBackendImpl(DOMStringList* objectStores, unsigned short mode, unsigned long timeout, IDBDatabaseBackendImpl*); + + enum State { + Unused, // Created, but no tasks yet. + StartPending, // Enqueued tasks, but SQLite transaction not yet started. + Running, // SQLite transaction started but not yet finished. + Finished, // Either aborted or committed. + }; + + void start(); + void commit(); + + void taskTimerFired(Timer<IDBTransactionBackendImpl>*); + void taskEventTimerFired(Timer<IDBTransactionBackendImpl>*); + + RefPtr<DOMStringList> m_objectStoreNames; + unsigned short m_mode; + unsigned long m_timeout; + + State m_state; + RefPtr<IDBTransactionCallbacks> m_callbacks; + RefPtr<IDBDatabaseBackendImpl> m_database; + + typedef Deque<OwnPtr<ScriptExecutionContext::Task> > TaskQueue; + TaskQueue m_taskQueue; + TaskQueue m_abortTaskQueue; + + OwnPtr<SQLiteTransaction> m_transaction; + + // FIXME: delete the timer once we have threads instead. + Timer<IDBTransactionBackendImpl> m_taskTimer; + Timer<IDBTransactionBackendImpl> m_taskEventTimer; + int m_pendingEvents; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBTransactionBackendImpl_h diff --git a/Source/WebCore/storage/IDBTransactionBackendInterface.h b/Source/WebCore/storage/IDBTransactionBackendInterface.h new file mode 100644 index 0000000..9b1fce6 --- /dev/null +++ b/Source/WebCore/storage/IDBTransactionBackendInterface.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBTransactionBackendInterface_h +#define IDBTransactionBackendInterface_h + +#include "ExceptionCode.h" +#include "IDBCallbacks.h" +#include "PlatformString.h" +#include "ScriptExecutionContext.h" +#include <wtf/Threading.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBObjectStoreBackendInterface; +class IDBTransactionCallbacks; +class SQLiteDatabase; + +// This class is shared by IDBTransaction (async) and IDBTransactionSync (sync). +// This is implemented by IDBTransactionBackendImpl and optionally others (in order to proxy +// calls across process barriers). All calls to these classes should be non-blocking and +// trigger work on a background thread if necessary. +class IDBTransactionBackendInterface : public ThreadSafeShared<IDBTransactionBackendInterface> { +public: + virtual ~IDBTransactionBackendInterface() { } + + virtual PassRefPtr<IDBObjectStoreBackendInterface> objectStore(const String& name, ExceptionCode&) = 0; + virtual unsigned short mode() const = 0; + virtual bool scheduleTask(PassOwnPtr<ScriptExecutionContext::Task> task, PassOwnPtr<ScriptExecutionContext::Task> abortTask = 0) = 0; + virtual void didCompleteTaskEvents() = 0; + virtual void abort() = 0; + virtual void setCallbacks(IDBTransactionCallbacks*) = 0; +}; + +} // namespace WebCore + +#endif + +#endif // IDBTransactionBackendInterface_h + diff --git a/Source/WebCore/storage/IDBTransactionCallbacks.h b/Source/WebCore/storage/IDBTransactionCallbacks.h new file mode 100644 index 0000000..348608d --- /dev/null +++ b/Source/WebCore/storage/IDBTransactionCallbacks.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBTransactionCallbacks_h +#define IDBTransactionCallbacks_h + +#if ENABLE(INDEXED_DATABASE) + +#include "SerializedScriptValue.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + +class IDBTransactionCallbacks : public RefCounted<IDBTransactionCallbacks> { +public: + virtual ~IDBTransactionCallbacks() { } + + virtual void onAbort() = 0; + virtual void onComplete() = 0; + virtual void onTimeout() = 0; +}; + +} // namespace WebCore + +#endif + +#endif // IDBTransactionCallbacks_h diff --git a/Source/WebCore/storage/IDBTransactionCoordinator.cpp b/Source/WebCore/storage/IDBTransactionCoordinator.cpp new file mode 100644 index 0000000..f867f42 --- /dev/null +++ b/Source/WebCore/storage/IDBTransactionCoordinator.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBTransactionCoordinator.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabaseBackendImpl.h" +#include "IDBObjectStoreBackendInterface.h" +#include "IDBTransactionBackendImpl.h" +#include "SQLiteDatabase.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +PassRefPtr<IDBTransactionCoordinator> IDBTransactionCoordinator::create() +{ + return adoptRef(new IDBTransactionCoordinator()); +} + +IDBTransactionCoordinator::IDBTransactionCoordinator() +{ +} + +IDBTransactionCoordinator::~IDBTransactionCoordinator() +{ +} + +void IDBTransactionCoordinator::didCreateTransaction(IDBTransactionBackendImpl* transaction) +{ + ASSERT(!m_transactions.contains(transaction)); + m_transactions.add(transaction, transaction); +} + +void IDBTransactionCoordinator::didStartTransaction(IDBTransactionBackendImpl* transaction) +{ + ASSERT(m_transactions.contains(transaction)); + + m_startedTransactions.add(transaction); + processStartedTransactions(); +} + +void IDBTransactionCoordinator::didFinishTransaction(IDBTransactionBackendImpl* transaction) +{ + ASSERT(m_transactions.contains(transaction)); + + if (m_startedTransactions.contains(transaction)) { + ASSERT(!m_runningTransactions.contains(transaction)); + m_startedTransactions.remove(transaction); + } else if (m_runningTransactions.contains(transaction)) + m_runningTransactions.remove(transaction); + + m_transactions.remove(transaction); + + processStartedTransactions(); +} + +#ifndef NDEBUG +// Verifies internal consistiency while returning whether anything is found. +bool IDBTransactionCoordinator::isActive(IDBTransactionBackendImpl* transaction) +{ + bool found = false; + if (m_startedTransactions.contains(transaction)) + found = true; + if (m_runningTransactions.contains(transaction)) { + ASSERT(!found); + found = true; + } + ASSERT(found == m_transactions.contains(transaction)); + return found; +} +#endif + +void IDBTransactionCoordinator::processStartedTransactions() +{ + // FIXME: For now, we only allow one transaction to run at a time. + if (!m_runningTransactions.isEmpty()) + return; + + if (m_startedTransactions.isEmpty()) + return; + + IDBTransactionBackendImpl* transaction = *m_startedTransactions.begin(); + m_startedTransactions.remove(transaction); + m_runningTransactions.add(transaction); + transaction->run(); +} + +}; + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/IDBTransactionCoordinator.h b/Source/WebCore/storage/IDBTransactionCoordinator.h new file mode 100644 index 0000000..ef99e73 --- /dev/null +++ b/Source/WebCore/storage/IDBTransactionCoordinator.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef IDBTransactionCoordinator_h +#define IDBTransactionCoordinator_h + +#if ENABLE(INDEXED_DATABASE) + +#include "DOMStringList.h" +#include "IDBTransactionBackendInterface.h" +#include <wtf/ListHashSet.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class IDBTransactionBackendImpl; +class IDBTransactionCallbacks; +class IDBDatabaseBackendImpl; + +// Transactions are executed in the order the were created. +class IDBTransactionCoordinator : public RefCounted<IDBTransactionCoordinator> { +public: + static PassRefPtr<IDBTransactionCoordinator> create(); + virtual ~IDBTransactionCoordinator(); + + // Called by transactions as they start and finish. + void didCreateTransaction(IDBTransactionBackendImpl*); + void didStartTransaction(IDBTransactionBackendImpl*); + void didFinishTransaction(IDBTransactionBackendImpl*); + +#ifndef NDEBUG + bool isActive(IDBTransactionBackendImpl*); +#endif + +private: + IDBTransactionCoordinator(); + + void processStartedTransactions(); + + // This is just an efficient way to keep references to all transactions. + HashMap<IDBTransactionBackendImpl*, RefPtr<IDBTransactionBackendImpl> > m_transactions; + // Transactions in different states are grouped below. + ListHashSet<IDBTransactionBackendImpl*> m_startedTransactions; + HashSet<IDBTransactionBackendImpl*> m_runningTransactions; +}; + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + +#endif // IDBTransactionCoordinator_h diff --git a/Source/WebCore/storage/LocalStorageTask.cpp b/Source/WebCore/storage/LocalStorageTask.cpp new file mode 100644 index 0000000..d31c991 --- /dev/null +++ b/Source/WebCore/storage/LocalStorageTask.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "LocalStorageTask.h" + +#if ENABLE(DOM_STORAGE) + +#include "LocalStorageThread.h" +#include "StorageAreaSync.h" + +namespace WebCore { + +LocalStorageTask::LocalStorageTask(Type type, StorageAreaSync* area) + : m_type(type) + , m_area(area) + , m_thread(0) +{ + ASSERT(m_area); + ASSERT(m_type == AreaImport || m_type == AreaSync || m_type == DeleteEmptyDatabase); +} + +LocalStorageTask::LocalStorageTask(Type type, LocalStorageThread* thread) + : m_type(type) + , m_area(0) + , m_thread(thread) +{ + ASSERT(m_thread); + ASSERT(m_type == TerminateThread); +} + +LocalStorageTask::~LocalStorageTask() +{ +} + +void LocalStorageTask::performTask() +{ + switch (m_type) { + case AreaImport: + m_area->performImport(); + break; + case AreaSync: + m_area->performSync(); + break; + case DeleteEmptyDatabase: + m_area->deleteEmptyDatabase(); + break; + case TerminateThread: + m_thread->performTerminate(); + break; + } +} + +} + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/LocalStorageTask.h b/Source/WebCore/storage/LocalStorageTask.h new file mode 100644 index 0000000..a2e35ea --- /dev/null +++ b/Source/WebCore/storage/LocalStorageTask.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef LocalStorageTask_h +#define LocalStorageTask_h + +#if ENABLE(DOM_STORAGE) + +#include <wtf/PassOwnPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + + class StorageAreaSync; + class LocalStorageThread; + + // FIXME: Rename this class to StorageTask + class LocalStorageTask : public Noncopyable { + public: + enum Type { AreaImport, AreaSync, DeleteEmptyDatabase, TerminateThread }; + + ~LocalStorageTask(); + + static PassOwnPtr<LocalStorageTask> createImport(StorageAreaSync* area) { return adoptPtr(new LocalStorageTask(AreaImport, area)); } + static PassOwnPtr<LocalStorageTask> createSync(StorageAreaSync* area) { return adoptPtr(new LocalStorageTask(AreaSync, area)); } + static PassOwnPtr<LocalStorageTask> createDeleteEmptyDatabase(StorageAreaSync* area) { return adoptPtr(new LocalStorageTask(DeleteEmptyDatabase, area)); } + static PassOwnPtr<LocalStorageTask> createTerminate(LocalStorageThread* thread) { return adoptPtr(new LocalStorageTask(TerminateThread, thread)); } + + void performTask(); + + private: + LocalStorageTask(Type, StorageAreaSync*); + LocalStorageTask(Type, LocalStorageThread*); + + Type m_type; + StorageAreaSync* m_area; + LocalStorageThread* m_thread; + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // LocalStorageTask_h diff --git a/Source/WebCore/storage/LocalStorageThread.cpp b/Source/WebCore/storage/LocalStorageThread.cpp new file mode 100644 index 0000000..cbb81c5 --- /dev/null +++ b/Source/WebCore/storage/LocalStorageThread.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "LocalStorageThread.h" + +#if ENABLE(DOM_STORAGE) + +#include "LocalStorageTask.h" +#include "StorageAreaSync.h" + +namespace WebCore { + +PassOwnPtr<LocalStorageThread> LocalStorageThread::create() +{ + return adoptPtr(new LocalStorageThread); +} + +LocalStorageThread::LocalStorageThread() + : m_threadID(0) +{ +} + +LocalStorageThread::~LocalStorageThread() +{ + ASSERT(isMainThread()); + ASSERT(!m_threadID); +} + +bool LocalStorageThread::start() +{ + ASSERT(isMainThread()); + if (!m_threadID) + m_threadID = createThread(LocalStorageThread::threadEntryPointCallback, this, "WebCore: LocalStorage"); + return m_threadID; +} + +void* LocalStorageThread::threadEntryPointCallback(void* thread) +{ + return static_cast<LocalStorageThread*>(thread)->threadEntryPoint(); +} + +void* LocalStorageThread::threadEntryPoint() +{ + ASSERT(!isMainThread()); + while (OwnPtr<LocalStorageTask> task = m_queue.waitForMessage()) + task->performTask(); + + return 0; +} + +void LocalStorageThread::scheduleTask(PassOwnPtr<LocalStorageTask> task) +{ + ASSERT(isMainThread()); + ASSERT(!m_queue.killed() && m_threadID); + m_queue.append(task); +} + +void LocalStorageThread::terminate() +{ + ASSERT(isMainThread()); + ASSERT(!m_queue.killed() && m_threadID); + // Even in weird, exceptional cases, don't wait on a nonexistent thread to terminate. + if (!m_threadID) + return; + + void* returnValue; + m_queue.append(LocalStorageTask::createTerminate(this)); + waitForThreadCompletion(m_threadID, &returnValue); + ASSERT(m_queue.killed()); + m_threadID = 0; +} + +void LocalStorageThread::performTerminate() +{ + ASSERT(!isMainThread()); + m_queue.kill(); +} + +} + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/LocalStorageThread.h b/Source/WebCore/storage/LocalStorageThread.h new file mode 100644 index 0000000..6f05911 --- /dev/null +++ b/Source/WebCore/storage/LocalStorageThread.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef LocalStorageThread_h +#define LocalStorageThread_h + +#if ENABLE(DOM_STORAGE) + +#include <wtf/HashSet.h> +#include <wtf/MessageQueue.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +namespace WebCore { + + class StorageAreaSync; + class LocalStorageTask; + + // FIXME: Rename this class to StorageThread + class LocalStorageThread : public Noncopyable { + public: + static PassOwnPtr<LocalStorageThread> create(); + ~LocalStorageThread(); + + bool start(); + void terminate(); + void scheduleTask(PassOwnPtr<LocalStorageTask>); + + // Background thread part of the terminate procedure. + void performTerminate(); + + private: + LocalStorageThread(); + + // Called on background thread. + static void* threadEntryPointCallback(void*); + void* threadEntryPoint(); + + ThreadIdentifier m_threadID; + MessageQueue<LocalStorageTask> m_queue; + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // LocalStorageThread_h diff --git a/Source/WebCore/storage/OriginQuotaManager.cpp b/Source/WebCore/storage/OriginQuotaManager.cpp new file mode 100644 index 0000000..0f415c1 --- /dev/null +++ b/Source/WebCore/storage/OriginQuotaManager.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "OriginQuotaManager.h" + +#if ENABLE(DATABASE) + +#include "AbstractDatabase.h" +#include "OriginUsageRecord.h" + +namespace WebCore { + +OriginQuotaManager::OriginQuotaManager() +#ifndef NDEBUG + : m_usageRecordGuardLocked(false) +#endif +{ +} + +bool OriginQuotaManager::tryLock() +{ + bool locked = m_usageRecordGuard.tryLock(); +#ifndef NDEBUG + if (locked) + m_usageRecordGuardLocked = true; + else + ASSERT(m_usageRecordGuardLocked); +#endif + return locked; +} + +void OriginQuotaManager::lock() +{ + m_usageRecordGuard.lock(); +#ifndef NDEBUG + m_usageRecordGuardLocked = true; +#endif +} + +void OriginQuotaManager::unlock() +{ +#ifndef NDEBUG + m_usageRecordGuardLocked = false; +#endif + m_usageRecordGuard.unlock(); +} + +void OriginQuotaManager::trackOrigin(PassRefPtr<SecurityOrigin> origin) +{ + ASSERT(m_usageRecordGuardLocked); + ASSERT(!m_usageMap.contains(origin.get())); + + m_usageMap.set(origin->threadsafeCopy(), new OriginUsageRecord); +} + +bool OriginQuotaManager::tracksOrigin(SecurityOrigin* origin) const +{ + ASSERT(m_usageRecordGuardLocked); + return m_usageMap.contains(origin); +} + +void OriginQuotaManager::addDatabase(SecurityOrigin* origin, const String& databaseIdentifier, const String& fullPath) +{ + ASSERT(m_usageRecordGuardLocked); + + OriginUsageRecord* usageRecord = m_usageMap.get(origin); + ASSERT(usageRecord); + + usageRecord->addDatabase(databaseIdentifier.threadsafeCopy(), fullPath.threadsafeCopy()); +} + +void OriginQuotaManager::removeDatabase(SecurityOrigin* origin, const String& databaseIdentifier) +{ + ASSERT(m_usageRecordGuardLocked); + + if (OriginUsageRecord* usageRecord = m_usageMap.get(origin)) + usageRecord->removeDatabase(databaseIdentifier); +} + +void OriginQuotaManager::removeOrigin(SecurityOrigin* origin) +{ + ASSERT(m_usageRecordGuardLocked); + + if (OriginUsageRecord* usageRecord = m_usageMap.get(origin)) { + m_usageMap.remove(origin); + delete usageRecord; + } +} + +void OriginQuotaManager::markDatabase(AbstractDatabase* database) +{ + ASSERT(database); + ASSERT(m_usageRecordGuardLocked); + OriginUsageRecord* usageRecord = m_usageMap.get(database->securityOrigin()); + ASSERT(usageRecord); + + usageRecord->markDatabase(database->stringIdentifier()); +} + +unsigned long long OriginQuotaManager::diskUsage(SecurityOrigin* origin) const +{ + ASSERT(m_usageRecordGuardLocked); + + OriginUsageRecord* usageRecord = m_usageMap.get(origin); + ASSERT(usageRecord); + + return usageRecord->diskUsage(); +} + +} + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/OriginQuotaManager.h b/Source/WebCore/storage/OriginQuotaManager.h new file mode 100644 index 0000000..ec9620c --- /dev/null +++ b/Source/WebCore/storage/OriginQuotaManager.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef OriginQuotaManager_h +#define OriginQuotaManager_h + +#if ENABLE(DATABASE) + +#include "SecurityOriginHash.h" +#include <wtf/HashMap.h> +#include <wtf/Threading.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +class AbstractDatabase; +class OriginUsageRecord; + +class OriginQuotaManager : public Noncopyable { +public: + OriginQuotaManager(); + + bool tryLock(); + void lock(); + void unlock(); + + void trackOrigin(PassRefPtr<SecurityOrigin>); + bool tracksOrigin(SecurityOrigin*) const; + void addDatabase(SecurityOrigin*, const String& databaseIdentifier, const String& fullPath); + void removeDatabase(SecurityOrigin*, const String& databaseIdentifier); + void removeOrigin(SecurityOrigin*); + + void markDatabase(AbstractDatabase*); // Mark dirtiness of a specific database. + unsigned long long diskUsage(SecurityOrigin*) const; + +private: + mutable Mutex m_usageRecordGuard; +#ifndef NDEBUG + bool m_usageRecordGuardLocked; +#endif + + typedef HashMap<RefPtr<SecurityOrigin>, OriginUsageRecord*, SecurityOriginHash> OriginUsageMap; + OriginUsageMap m_usageMap; +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) + +#endif // OriginQuotaManager_h diff --git a/Source/WebCore/storage/OriginUsageRecord.cpp b/Source/WebCore/storage/OriginUsageRecord.cpp new file mode 100644 index 0000000..eac08fd --- /dev/null +++ b/Source/WebCore/storage/OriginUsageRecord.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "OriginUsageRecord.h" + +#if ENABLE(DATABASE) + +#include "SQLiteFileSystem.h" + +namespace WebCore { + +OriginUsageRecord::OriginUsageRecord() + : m_cachedDiskUsageIsValid(false) +{ +} + +void OriginUsageRecord::addDatabase(const String& identifier, const String& fullPath) +{ + ASSERT(!m_databaseMap.contains(identifier)); + ASSERT_ARG(identifier, identifier.impl()->hasOneRef() || identifier.isEmpty()); + ASSERT_ARG(fullPath, fullPath.impl()->hasOneRef() || fullPath.isEmpty()); + + m_databaseMap.set(identifier, DatabaseEntry(fullPath)); + m_unknownSet.add(identifier); + + m_cachedDiskUsageIsValid = false; +} + +void OriginUsageRecord::removeDatabase(const String& identifier) +{ + ASSERT(m_databaseMap.contains(identifier)); + + m_databaseMap.remove(identifier); + m_unknownSet.remove(identifier); + m_cachedDiskUsageIsValid = false; +} + +void OriginUsageRecord::markDatabase(const String& identifier) +{ + ASSERT(m_databaseMap.contains(identifier)); + ASSERT_ARG(identifier, identifier.impl()->hasOneRef() || identifier.isEmpty()); + + m_unknownSet.add(identifier); + m_cachedDiskUsageIsValid = false; +} + +unsigned long long OriginUsageRecord::diskUsage() +{ + // Use the last cached usage value if we have it. + if (m_cachedDiskUsageIsValid) + return m_cachedDiskUsage; + + // stat() for the sizes known to be dirty. + HashSet<String>::iterator iUnknown = m_unknownSet.begin(); + HashSet<String>::iterator endUnknown = m_unknownSet.end(); + for (; iUnknown != endUnknown; ++iUnknown) { + const String& path = m_databaseMap.get(*iUnknown).filename; + ASSERT(!path.isEmpty()); + + // When we can't determine the file size, we'll just have to assume the file is missing/inaccessible. + long long size = SQLiteFileSystem::getDatabaseFileSize(path); + m_databaseMap.set(*iUnknown, DatabaseEntry(path, size)); + } + m_unknownSet.clear(); + + // Recalculate the cached usage value. + m_cachedDiskUsage = 0; + HashMap<String, DatabaseEntry>::iterator iDatabase = m_databaseMap.begin(); + HashMap<String, DatabaseEntry>::iterator endDatabase = m_databaseMap.end(); + for (; iDatabase != endDatabase; ++iDatabase) + m_cachedDiskUsage += iDatabase->second.size; + + m_cachedDiskUsageIsValid = true; + return m_cachedDiskUsage; +} + +} + +#endif diff --git a/Source/WebCore/storage/OriginUsageRecord.h b/Source/WebCore/storage/OriginUsageRecord.h new file mode 100644 index 0000000..a830e68 --- /dev/null +++ b/Source/WebCore/storage/OriginUsageRecord.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef OriginUsageRecord_h +#define OriginUsageRecord_h + +#if ENABLE(DATABASE) + +#include "PlatformString.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +// Objects of this class can be used from multiple threads with external synchronization. +// String arguments are also supposed to be deeply copied by the caller when necessary. +class OriginUsageRecord : public Noncopyable { +public: + OriginUsageRecord(); + + void addDatabase(const String& identifier, const String& fullPath); + void removeDatabase(const String& identifier); + void markDatabase(const String& identifier); // Size may have changed, and will need to be recalculated. + unsigned long long diskUsage(); + +private: + struct DatabaseEntry { + DatabaseEntry() { } + DatabaseEntry(const String& filename) : filename(filename) { } + DatabaseEntry(const String& filename, unsigned long long size) : filename(filename), size(size) { } + String filename; + unsigned long long size; // This member remains uninitialized until disk usage is calculated. + }; + HashMap<String, DatabaseEntry> m_databaseMap; + HashSet<String> m_unknownSet; + + unsigned long long m_cachedDiskUsage; + bool m_cachedDiskUsageIsValid; +}; + +} // namespace WebCore + +#endif + +#endif diff --git a/Source/WebCore/storage/SQLError.h b/Source/WebCore/storage/SQLError.h new file mode 100644 index 0000000..496145a --- /dev/null +++ b/Source/WebCore/storage/SQLError.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef SQLError_h +#define SQLError_h + +#if ENABLE(DATABASE) + +#include "PlatformString.h" +#include <wtf/ThreadSafeShared.h> + +namespace WebCore { + +class SQLError : public ThreadSafeShared<SQLError> { +public: + static PassRefPtr<SQLError> create(unsigned code, const String& message) { return adoptRef(new SQLError(code, message)); } + + unsigned code() const { return m_code; } + String message() const { return m_message.threadsafeCopy(); } + + enum SQLErrorCode { + UNKNOWN_ERR = 0, + DATABASE_ERR = 1, + VERSION_ERR = 2, + TOO_LARGE_ERR = 3, + QUOTA_ERR = 4, + SYNTAX_ERR = 5, + CONSTRAINT_ERR = 6, + TIMEOUT_ERR = 7 + }; + +private: + SQLError(unsigned code, const String& message) : m_code(code), m_message(message.threadsafeCopy()) { } + unsigned m_code; + String m_message; +}; + +} + +#endif + +#endif // SQLError_h diff --git a/Source/WebCore/storage/SQLError.idl b/Source/WebCore/storage/SQLError.idl new file mode 100644 index 0000000..87be8c7 --- /dev/null +++ b/Source/WebCore/storage/SQLError.idl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=DATABASE, + OmitConstructor, + NoStaticTables + ] SQLError { + readonly attribute unsigned long code; + readonly attribute DOMString message; + + // SQLErrorCode: used only in the async DB API + const unsigned short UNKNOWN_ERR = 0; + const unsigned short DATABASE_ERR = 1; + const unsigned short VERSION_ERR = 2; + const unsigned short TOO_LARGE_ERR = 3; + const unsigned short QUOTA_ERR = 4; + const unsigned short SYNTAX_ERR = 5; + const unsigned short CONSTRAINT_ERR = 6; + const unsigned short TIMEOUT_ERR = 7; + }; +} diff --git a/Source/WebCore/storage/SQLException.h b/Source/WebCore/storage/SQLException.h new file mode 100644 index 0000000..a0f118d --- /dev/null +++ b/Source/WebCore/storage/SQLException.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef SQLException_h +#define SQLException_h + +#if ENABLE(DATABASE) + +#include "ExceptionBase.h" + +namespace WebCore { + +class SQLException : public ExceptionBase { +public: + static PassRefPtr<SQLException> create(const ExceptionCodeDescription& description) + { + return adoptRef(new SQLException(description)); + } + + static const int SQLExceptionOffset = 1000; + static const int SQLExceptionMax = 1099; + + enum SQLExceptionCode { + UNKNOWN_ERR = SQLExceptionOffset, + DATABASE_ERR = SQLExceptionOffset + 1, + VERSION_ERR = SQLExceptionOffset + 2, + TOO_LARGE_ERR = SQLExceptionOffset + 3, + QUOTA_ERR = SQLExceptionOffset + 4, + SYNTAX_ERR = SQLExceptionOffset + 5, + CONSTRAINT_ERR = SQLExceptionOffset + 6, + TIMEOUT_ERR = SQLExceptionOffset + 7 + }; + +private: + SQLException(const ExceptionCodeDescription& description) + : ExceptionBase(description) + { + } +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) + +#endif // SQLException_h diff --git a/Source/WebCore/storage/SQLException.idl b/Source/WebCore/storage/SQLException.idl new file mode 100644 index 0000000..cbbc311 --- /dev/null +++ b/Source/WebCore/storage/SQLException.idl @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +module storage { + + interface [ + Conditional=DATABASE, + NoStaticTables, + DontCheckEnums + ] SQLException { + readonly attribute unsigned long code; + readonly attribute DOMString message; + + // SQLExceptionCode: used only in the sync DB API + const unsigned short UNKNOWN_ERR = 0; + const unsigned short DATABASE_ERR = 1; + const unsigned short VERSION_ERR = 2; + const unsigned short TOO_LARGE_ERR = 3; + const unsigned short QUOTA_ERR = 4; + const unsigned short SYNTAX_ERR = 5; + const unsigned short CONSTRAINT_ERR = 6; + const unsigned short TIMEOUT_ERR = 7; + }; +} diff --git a/Source/WebCore/storage/SQLResultSet.cpp b/Source/WebCore/storage/SQLResultSet.cpp new file mode 100644 index 0000000..7482628 --- /dev/null +++ b/Source/WebCore/storage/SQLResultSet.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "SQLResultSet.h" + +#if ENABLE(DATABASE) + +namespace WebCore { + +static unsigned const MaxErrorCode = 2; + +SQLResultSet::SQLResultSet() + : m_rows(SQLResultSetRowList::create()) + , m_insertId(0) + , m_insertIdSet(false) + , m_rowsAffected(0) +{ +} + +int64_t SQLResultSet::insertId(ExceptionCode& e) const +{ + // 4.11.4 - Return the id of the last row inserted as a result of the query + // If the query didn't result in any rows being added, raise an INVALID_ACCESS_ERR exception + if (m_insertIdSet) + return m_insertId; + + e = INVALID_ACCESS_ERR; + return -1; +} + +int SQLResultSet::rowsAffected() const +{ + return m_rowsAffected; +} + +SQLResultSetRowList* SQLResultSet::rows() const +{ + return m_rows.get(); +} + +void SQLResultSet::setInsertId(int64_t id) +{ + ASSERT(!m_insertIdSet); + + m_insertId = id; + m_insertIdSet = true; +} + +void SQLResultSet::setRowsAffected(int count) +{ + m_rowsAffected = count; +} + +} + +#endif diff --git a/Source/WebCore/storage/SQLResultSet.h b/Source/WebCore/storage/SQLResultSet.h new file mode 100644 index 0000000..268472f --- /dev/null +++ b/Source/WebCore/storage/SQLResultSet.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef SQLResultSet_h +#define SQLResultSet_h + +#if ENABLE(DATABASE) + +#include "ExceptionCode.h" +#include "SQLResultSetRowList.h" +#include <wtf/ThreadSafeShared.h> + +namespace WebCore { + +class SQLResultSet : public ThreadSafeShared<SQLResultSet> { +public: + static PassRefPtr<SQLResultSet> create() { return adoptRef(new SQLResultSet); } + + SQLResultSetRowList* rows() const; + + int64_t insertId(ExceptionCode&) const; + int rowsAffected() const; + +// For internal (non-JS) use + void setInsertId(int64_t); + void setRowsAffected(int); + +private: + SQLResultSet(); + + RefPtr<SQLResultSetRowList> m_rows; + int64_t m_insertId; + bool m_insertIdSet; + int m_rowsAffected; +}; + +} // namespace WebCore + +#endif + +#endif // SQLResultSet_h diff --git a/Source/WebCore/storage/SQLResultSet.idl b/Source/WebCore/storage/SQLResultSet.idl new file mode 100644 index 0000000..52f06da --- /dev/null +++ b/Source/WebCore/storage/SQLResultSet.idl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=DATABASE, + OmitConstructor, + NoStaticTables + ] SQLResultSet { + readonly attribute SQLResultSetRowList rows; + +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP + readonly attribute long insertId + getter raises(DOMException); +#else + // Explicitely choose 'long long' here to avoid a 64bit->32bit shortening warning for us. + readonly attribute long long insertId + getter raises(DOMException); +#endif + readonly attribute long rowsAffected; + }; +} diff --git a/Source/WebCore/storage/SQLResultSetRowList.cpp b/Source/WebCore/storage/SQLResultSetRowList.cpp new file mode 100644 index 0000000..c1e4844 --- /dev/null +++ b/Source/WebCore/storage/SQLResultSetRowList.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "SQLResultSetRowList.h" + +#if ENABLE(DATABASE) + +namespace WebCore { + +unsigned SQLResultSetRowList::length() const +{ + if (m_result.size() == 0) + return 0; + + ASSERT(m_result.size() % m_columns.size() == 0); + + return m_result.size() / m_columns.size(); +} + +} + +#endif diff --git a/Source/WebCore/storage/SQLResultSetRowList.h b/Source/WebCore/storage/SQLResultSetRowList.h new file mode 100644 index 0000000..92b5ec0 --- /dev/null +++ b/Source/WebCore/storage/SQLResultSetRowList.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef SQLResultSetRowList_h +#define SQLResultSetRowList_h + +#if ENABLE(DATABASE) + +#include <wtf/PassRefPtr.h> +#include "SQLValue.h" + +namespace WebCore { + +class SQLResultSetRowList : public RefCounted<SQLResultSetRowList> { +public: + static PassRefPtr<SQLResultSetRowList> create() { return adoptRef(new SQLResultSetRowList); } + + const Vector<String>& columnNames() const { return m_columns; } + const Vector<SQLValue>& values() const { return m_result; } + + void addColumn(const String& name) { m_columns.append(name); } + void addResult(const SQLValue& result) { m_result.append(result); } + + unsigned length() const; + +private: + SQLResultSetRowList() { } + + Vector<String> m_columns; + Vector<SQLValue> m_result; +}; + +} + +#endif + +#endif diff --git a/Source/WebCore/storage/SQLResultSetRowList.idl b/Source/WebCore/storage/SQLResultSetRowList.idl new file mode 100644 index 0000000..26239cb --- /dev/null +++ b/Source/WebCore/storage/SQLResultSetRowList.idl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=DATABASE, + OmitConstructor, + NoStaticTables + ] SQLResultSetRowList { + readonly attribute unsigned long length; + [Custom] DOMObject item(in unsigned long index); + }; +} diff --git a/Source/WebCore/storage/SQLStatement.cpp b/Source/WebCore/storage/SQLStatement.cpp new file mode 100644 index 0000000..306f561 --- /dev/null +++ b/Source/WebCore/storage/SQLStatement.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "SQLStatement.h" + +#if ENABLE(DATABASE) + +#include "Database.h" +#include "Logging.h" +#include "SQLError.h" +#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" +#include "SQLStatementCallback.h" +#include "SQLStatementErrorCallback.h" +#include "SQLTransaction.h" +#include "SQLValue.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +PassRefPtr<SQLStatement> SQLStatement::create(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback, int permissions) +{ + return adoptRef(new SQLStatement(statement, arguments, callback, errorCallback, permissions)); +} + +SQLStatement::SQLStatement(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback, int permissions) + : m_statement(statement.crossThreadString()) + , m_arguments(arguments) + , m_statementCallback(callback) + , m_statementErrorCallback(errorCallback) + , m_permissions(permissions) +{ +} + +bool SQLStatement::execute(Database* db) +{ + ASSERT(!m_resultSet); + + // If we're re-running this statement after a quota violation, we need to clear that error now + clearFailureDueToQuota(); + + // This transaction might have been marked bad while it was being set up on the main thread, + // so if there is still an error, return false. + if (m_error) + return false; + + db->setAuthorizerPermissions(m_permissions); + + SQLiteDatabase* database = &db->sqliteDatabase(); + + SQLiteStatement statement(*database, m_statement); + int result = statement.prepare(); + + if (result != SQLResultOk) { + LOG(StorageAPI, "Unable to verify correctness of statement %s - error %i (%s)", m_statement.ascii().data(), result, database->lastErrorMsg()); + m_error = SQLError::create(result == SQLResultInterrupt ? SQLError::DATABASE_ERR : SQLError::SYNTAX_ERR, database->lastErrorMsg()); + return false; + } + + // FIXME: If the statement uses the ?### syntax supported by sqlite, the bind parameter count is very likely off from the number of question marks. + // If this is the case, they might be trying to do something fishy or malicious + if (statement.bindParameterCount() != m_arguments.size()) { + LOG(StorageAPI, "Bind parameter count doesn't match number of question marks"); + m_error = SQLError::create(db->isInterrupted() ? SQLError::DATABASE_ERR : SQLError::SYNTAX_ERR, "number of '?'s in statement string does not match argument count"); + return false; + } + + for (unsigned i = 0; i < m_arguments.size(); ++i) { + result = statement.bindValue(i + 1, m_arguments[i]); + if (result == SQLResultFull) { + setFailureDueToQuota(); + return false; + } + + if (result != SQLResultOk) { + LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data()); + m_error = SQLError::create(SQLError::DATABASE_ERR, database->lastErrorMsg()); + return false; + } + } + + RefPtr<SQLResultSet> resultSet = SQLResultSet::create(); + + // Step so we can fetch the column names. + result = statement.step(); + if (result == SQLResultRow) { + int columnCount = statement.columnCount(); + SQLResultSetRowList* rows = resultSet->rows(); + + for (int i = 0; i < columnCount; i++) + rows->addColumn(statement.getColumnName(i)); + + do { + for (int i = 0; i < columnCount; i++) + rows->addResult(statement.getColumnValue(i)); + + result = statement.step(); + } while (result == SQLResultRow); + + if (result != SQLResultDone) { + m_error = SQLError::create(SQLError::DATABASE_ERR, database->lastErrorMsg()); + return false; + } + } else if (result == SQLResultDone) { + // Didn't find anything, or was an insert + if (db->lastActionWasInsert()) + resultSet->setInsertId(database->lastInsertRowID()); + } else if (result == SQLResultFull) { + // Return the Quota error - the delegate will be asked for more space and this statement might be re-run + setFailureDueToQuota(); + return false; + } else { + m_error = SQLError::create(SQLError::DATABASE_ERR, database->lastErrorMsg()); + return false; + } + + // FIXME: If the spec allows triggers, and we want to be "accurate" in a different way, we'd use + // sqlite3_total_changes() here instead of sqlite3_changed, because that includes rows modified from within a trigger + // For now, this seems sufficient + resultSet->setRowsAffected(database->lastChanges()); + + m_resultSet = resultSet; + return true; +} + +void SQLStatement::setDatabaseDeletedError() +{ + ASSERT(!m_error && !m_resultSet); + m_error = SQLError::create(SQLError::UNKNOWN_ERR, "unable to execute statement, because the user deleted the database"); +} + +void SQLStatement::setVersionMismatchedError() +{ + ASSERT(!m_error && !m_resultSet); + m_error = SQLError::create(SQLError::VERSION_ERR, "current version of the database and `oldVersion` argument do not match"); +} + +bool SQLStatement::performCallback(SQLTransaction* transaction) +{ + ASSERT(transaction); + + bool callbackError = false; + + // Call the appropriate statement callback and track if it resulted in an error, + // because then we need to jump to the transaction error callback. + if (m_error) { + ASSERT(m_statementErrorCallback); + callbackError = m_statementErrorCallback->handleEvent(transaction, m_error.get()); + } else if (m_statementCallback) + callbackError = !m_statementCallback->handleEvent(transaction, m_resultSet.get()); + + // Now release our callbacks, to break reference cycles. + m_statementCallback = 0; + m_statementErrorCallback = 0; + + return callbackError; +} + +void SQLStatement::setFailureDueToQuota() +{ + ASSERT(!m_error && !m_resultSet); + m_error = SQLError::create(SQLError::QUOTA_ERR, "there was not enough remaining storage space, or the storage quota was reached and the user declined to allow more space"); +} + +void SQLStatement::clearFailureDueToQuota() +{ + if (lastExecutionFailedDueToQuota()) + m_error = 0; +} + +bool SQLStatement::lastExecutionFailedDueToQuota() const +{ + return m_error && m_error->code() == SQLError::QUOTA_ERR; +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/SQLStatement.h b/Source/WebCore/storage/SQLStatement.h new file mode 100644 index 0000000..afd605a --- /dev/null +++ b/Source/WebCore/storage/SQLStatement.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef SQLStatement_h +#define SQLStatement_h + +#if ENABLE(DATABASE) + +#include "PlatformString.h" +#include "SQLResultSet.h" +#include "SQLValue.h" +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Database; +class SQLError; +class SQLStatementCallback; +class SQLStatementErrorCallback; +class SQLTransaction; + +class SQLStatement : public ThreadSafeShared<SQLStatement> { +public: + static PassRefPtr<SQLStatement> create(const String&, const Vector<SQLValue>&, PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>, int permissions); + + bool execute(Database*); + bool lastExecutionFailedDueToQuota() const; + + bool hasStatementCallback() const { return m_statementCallback; } + bool hasStatementErrorCallback() const { return m_statementErrorCallback; } + + void setDatabaseDeletedError(); + void setVersionMismatchedError(); + + bool performCallback(SQLTransaction*); + + SQLError* sqlError() const { return m_error.get(); } +private: + SQLStatement(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>, int permissions); + + void setFailureDueToQuota(); + void clearFailureDueToQuota(); + + String m_statement; + Vector<SQLValue> m_arguments; + RefPtr<SQLStatementCallback> m_statementCallback; + RefPtr<SQLStatementErrorCallback> m_statementErrorCallback; + + RefPtr<SQLError> m_error; + RefPtr<SQLResultSet> m_resultSet; + + int m_permissions; +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) + +#endif // SQLStatement_h diff --git a/Source/WebCore/storage/SQLStatementCallback.h b/Source/WebCore/storage/SQLStatementCallback.h new file mode 100644 index 0000000..f86e29f --- /dev/null +++ b/Source/WebCore/storage/SQLStatementCallback.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef SQLStatementCallback_h +#define SQLStatementCallback_h + +#if ENABLE(DATABASE) + +#include <wtf/ThreadSafeShared.h> + +namespace WebCore { + +class SQLTransaction; +class SQLResultSet; + +class SQLStatementCallback : public ThreadSafeShared<SQLStatementCallback> { +public: + virtual ~SQLStatementCallback() { } + virtual bool handleEvent(SQLTransaction*, SQLResultSet*) = 0; +}; + +} + +#endif + +#endif // SQLStatementErrorCallback_h diff --git a/Source/WebCore/storage/SQLStatementCallback.idl b/Source/WebCore/storage/SQLStatementCallback.idl new file mode 100644 index 0000000..cc25711 --- /dev/null +++ b/Source/WebCore/storage/SQLStatementCallback.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + interface [ + Conditional=DATABASE, + Callback + ] SQLStatementCallback { + boolean handleEvent(in SQLTransaction transaction, in SQLResultSet resultSet); + }; +} diff --git a/Source/WebCore/storage/SQLStatementErrorCallback.h b/Source/WebCore/storage/SQLStatementErrorCallback.h new file mode 100644 index 0000000..acb8f64 --- /dev/null +++ b/Source/WebCore/storage/SQLStatementErrorCallback.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef SQLStatementErrorCallback_h +#define SQLStatementErrorCallback_h + +#if ENABLE(DATABASE) + +#include <wtf/ThreadSafeShared.h> + +namespace WebCore { + +class SQLTransaction; +class SQLError; + +class SQLStatementErrorCallback : public ThreadSafeShared<SQLStatementErrorCallback> { +public: + virtual ~SQLStatementErrorCallback() { } + virtual bool handleEvent(SQLTransaction*, SQLError*) = 0; +}; + +} + +#endif + +#endif // SQLStatementErrorCallback_h diff --git a/Source/WebCore/storage/SQLStatementErrorCallback.idl b/Source/WebCore/storage/SQLStatementErrorCallback.idl new file mode 100644 index 0000000..315500d --- /dev/null +++ b/Source/WebCore/storage/SQLStatementErrorCallback.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + interface [ + Conditional=DATABASE, + Callback + ] SQLStatementErrorCallback { + [Custom] boolean handleEvent(in SQLTransaction transaction, in SQLError error); + }; +} diff --git a/Source/WebCore/storage/SQLStatementSync.cpp b/Source/WebCore/storage/SQLStatementSync.cpp new file mode 100644 index 0000000..673d50d --- /dev/null +++ b/Source/WebCore/storage/SQLStatementSync.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "SQLStatementSync.h" + +#if ENABLE(DATABASE) + +#include "DatabaseSync.h" +#include "SQLException.h" +#include "SQLResultSet.h" +#include "SQLValue.h" +#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +SQLStatementSync::SQLStatementSync(const String& statement, const Vector<SQLValue>& arguments, int permissions) + : m_statement(statement) + , m_arguments(arguments) + , m_permissions(permissions) +{ + ASSERT(!m_statement.isEmpty()); +} + +PassRefPtr<SQLResultSet> SQLStatementSync::execute(DatabaseSync* db, ExceptionCode& ec) +{ + db->setAuthorizerPermissions(m_permissions); + + SQLiteDatabase* database = &db->sqliteDatabase(); + + SQLiteStatement statement(*database, m_statement); + int result = statement.prepare(); + if (result != SQLResultOk) { + ec = (result == SQLResultInterrupt ? SQLException::DATABASE_ERR : SQLException::SYNTAX_ERR); + return 0; + } + + if (statement.bindParameterCount() != m_arguments.size()) { + ec = (db->isInterrupted()? SQLException::DATABASE_ERR : SQLException::SYNTAX_ERR); + return 0; + } + + for (unsigned i = 0; i < m_arguments.size(); ++i) { + result = statement.bindValue(i + 1, m_arguments[i]); + if (result == SQLResultFull) { + ec = SQLException::QUOTA_ERR; + return 0; + } + + if (result != SQLResultOk) { + ec = SQLException::DATABASE_ERR; + return 0; + } + } + + RefPtr<SQLResultSet> resultSet = SQLResultSet::create(); + + // Step so we can fetch the column names. + result = statement.step(); + if (result == SQLResultRow) { + int columnCount = statement.columnCount(); + SQLResultSetRowList* rows = resultSet->rows(); + + for (int i = 0; i < columnCount; i++) + rows->addColumn(statement.getColumnName(i)); + + do { + for (int i = 0; i < columnCount; i++) + rows->addResult(statement.getColumnValue(i)); + + result = statement.step(); + } while (result == SQLResultRow); + + if (result != SQLResultDone) { + ec = SQLException::DATABASE_ERR; + return 0; + } + } else if (result == SQLResultDone) { + // Didn't find anything, or was an insert. + if (db->lastActionWasInsert()) + resultSet->setInsertId(database->lastInsertRowID()); + } else if (result == SQLResultFull) { + // Quota error, the delegate will be asked for more space and this statement might be re-run. + ec = SQLException::QUOTA_ERR; + return 0; + } else { + ec = SQLException::DATABASE_ERR; + return 0; + } + + resultSet->setRowsAffected(database->lastChanges()); + return resultSet.release(); +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/SQLStatementSync.h b/Source/WebCore/storage/SQLStatementSync.h new file mode 100644 index 0000000..d807e55 --- /dev/null +++ b/Source/WebCore/storage/SQLStatementSync.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef SQLStatementSync_h +#define SQLStatementSync_h + +#if ENABLE(DATABASE) + +#include "ExceptionCode.h" +#include "PlatformString.h" +#include "SQLValue.h" +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class DatabaseSync; +class SQLResultSet; + +class SQLStatementSync { +public: + SQLStatementSync(const String& statement, const Vector<SQLValue>& arguments, int permissions); + + PassRefPtr<SQLResultSet> execute(DatabaseSync*, ExceptionCode&); + +private: + String m_statement; + Vector<SQLValue> m_arguments; + int m_permissions; +}; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) + +#endif // SQLStatementSync_h diff --git a/Source/WebCore/storage/SQLTransaction.cpp b/Source/WebCore/storage/SQLTransaction.cpp new file mode 100644 index 0000000..dea9d97 --- /dev/null +++ b/Source/WebCore/storage/SQLTransaction.cpp @@ -0,0 +1,596 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "SQLTransaction.h" + +#if ENABLE(DATABASE) + +#include "Database.h" +#include "DatabaseAuthorizer.h" +#include "DatabaseThread.h" +#include "Logging.h" +#include "PlatformString.h" +#include "ScriptExecutionContext.h" +#include "SQLError.h" +#include "SQLiteTransaction.h" +#include "SQLStatement.h" +#include "SQLStatementCallback.h" +#include "SQLStatementErrorCallback.h" +#include "SQLTransactionCallback.h" +#include "SQLTransactionClient.h" +#include "SQLTransactionCoordinator.h" +#include "SQLTransactionErrorCallback.h" +#include "SQLValue.h" +#include "VoidCallback.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +// There's no way of knowing exactly how much more space will be required when a statement hits the quota limit. +// For now, we'll arbitrarily choose currentQuota + 1mb. +// In the future we decide to track if a size increase wasn't enough, and ask for larger-and-larger increases until its enough. +static const int DefaultQuotaSizeIncrease = 1048576; + +namespace WebCore { + +PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, + PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) +{ + return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper, readOnly)); +} + +SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, + PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) + : m_nextStep(&SQLTransaction::acquireLock) + , m_executeSqlAllowed(false) + , m_database(db) + , m_wrapper(wrapper) + , m_callback(callback) + , m_successCallback(successCallback) + , m_errorCallback(errorCallback) + , m_shouldRetryCurrentStatement(false) + , m_modifiedDatabase(false) + , m_lockAcquired(false) + , m_readOnly(readOnly) +{ + ASSERT(m_database); +} + +SQLTransaction::~SQLTransaction() +{ + ASSERT(!m_sqliteTransaction); +} + +void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e) +{ + if (!m_executeSqlAllowed || !m_database->opened()) { + e = INVALID_STATE_ERR; + return; + } + + int permissions = DatabaseAuthorizer::ReadWriteMask; + if (!m_database->scriptExecutionContext()->allowDatabaseAccess()) + permissions |= DatabaseAuthorizer::NoAccessMask; + else if (m_readOnly) + permissions |= DatabaseAuthorizer::ReadOnlyMask; + + RefPtr<SQLStatement> statement = SQLStatement::create(sqlStatement, arguments, callback, callbackError, permissions); + + if (m_database->deleted()) + statement->setDatabaseDeletedError(); + + if (!m_database->versionMatchesExpected()) + statement->setVersionMismatchedError(); + + enqueueStatement(statement); +} + +void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement) +{ + MutexLocker locker(m_statementMutex); + m_statementQueue.append(statement); +} + +#ifndef NDEBUG +const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step) +{ + if (step == &SQLTransaction::acquireLock) + return "acquireLock"; + else if (step == &SQLTransaction::openTransactionAndPreflight) + return "openTransactionAndPreflight"; + else if (step == &SQLTransaction::runStatements) + return "runStatements"; + else if (step == &SQLTransaction::postflightAndCommit) + return "postflightAndCommit"; + else if (step == &SQLTransaction::cleanupAfterTransactionErrorCallback) + return "cleanupAfterTransactionErrorCallback"; + else if (step == &SQLTransaction::deliverTransactionCallback) + return "deliverTransactionCallback"; + else if (step == &SQLTransaction::deliverTransactionErrorCallback) + return "deliverTransactionErrorCallback"; + else if (step == &SQLTransaction::deliverStatementCallback) + return "deliverStatementCallback"; + else if (step == &SQLTransaction::deliverQuotaIncreaseCallback) + return "deliverQuotaIncreaseCallback"; + else if (step == &SQLTransaction::deliverSuccessCallback) + return "deliverSuccessCallback"; + else if (step == &SQLTransaction::cleanupAfterSuccessCallback) + return "cleanupAfterSuccessCallback"; + else + return "UNKNOWN"; +} +#endif + +void SQLTransaction::checkAndHandleClosedOrInterruptedDatabase() +{ + if (m_database->opened() && !m_database->isInterrupted()) + return; + + // If the database was stopped, don't do anything and cancel queued work + LOG(StorageAPI, "Database was stopped or interrupted - cancelling work for this transaction"); + MutexLocker locker(m_statementMutex); + m_statementQueue.clear(); + m_nextStep = 0; + + // Release the unneeded callbacks, to break reference cycles. + m_callback = 0; + m_successCallback = 0; + m_errorCallback = 0; + + // The next steps should be executed only if we're on the DB thread. + if (currentThread() != database()->scriptExecutionContext()->databaseThread()->getThreadID()) + return; + + // The current SQLite transaction should be stopped, as well + if (m_sqliteTransaction) { + m_sqliteTransaction->stop(); + m_sqliteTransaction.clear(); + } + + if (m_lockAcquired) + m_database->transactionCoordinator()->releaseLock(this); +} + + +bool SQLTransaction::performNextStep() +{ + LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep)); + + ASSERT(m_nextStep == &SQLTransaction::acquireLock || + m_nextStep == &SQLTransaction::openTransactionAndPreflight || + m_nextStep == &SQLTransaction::runStatements || + m_nextStep == &SQLTransaction::postflightAndCommit || + m_nextStep == &SQLTransaction::cleanupAfterSuccessCallback || + m_nextStep == &SQLTransaction::cleanupAfterTransactionErrorCallback); + + checkAndHandleClosedOrInterruptedDatabase(); + + if (m_nextStep) + (this->*m_nextStep)(); + + // If there is no nextStep after performing the above step, the transaction is complete + return !m_nextStep; +} + +void SQLTransaction::performPendingCallback() +{ + LOG(StorageAPI, "Callback %s\n", debugStepName(m_nextStep)); + + ASSERT(m_nextStep == &SQLTransaction::deliverTransactionCallback || + m_nextStep == &SQLTransaction::deliverTransactionErrorCallback || + m_nextStep == &SQLTransaction::deliverStatementCallback || + m_nextStep == &SQLTransaction::deliverQuotaIncreaseCallback || + m_nextStep == &SQLTransaction::deliverSuccessCallback); + + checkAndHandleClosedOrInterruptedDatabase(); + + if (m_nextStep) + (this->*m_nextStep)(); +} + +void SQLTransaction::notifyDatabaseThreadIsShuttingDown() +{ + ASSERT(currentThread() == database()->scriptExecutionContext()->databaseThread()->getThreadID()); + + // If the transaction is in progress, we should roll it back here, since this is our last + // oportunity to do something related to this transaction on the DB thread. + // Clearing m_sqliteTransaction invokes SQLiteTransaction's destructor which does just that. + m_sqliteTransaction.clear(); +} + +void SQLTransaction::acquireLock() +{ + m_database->transactionCoordinator()->acquireLock(this); +} + +void SQLTransaction::lockAcquired() +{ + m_lockAcquired = true; + m_nextStep = &SQLTransaction::openTransactionAndPreflight; + LOG(StorageAPI, "Scheduling openTransactionAndPreflight immediately for transaction %p\n", this); + m_database->scheduleTransactionStep(this, true); +} + +void SQLTransaction::openTransactionAndPreflight() +{ + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + ASSERT(m_lockAcquired); + + LOG(StorageAPI, "Opening and preflighting transaction %p", this); + + // If the database was deleted, jump to the error callback + if (m_database->deleted()) { + m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unable to open a transaction, because the user deleted the database"); + handleTransactionError(false); + return; + } + + // Set the maximum usage for this transaction if this transactions is not read-only + if (!m_readOnly) + m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); + + ASSERT(!m_sqliteTransaction); + m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); + + m_database->resetDeletes(); + m_database->disableAuthorizer(); + m_sqliteTransaction->begin(); + m_database->enableAuthorizer(); + + // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails + if (!m_sqliteTransaction->inProgress()) { + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + m_sqliteTransaction.clear(); + m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "unable to open a transaction to the database"); + handleTransactionError(false); + return; + } + + // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail + if (m_wrapper && !m_wrapper->performPreflight(this)) { + m_sqliteTransaction.clear(); + m_transactionError = m_wrapper->sqlError(); + if (!m_transactionError) + m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occured setting up transaction"); + + handleTransactionError(false); + return; + } + + // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object + m_nextStep = &SQLTransaction::deliverTransactionCallback; + LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this); + m_database->scheduleTransactionCallback(this); +} + +void SQLTransaction::deliverTransactionCallback() +{ + bool shouldDeliverErrorCallback = false; + + if (m_callback) { + m_executeSqlAllowed = true; + shouldDeliverErrorCallback = !m_callback->handleEvent(this); + m_executeSqlAllowed = false; + m_callback = 0; + } + + // Transaction Step 5 - If the transaction callback was null or raised an exception, jump to the error callback + if (shouldDeliverErrorCallback) { + m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception"); + deliverTransactionErrorCallback(); + } else + scheduleToRunStatements(); +} + +void SQLTransaction::scheduleToRunStatements() +{ + m_nextStep = &SQLTransaction::runStatements; + LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); + m_database->scheduleTransactionStep(this); +} + +void SQLTransaction::runStatements() +{ + ASSERT(m_lockAcquired); + + // If there is a series of statements queued up that are all successful and have no associated + // SQLStatementCallback objects, then we can burn through the queue + do { + if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) { + m_shouldRetryCurrentStatement = false; + // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. + // See ::openTransactionAndPreflight() for discussion + + // Reset the maximum size here, as it was increased to allow us to retry this statement. + // m_shouldRetryCurrentStatement is set to true only when a statement exceeds + // the quota, which can happen only in a read-write transaction. Therefore, there + // is no need to check here if the transaction is read-write. + m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); + } else { + // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, + // that means it ended in an error. Handle it now + if (m_currentStatement && m_currentStatement->lastExecutionFailedDueToQuota()) { + handleCurrentStatementError(); + break; + } + + // Otherwise, advance to the next statement + getNextStatement(); + } + } while (runCurrentStatement()); + + // If runCurrentStatement() returned false, that means either there was no current statement to run, + // or the current statement requires a callback to complete. In the later case, it also scheduled + // the callback or performed any other additional work so we can return + if (!m_currentStatement) + postflightAndCommit(); +} + +void SQLTransaction::getNextStatement() +{ + m_currentStatement = 0; + + MutexLocker locker(m_statementMutex); + if (!m_statementQueue.isEmpty()) { + m_currentStatement = m_statementQueue.takeFirst(); + } +} + +bool SQLTransaction::runCurrentStatement() +{ + if (!m_currentStatement) + return false; + + m_database->resetAuthorizer(); + + if (m_currentStatement->execute(m_database.get())) { + if (m_database->lastActionChangedDatabase()) { + // Flag this transaction as having changed the database for later delegate notification + m_modifiedDatabase = true; + // Also dirty the size of this database file for calculating quota usage + m_database->transactionClient()->didExecuteStatement(database()); + } + + if (m_currentStatement->hasStatementCallback()) { + m_nextStep = &SQLTransaction::deliverStatementCallback; + LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); + m_database->scheduleTransactionCallback(this); + return false; + } + return true; + } + + if (m_currentStatement->lastExecutionFailedDueToQuota()) { + m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback; + LOG(StorageAPI, "Scheduling deliverQuotaIncreaseCallback for transaction %p\n", this); + m_database->scheduleTransactionCallback(this); + return false; + } + + handleCurrentStatementError(); + + return false; +} + +void SQLTransaction::handleCurrentStatementError() +{ + // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback, + // or the transaction was rolled back, jump to the transaction error callback + if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { + m_nextStep = &SQLTransaction::deliverStatementCallback; + LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); + m_database->scheduleTransactionCallback(this); + } else { + m_transactionError = m_currentStatement->sqlError(); + if (!m_transactionError) + m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "the statement failed to execute"); + handleTransactionError(false); + } +} + +void SQLTransaction::deliverStatementCallback() +{ + ASSERT(m_currentStatement); + + // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback + // Otherwise, continue to loop through the statement queue + m_executeSqlAllowed = true; + bool result = m_currentStatement->performCallback(this); + m_executeSqlAllowed = false; + + if (result) { + m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false"); + handleTransactionError(true); + } else + scheduleToRunStatements(); +} + +void SQLTransaction::deliverQuotaIncreaseCallback() +{ + ASSERT(m_currentStatement); + ASSERT(!m_shouldRetryCurrentStatement); + + m_shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(database()); + + m_nextStep = &SQLTransaction::runStatements; + LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); + m_database->scheduleTransactionStep(this); +} + +void SQLTransaction::postflightAndCommit() +{ + ASSERT(m_lockAcquired); + + // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail + if (m_wrapper && !m_wrapper->performPostflight(this)) { + m_transactionError = m_wrapper->sqlError(); + if (!m_transactionError) + m_transactionError = SQLError::create(SQLError::UNKNOWN_ERR, "unknown error occured setting up transaction"); + handleTransactionError(false); + return; + } + + // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails + ASSERT(m_sqliteTransaction); + + m_database->disableAuthorizer(); + m_sqliteTransaction->commit(); + m_database->enableAuthorizer(); + + // If the commit failed, the transaction will still be marked as "in progress" + if (m_sqliteTransaction->inProgress()) { + m_successCallback = 0; + m_transactionError = SQLError::create(SQLError::DATABASE_ERR, "failed to commit the transaction"); + handleTransactionError(false); + return; + } + + // Vacuum the database if anything was deleted. + if (m_database->hadDeletes()) + m_database->incrementalVacuumIfNeeded(); + + // The commit was successful. If the transaction modified this database, notify the delegates. + if (m_modifiedDatabase) + m_database->transactionClient()->didCommitWriteTransaction(database()); + + // Now release our unneeded callbacks, to break reference cycles. + m_errorCallback = 0; + + // Transaction Step 10 - Deliver success callback, if there is one + if (m_successCallback) { + m_nextStep = &SQLTransaction::deliverSuccessCallback; + LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this); + m_database->scheduleTransactionCallback(this); + } else + cleanupAfterSuccessCallback(); +} + +void SQLTransaction::deliverSuccessCallback() +{ + // Transaction Step 10 - Deliver success callback + ASSERT(m_successCallback); + m_successCallback->handleEvent(); + + // Release the last callback to break reference cycle + m_successCallback = 0; + + // Schedule a "post-success callback" step to return control to the database thread in case there + // are further transactions queued up for this Database + m_nextStep = &SQLTransaction::cleanupAfterSuccessCallback; + LOG(StorageAPI, "Scheduling cleanupAfterSuccessCallback for transaction %p\n", this); + m_database->scheduleTransactionStep(this); +} + +void SQLTransaction::cleanupAfterSuccessCallback() +{ + ASSERT(m_lockAcquired); + + // Transaction Step 11 - End transaction steps + // There is no next step + LOG(StorageAPI, "Transaction %p is complete\n", this); + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + m_sqliteTransaction.clear(); + m_nextStep = 0; + + // Release the lock on this database + m_database->transactionCoordinator()->releaseLock(this); +} + +void SQLTransaction::handleTransactionError(bool inCallback) +{ + if (m_errorCallback) { + if (inCallback) + deliverTransactionErrorCallback(); + else { + m_nextStep = &SQLTransaction::deliverTransactionErrorCallback; + LOG(StorageAPI, "Scheduling deliverTransactionErrorCallback for transaction %p\n", this); + m_database->scheduleTransactionCallback(this); + } + return; + } + + // No error callback, so fast-forward to: + // Transaction Step 12 - Rollback the transaction. + if (inCallback) { + m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; + LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); + m_database->scheduleTransactionStep(this); + } else { + cleanupAfterTransactionErrorCallback(); + } +} + +void SQLTransaction::deliverTransactionErrorCallback() +{ + ASSERT(m_transactionError); + + // Transaction Step 12 - If exists, invoke error callback with the last + // error to have occurred in this transaction. + if (m_errorCallback) { + m_errorCallback->handleEvent(m_transactionError.get()); + m_errorCallback = 0; + } + + m_nextStep = &SQLTransaction::cleanupAfterTransactionErrorCallback; + LOG(StorageAPI, "Scheduling cleanupAfterTransactionErrorCallback for transaction %p\n", this); + m_database->scheduleTransactionStep(this); +} + +void SQLTransaction::cleanupAfterTransactionErrorCallback() +{ + ASSERT(m_lockAcquired); + + m_database->disableAuthorizer(); + if (m_sqliteTransaction) { + // Transaction Step 12 - Rollback the transaction. + m_sqliteTransaction->rollback(); + + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + m_sqliteTransaction.clear(); + } + m_database->enableAuthorizer(); + + // Transaction Step 12 - Any still-pending statements in the transaction are discarded. + { + MutexLocker locker(m_statementMutex); + m_statementQueue.clear(); + } + + // Transaction is complete! There is no next step + LOG(StorageAPI, "Transaction %p is complete with an error\n", this); + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + m_nextStep = 0; + + // Now release the lock on this database + m_database->transactionCoordinator()->releaseLock(this); +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/SQLTransaction.h b/Source/WebCore/storage/SQLTransaction.h new file mode 100644 index 0000000..3eb1fd5 --- /dev/null +++ b/Source/WebCore/storage/SQLTransaction.h @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ +#ifndef SQLTransaction_h +#define SQLTransaction_h + +#if ENABLE(DATABASE) + +#include "ExceptionCode.h" +#include "SQLStatement.h" +#include <wtf/Deque.h> +#include <wtf/Forward.h> +#include <wtf/ThreadSafeShared.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Database; +class SQLError; +class SQLiteTransaction; +class SQLStatementCallback; +class SQLStatementErrorCallback; +class SQLTransaction; +class SQLTransactionCallback; +class SQLTransactionErrorCallback; +class SQLValue; +class VoidCallback; + +class SQLTransactionWrapper : public ThreadSafeShared<SQLTransactionWrapper> { +public: + virtual ~SQLTransactionWrapper() { } + virtual bool performPreflight(SQLTransaction*) = 0; + virtual bool performPostflight(SQLTransaction*) = 0; + + virtual SQLError* sqlError() const = 0; +}; + +class SQLTransaction : public ThreadSafeShared<SQLTransaction> { +public: + static PassRefPtr<SQLTransaction> create(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, + PassRefPtr<VoidCallback>, PassRefPtr<SQLTransactionWrapper>, bool readOnly = false); + + ~SQLTransaction(); + + void executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, + PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>, ExceptionCode&); + + void lockAcquired(); + bool performNextStep(); + void performPendingCallback(); + + Database* database() { return m_database.get(); } + bool isReadOnly() { return m_readOnly; } + void notifyDatabaseThreadIsShuttingDown(); + +private: + SQLTransaction(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, + PassRefPtr<VoidCallback>, PassRefPtr<SQLTransactionWrapper>, bool readOnly); + + typedef void (SQLTransaction::*TransactionStepMethod)(); + TransactionStepMethod m_nextStep; + + void enqueueStatement(PassRefPtr<SQLStatement>); + + void checkAndHandleClosedOrInterruptedDatabase(); + + void acquireLock(); + void openTransactionAndPreflight(); + void deliverTransactionCallback(); + void scheduleToRunStatements(); + void runStatements(); + void getNextStatement(); + bool runCurrentStatement(); + void handleCurrentStatementError(); + void deliverStatementCallback(); + void deliverQuotaIncreaseCallback(); + void postflightAndCommit(); + void deliverSuccessCallback(); + void cleanupAfterSuccessCallback(); + void handleTransactionError(bool inCallback); + void deliverTransactionErrorCallback(); + void cleanupAfterTransactionErrorCallback(); + +#ifndef NDEBUG + static const char* debugStepName(TransactionStepMethod); +#endif + + RefPtr<SQLStatement> m_currentStatement; + + bool m_executeSqlAllowed; + + RefPtr<Database> m_database; + RefPtr<SQLTransactionWrapper> m_wrapper; + RefPtr<SQLTransactionCallback> m_callback; + RefPtr<VoidCallback> m_successCallback; + RefPtr<SQLTransactionErrorCallback> m_errorCallback; + RefPtr<SQLError> m_transactionError; + bool m_shouldRetryCurrentStatement; + bool m_modifiedDatabase; + bool m_lockAcquired; + bool m_readOnly; + + Mutex m_statementMutex; + Deque<RefPtr<SQLStatement> > m_statementQueue; + + OwnPtr<SQLiteTransaction> m_sqliteTransaction; +}; + +} // namespace WebCore + +#endif + +#endif // SQLTransaction_h diff --git a/Source/WebCore/storage/SQLTransaction.idl b/Source/WebCore/storage/SQLTransaction.idl new file mode 100644 index 0000000..4e353d3 --- /dev/null +++ b/Source/WebCore/storage/SQLTransaction.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + + interface [ + Conditional=DATABASE, + OmitConstructor, + NoStaticTables + ] SQLTransaction { + [Custom] void executeSql(in DOMString sqlStatement, in ObjectArray arguments, in SQLStatementCallback callback, in SQLStatementErrorCallback errorCallback); + }; +} diff --git a/Source/WebCore/storage/SQLTransactionCallback.h b/Source/WebCore/storage/SQLTransactionCallback.h new file mode 100644 index 0000000..aff6233 --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionCallback.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + *Transaction + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef SQLTransactionCallback_h +#define SQLTransactionCallback_h + +#if ENABLE(DATABASE) + +#include <wtf/ThreadSafeShared.h> + +namespace WebCore { + +class SQLTransaction; + +class SQLTransactionCallback : public ThreadSafeShared<SQLTransactionCallback> { +public: + virtual ~SQLTransactionCallback() { } + virtual bool handleEvent(SQLTransaction*) = 0; +}; + +} + +#endif + +#endif // SQLTransactionCallback_h diff --git a/Source/WebCore/storage/SQLTransactionCallback.idl b/Source/WebCore/storage/SQLTransactionCallback.idl new file mode 100644 index 0000000..d1bc77c --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionCallback.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + interface [ + Conditional=DATABASE, + Callback + ] SQLTransactionCallback { + boolean handleEvent(in SQLTransaction transaction); + }; +} diff --git a/Source/WebCore/storage/SQLTransactionClient.cpp b/Source/WebCore/storage/SQLTransactionClient.cpp new file mode 100644 index 0000000..6b95606 --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionClient.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 "SQLTransactionClient.h" + +#if ENABLE(DATABASE) + +#include "AbstractDatabase.h" +#include "DatabaseTracker.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" + +namespace WebCore { + +void SQLTransactionClient::didCommitWriteTransaction(AbstractDatabase* database) +{ + DatabaseTracker::tracker().scheduleNotifyDatabaseChanged( + database->securityOrigin(), database->stringIdentifier()); +} + +void SQLTransactionClient::didExecuteStatement(AbstractDatabase* database) +{ + DatabaseTracker::tracker().databaseChanged(database); +} + +bool SQLTransactionClient::didExceedQuota(AbstractDatabase* database) +{ + ASSERT(database->scriptExecutionContext()->isContextThread()); + unsigned long long currentQuota = DatabaseTracker::tracker().quotaForOrigin(database->securityOrigin()); + database->scriptExecutionContext()->databaseExceededQuota(database->stringIdentifier()); + unsigned long long newQuota = DatabaseTracker::tracker().quotaForOrigin(database->securityOrigin()); + return (newQuota > currentQuota); +} + +} + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/SQLTransactionClient.h b/Source/WebCore/storage/SQLTransactionClient.h new file mode 100644 index 0000000..fed0657 --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionClient.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef SQLTransactionClient_h +#define SQLTransactionClient_h + +#if ENABLE(DATABASE) + +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class AbstractDatabase; + +// A client to the SQLTransaction class. Allows SQLTransaction to notify interested +// parties that certain things have happened in a transaction. +class SQLTransactionClient : public Noncopyable { +public: + void didCommitWriteTransaction(AbstractDatabase*); + void didExecuteStatement(AbstractDatabase*); + bool didExceedQuota(AbstractDatabase*); +}; + +} + +#endif // ENABLE(DATABASE) + +#endif // SQLTransactionClient_h diff --git a/Source/WebCore/storage/SQLTransactionCoordinator.cpp b/Source/WebCore/storage/SQLTransactionCoordinator.cpp new file mode 100644 index 0000000..104ea10 --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionCoordinator.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 "SQLTransactionCoordinator.h" + +#if ENABLE(DATABASE) + +#include "Database.h" +#include "SQLTransaction.h" +#include <wtf/Deque.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +static String getDatabaseIdentifier(SQLTransaction* transaction) +{ + Database* database = transaction->database(); + ASSERT(database); + return database->stringIdentifier(); +} + +void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& info) +{ + if (info.activeWriteTransaction || info.pendingTransactions.isEmpty()) + return; + + RefPtr<SQLTransaction> firstPendingTransaction = info.pendingTransactions.first(); + if (firstPendingTransaction->isReadOnly()) { + do { + firstPendingTransaction = info.pendingTransactions.takeFirst(); + info.activeReadTransactions.add(firstPendingTransaction); + firstPendingTransaction->lockAcquired(); + } while (!info.pendingTransactions.isEmpty() && info.pendingTransactions.first()->isReadOnly()); + } else if (info.activeReadTransactions.isEmpty()) { + info.pendingTransactions.removeFirst(); + info.activeWriteTransaction = firstPendingTransaction; + firstPendingTransaction->lockAcquired(); + } +} + +void SQLTransactionCoordinator::acquireLock(SQLTransaction* transaction) +{ + String dbIdentifier = getDatabaseIdentifier(transaction); + + CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier); + if (coordinationInfoIterator == m_coordinationInfoMap.end()) { + // No pending transactions for this DB + coordinationInfoIterator = m_coordinationInfoMap.add(dbIdentifier, CoordinationInfo()).first; + } + + CoordinationInfo& info = coordinationInfoIterator->second; + info.pendingTransactions.append(transaction); + processPendingTransactions(info); +} + +void SQLTransactionCoordinator::releaseLock(SQLTransaction* transaction) +{ + if (m_coordinationInfoMap.isEmpty()) + return; + + String dbIdentifier = getDatabaseIdentifier(transaction); + + CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier); + ASSERT(coordinationInfoIterator != m_coordinationInfoMap.end()); + CoordinationInfo& info = coordinationInfoIterator->second; + + if (transaction->isReadOnly()) { + ASSERT(info.activeReadTransactions.contains(transaction)); + info.activeReadTransactions.remove(transaction); + } else { + ASSERT(info.activeWriteTransaction == transaction); + info.activeWriteTransaction = 0; + } + + processPendingTransactions(info); +} + +void SQLTransactionCoordinator::shutdown() +{ + // Notify all transactions in progress that the database thread is shutting down + for (CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin(); + coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) { + CoordinationInfo& info = coordinationInfoIterator->second; + if (info.activeWriteTransaction) + info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown(); + for (HashSet<RefPtr<SQLTransaction> >::iterator activeReadTransactionsIterator = + info.activeReadTransactions.begin(); + activeReadTransactionsIterator != info.activeReadTransactions.end(); + ++activeReadTransactionsIterator) { + (*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown(); + } + } + + // Clean up all pending transactions for all databases + m_coordinationInfoMap.clear(); +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/SQLTransactionCoordinator.h b/Source/WebCore/storage/SQLTransactionCoordinator.h new file mode 100644 index 0000000..94360c0 --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionCoordinator.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef SQLTransactionCoordinator_h +#define SQLTransactionCoordinator_h + +#if ENABLE(DATABASE) + +#include <wtf/Deque.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + + class SQLTransaction; + + class SQLTransactionCoordinator : public Noncopyable { + public: + void acquireLock(SQLTransaction*); + void releaseLock(SQLTransaction*); + void shutdown(); + private: + typedef Deque<RefPtr<SQLTransaction> > TransactionsQueue; + struct CoordinationInfo { + TransactionsQueue pendingTransactions; + HashSet<RefPtr<SQLTransaction> > activeReadTransactions; + RefPtr<SQLTransaction> activeWriteTransaction; + }; + // Maps database names to information about pending transactions + typedef HashMap<String, CoordinationInfo> CoordinationInfoMap; + CoordinationInfoMap m_coordinationInfoMap; + + void processPendingTransactions(CoordinationInfo& info); + }; +} + +#endif // ENABLE(DATABASE) + +#endif // SQLTransactionCoordinator_h diff --git a/Source/WebCore/storage/SQLTransactionErrorCallback.h b/Source/WebCore/storage/SQLTransactionErrorCallback.h new file mode 100644 index 0000000..4095d6a --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionErrorCallback.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef SQLTransactionErrorCallback_h +#define SQLTransactionErrorCallback_h + +#if ENABLE(DATABASE) + +#include <wtf/ThreadSafeShared.h> + +namespace WebCore { + +class SQLError; + +class SQLTransactionErrorCallback : public ThreadSafeShared<SQLTransactionErrorCallback> { +public: + virtual ~SQLTransactionErrorCallback() { } + virtual bool handleEvent(SQLError*) = 0; +}; + +} + +#endif + +#endif // SQLTransactionErrorCallback_h diff --git a/Source/WebCore/storage/SQLTransactionErrorCallback.idl b/Source/WebCore/storage/SQLTransactionErrorCallback.idl new file mode 100644 index 0000000..779ba69 --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionErrorCallback.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +module storage { + interface [ + Conditional=DATABASE, + Callback + ] SQLTransactionErrorCallback { + boolean handleEvent(in SQLError error); + }; +} diff --git a/Source/WebCore/storage/SQLTransactionSync.cpp b/Source/WebCore/storage/SQLTransactionSync.cpp new file mode 100644 index 0000000..97a5cc5 --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionSync.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 "SQLTransactionSync.h" + +#if ENABLE(DATABASE) + +#include "DatabaseAuthorizer.h" +#include "DatabaseSync.h" +#include "PlatformString.h" +#include "SQLException.h" +#include "SQLResultSet.h" +#include "SQLStatementSync.h" +#include "SQLTransactionClient.h" +#include "SQLTransactionSyncCallback.h" +#include "SQLValue.h" +#include "SQLiteTransaction.h" +#include "ScriptExecutionContext.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +PassRefPtr<SQLTransactionSync> SQLTransactionSync::create(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly) +{ + return adoptRef(new SQLTransactionSync(db, callback, readOnly)); +} + +SQLTransactionSync::SQLTransactionSync(DatabaseSync* db, PassRefPtr<SQLTransactionSyncCallback> callback, bool readOnly) + : m_database(db) + , m_callback(callback) + , m_readOnly(readOnly) + , m_modifiedDatabase(false) + , m_transactionClient(adoptPtr(new SQLTransactionClient())) +{ + ASSERT(m_database->scriptExecutionContext()->isContextThread()); +} + +SQLTransactionSync::~SQLTransactionSync() +{ + ASSERT(m_database->scriptExecutionContext()->isContextThread()); + if (m_sqliteTransaction && m_sqliteTransaction->inProgress()) + rollback(); +} + +PassRefPtr<SQLResultSet> SQLTransactionSync::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionCode& ec) +{ + ASSERT(m_database->scriptExecutionContext()->isContextThread()); + if (!m_database->opened()) { + ec = SQLException::UNKNOWN_ERR; + return 0; + } + + if (!m_database->versionMatchesExpected()) { + ec = SQLException::VERSION_ERR; + return 0; + } + + if (sqlStatement.isEmpty()) + return 0; + + int permissions = DatabaseAuthorizer::ReadWriteMask; + if (!m_database->scriptExecutionContext()->allowDatabaseAccess()) + permissions |= DatabaseAuthorizer::NoAccessMask; + else if (m_readOnly) + permissions |= DatabaseAuthorizer::ReadOnlyMask; + + SQLStatementSync statement(sqlStatement, arguments, permissions); + + m_database->resetAuthorizer(); + bool retryStatement = true; + RefPtr<SQLResultSet> resultSet; + while (retryStatement) { + retryStatement = false; + resultSet = statement.execute(m_database.get(), ec); + if (!resultSet) { + if (m_sqliteTransaction->wasRolledBackBySqlite()) + return 0; + + if (ec == SQLException::QUOTA_ERR) { + if (m_transactionClient->didExceedQuota(database())) { + ec = 0; + retryStatement = true; + } else + return 0; + } + } + } + + if (m_database->lastActionChangedDatabase()) { + m_modifiedDatabase = true; + m_transactionClient->didExecuteStatement(database()); + } + + return resultSet.release(); +} + +ExceptionCode SQLTransactionSync::begin() +{ + ASSERT(m_database->scriptExecutionContext()->isContextThread()); + if (!m_database->opened()) + return SQLException::UNKNOWN_ERR; + + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + + // Set the maximum usage for this transaction if this transactions is not read-only. + if (!m_readOnly) + m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize()); + + ASSERT(!m_sqliteTransaction); + m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly)); + + m_database->resetDeletes(); + m_database->disableAuthorizer(); + m_sqliteTransaction->begin(); + m_database->enableAuthorizer(); + + // Check if begin() succeeded. + if (!m_sqliteTransaction->inProgress()) { + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); + m_sqliteTransaction.clear(); + return SQLException::DATABASE_ERR; + } + + return 0; +} + +ExceptionCode SQLTransactionSync::execute() +{ + ASSERT(m_database->scriptExecutionContext()->isContextThread()); + if (!m_database->opened() || (m_callback && !m_callback->handleEvent(this))) { + m_callback = 0; + return SQLException::UNKNOWN_ERR; + } + + m_callback = 0; + return 0; +} + +ExceptionCode SQLTransactionSync::commit() +{ + ASSERT(m_database->scriptExecutionContext()->isContextThread()); + if (!m_database->opened()) + return SQLException::UNKNOWN_ERR; + + ASSERT(m_sqliteTransaction); + + m_database->disableAuthorizer(); + m_sqliteTransaction->commit(); + m_database->enableAuthorizer(); + + // If the commit failed, the transaction will still be marked as "in progress" + if (m_sqliteTransaction->inProgress()) + return SQLException::DATABASE_ERR; + + m_sqliteTransaction.clear(); + + // Vacuum the database if anything was deleted. + if (m_database->hadDeletes()) + m_database->incrementalVacuumIfNeeded(); + + // The commit was successful. If the transaction modified this database, notify the delegates. + if (m_modifiedDatabase) + m_transactionClient->didCommitWriteTransaction(database()); + + return 0; +} + +void SQLTransactionSync::rollback() +{ + ASSERT(m_database->scriptExecutionContext()->isContextThread()); + m_database->disableAuthorizer(); + if (m_sqliteTransaction) { + m_sqliteTransaction->rollback(); + m_sqliteTransaction.clear(); + } + m_database->enableAuthorizer(); + + ASSERT(!m_database->sqliteDatabase().transactionInProgress()); +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/SQLTransactionSync.h b/Source/WebCore/storage/SQLTransactionSync.h new file mode 100644 index 0000000..e66c876 --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionSync.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef SQLTransactionSync_h +#define SQLTransactionSync_h + +#if ENABLE(DATABASE) + +#include "ExceptionCode.h" +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class DatabaseSync; +class SQLResultSet; +class SQLTransactionClient; +class SQLTransactionSyncCallback; +class SQLValue; +class SQLiteTransaction; + +// Instances of this class should be created and used only on the worker's context thread. +class SQLTransactionSync : public RefCounted<SQLTransactionSync> { +public: + static PassRefPtr<SQLTransactionSync> create(DatabaseSync*, PassRefPtr<SQLTransactionSyncCallback>, bool readOnly = false); + + ~SQLTransactionSync(); + + PassRefPtr<SQLResultSet> executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, ExceptionCode&); + + DatabaseSync* database() { return m_database.get(); } + bool isReadOnly() const { return m_readOnly; } + + ExceptionCode begin(); + ExceptionCode execute(); + ExceptionCode commit(); + void rollback(); + +private: + SQLTransactionSync(DatabaseSync*, PassRefPtr<SQLTransactionSyncCallback>, bool readOnly); + + RefPtr<DatabaseSync> m_database; + RefPtr<SQLTransactionSyncCallback> m_callback; + bool m_readOnly; + + bool m_modifiedDatabase; + OwnPtr<SQLTransactionClient> m_transactionClient; + OwnPtr<SQLiteTransaction> m_sqliteTransaction; +}; + +} // namespace WebCore + +#endif + +#endif // SQLTransactionSync_h diff --git a/Source/WebCore/storage/SQLTransactionSync.idl b/Source/WebCore/storage/SQLTransactionSync.idl new file mode 100644 index 0000000..003a21d --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionSync.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +module storage { + + interface [ + Conditional=DATABASE, + OmitConstructor, + NoStaticTables + ] SQLTransactionSync { + [Custom] SQLResultSet executeSql(in DOMString sqlStatement, in ObjectArray arguments); + }; +} diff --git a/Source/WebCore/storage/SQLTransactionSyncCallback.h b/Source/WebCore/storage/SQLTransactionSyncCallback.h new file mode 100644 index 0000000..f22e62f --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionSyncCallback.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef SQLTransactionSyncCallback_h +#define SQLTransactionSyncCallback_h + +#if ENABLE(DATABASE) + +#include <wtf/RefCounted.h> + +namespace WebCore { + +class SQLTransactionSync; + +// Instances of this class should be created and used only on the worker's context thread. +class SQLTransactionSyncCallback : public RefCounted<SQLTransactionSyncCallback> { +public: + virtual ~SQLTransactionSyncCallback() { } + virtual bool handleEvent(SQLTransactionSync*) = 0; +}; + +} + +#endif + +#endif // SQLTransactionSyncCallback_h diff --git a/Source/WebCore/storage/SQLTransactionSyncCallback.idl b/Source/WebCore/storage/SQLTransactionSyncCallback.idl new file mode 100644 index 0000000..b0fffca --- /dev/null +++ b/Source/WebCore/storage/SQLTransactionSyncCallback.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +module storage { + interface [ + Conditional=DATABASE, + Callback + ] SQLTransactionSyncCallback { + boolean handleEvent(in SQLTransactionSync transaction); + }; +} diff --git a/Source/WebCore/storage/Storage.cpp b/Source/WebCore/storage/Storage.cpp new file mode 100644 index 0000000..0259ca4 --- /dev/null +++ b/Source/WebCore/storage/Storage.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "Storage.h" + +#if ENABLE(DOM_STORAGE) + +#include "Frame.h" +#include "Page.h" +#include "Settings.h" +#include "StorageArea.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +PassRefPtr<Storage> Storage::create(Frame* frame, PassRefPtr<StorageArea> storageArea) +{ + return adoptRef(new Storage(frame, storageArea)); +} + +Storage::Storage(Frame* frame, PassRefPtr<StorageArea> storageArea) + : m_frame(frame) + , m_storageArea(storageArea) +{ + ASSERT(m_frame); + ASSERT(m_storageArea); +} + +Storage::~Storage() +{ +} + +unsigned Storage::length() const +{ + if (!m_frame || !m_frame->page() || m_frame->page()->settings()->privateBrowsingEnabled()) + return 0; + + return m_storageArea->length(); +} + +String Storage::key(unsigned index) const +{ + if (!m_frame || !m_frame->page() || m_frame->page()->settings()->privateBrowsingEnabled()) + return String(); + + return m_storageArea->key(index); +} + +String Storage::getItem(const String& key) const +{ + if (!m_frame || !m_frame->page() || m_frame->page()->settings()->privateBrowsingEnabled()) + return String(); + + return m_storageArea->getItem(key); +} + +void Storage::setItem(const String& key, const String& value, ExceptionCode& ec) +{ + ec = 0; + if (!m_frame) + return; + + m_storageArea->setItem(key, value, ec, m_frame); +} + +void Storage::removeItem(const String& key) +{ + if (!m_frame) + return; + + m_storageArea->removeItem(key, m_frame); +} + +void Storage::clear() +{ + if (!m_frame) + return; + + m_storageArea->clear(m_frame); +} + +bool Storage::contains(const String& key) const +{ + if (!m_frame || !m_frame->page() || m_frame->page()->settings()->privateBrowsingEnabled()) + return false; + + return m_storageArea->contains(key); +} + +} + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/Storage.h b/Source/WebCore/storage/Storage.h new file mode 100644 index 0000000..c81481a --- /dev/null +++ b/Source/WebCore/storage/Storage.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef Storage_h +#define Storage_h + +#if ENABLE(DOM_STORAGE) + +#include <wtf/Forward.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class Frame; + class StorageArea; + typedef int ExceptionCode; + + class Storage : public RefCounted<Storage> { + public: + static PassRefPtr<Storage> create(Frame*, PassRefPtr<StorageArea>); + ~Storage(); + + unsigned length() const; + String key(unsigned index) const; + String getItem(const String&) const; + void setItem(const String& key, const String& value, ExceptionCode&); + void removeItem(const String&); + void clear(); + + bool contains(const String& key) const; + + Frame* frame() { return m_frame; } + void disconnectFrame() { m_frame = 0; } + + private: + Storage(Frame*, PassRefPtr<StorageArea>); + + Frame* m_frame; + RefPtr<StorageArea> m_storageArea; + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // Storage_h diff --git a/Source/WebCore/storage/Storage.idl b/Source/WebCore/storage/Storage.idl new file mode 100644 index 0000000..ffd1af1 --- /dev/null +++ b/Source/WebCore/storage/Storage.idl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +module storage { + + interface [ + HasNameGetter, + CustomDeleteProperty, + CustomGetPropertyNames, + DelegatingPutFunction, + Conditional=DOM_STORAGE + ] Storage { + readonly attribute [DontEnum] unsigned long length; + [DontEnum, ConvertNullStringTo=Null] DOMString key(in unsigned long index); + [DontEnum, ConvertNullStringTo=Null] DOMString getItem(in DOMString key); + [DontEnum] void setItem(in DOMString key, in DOMString data) + raises(DOMException); + [DontEnum] void removeItem(in DOMString key); + [DontEnum] void clear(); + }; + +} diff --git a/Source/WebCore/storage/StorageArea.h b/Source/WebCore/storage/StorageArea.h new file mode 100644 index 0000000..6081240 --- /dev/null +++ b/Source/WebCore/storage/StorageArea.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StorageArea_h +#define StorageArea_h + +#if ENABLE(DOM_STORAGE) + +#include "PlatformString.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class Frame; + class SecurityOrigin; + class StorageSyncManager; + typedef int ExceptionCode; + enum StorageType { LocalStorage, SessionStorage }; + + // This interface is required for Chromium since these actions need to be proxied between processes. + class StorageArea : public RefCounted<StorageArea> { + public: + virtual ~StorageArea() { } + + // The HTML5 DOM Storage API + virtual unsigned length() const = 0; + virtual String key(unsigned index) const = 0; + virtual String getItem(const String& key) const = 0; + virtual String setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame) = 0; + virtual String removeItem(const String& key, Frame* sourceFrame) = 0; + virtual bool clear(Frame* sourceFrame) = 0; + virtual bool contains(const String& key) const = 0; + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageArea_h diff --git a/Source/WebCore/storage/StorageAreaImpl.cpp b/Source/WebCore/storage/StorageAreaImpl.cpp new file mode 100644 index 0000000..54df135 --- /dev/null +++ b/Source/WebCore/storage/StorageAreaImpl.cpp @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "StorageAreaImpl.h" + +#if ENABLE(DOM_STORAGE) + +#include "ExceptionCode.h" +#include "Frame.h" +#include "Page.h" +#include "Settings.h" +#include "StorageAreaSync.h" +#include "StorageEventDispatcher.h" +#include "StorageMap.h" +#include "StorageSyncManager.h" + +namespace WebCore { + +StorageAreaImpl::~StorageAreaImpl() +{ + ASSERT(isMainThread()); +} + +inline StorageAreaImpl::StorageAreaImpl(StorageType storageType, PassRefPtr<SecurityOrigin> origin, PassRefPtr<StorageSyncManager> syncManager, unsigned quota) + : m_storageType(storageType) + , m_securityOrigin(origin) + , m_storageMap(StorageMap::create(quota)) + , m_storageSyncManager(syncManager) +#ifndef NDEBUG + , m_isShutdown(false) +#endif +{ + ASSERT(isMainThread()); + ASSERT(m_securityOrigin); + ASSERT(m_storageMap); +} + +PassRefPtr<StorageAreaImpl> StorageAreaImpl::create(StorageType storageType, PassRefPtr<SecurityOrigin> origin, PassRefPtr<StorageSyncManager> syncManager, unsigned quota) +{ + RefPtr<StorageAreaImpl> area = adoptRef(new StorageAreaImpl(storageType, origin, syncManager, quota)); + + // FIXME: If there's no backing storage for LocalStorage, the default WebKit behavior should be that of private browsing, + // not silently ignoring it. https://bugs.webkit.org/show_bug.cgi?id=25894 + if (area->m_storageSyncManager) { + area->m_storageAreaSync = StorageAreaSync::create(area->m_storageSyncManager, area.get(), area->m_securityOrigin->databaseIdentifier()); + ASSERT(area->m_storageAreaSync); + } + + return area.release(); +} + +PassRefPtr<StorageAreaImpl> StorageAreaImpl::copy() +{ + ASSERT(!m_isShutdown); + return adoptRef(new StorageAreaImpl(this)); +} + +StorageAreaImpl::StorageAreaImpl(StorageAreaImpl* area) + : m_storageType(area->m_storageType) + , m_securityOrigin(area->m_securityOrigin) + , m_storageMap(area->m_storageMap) + , m_storageSyncManager(area->m_storageSyncManager) +#ifndef NDEBUG + , m_isShutdown(area->m_isShutdown) +#endif +{ + ASSERT(isMainThread()); + ASSERT(m_securityOrigin); + ASSERT(m_storageMap); + ASSERT(!m_isShutdown); +} + +static bool privateBrowsingEnabled(Frame* frame) +{ +#if PLATFORM(ANDROID) + if (!frame) + return false; +#endif +#if PLATFORM(CHROMIUM) + // The frame pointer can be NULL in Chromium since this call is made in a different + // process from where the Frame object exists. Luckily, private browseing is + // implemented differently in Chromium, so it'd never return true anyway. + ASSERT(!frame); + return false; +#else + return frame->page()->settings()->privateBrowsingEnabled(); +#endif +} + +unsigned StorageAreaImpl::length() const +{ + ASSERT(!m_isShutdown); + blockUntilImportComplete(); + + return m_storageMap->length(); +} + +String StorageAreaImpl::key(unsigned index) const +{ + ASSERT(!m_isShutdown); + blockUntilImportComplete(); + + return m_storageMap->key(index); +} + +String StorageAreaImpl::getItem(const String& key) const +{ + ASSERT(!m_isShutdown); + blockUntilImportComplete(); + + return m_storageMap->getItem(key); +} + +String StorageAreaImpl::setItem(const String& key, const String& value, ExceptionCode& ec, Frame* frame) +{ + ASSERT(!m_isShutdown); + ASSERT(!value.isNull()); + blockUntilImportComplete(); + + if (privateBrowsingEnabled(frame)) { + ec = QUOTA_EXCEEDED_ERR; + return String(); + } + + String oldValue; + bool quotaException; + RefPtr<StorageMap> newMap = m_storageMap->setItem(key, value, oldValue, quotaException); + if (newMap) + m_storageMap = newMap.release(); + + if (quotaException) { + ec = QUOTA_EXCEEDED_ERR; + return oldValue; + } + + if (oldValue == value) + return oldValue; + + if (m_storageAreaSync) + m_storageAreaSync->scheduleItemForSync(key, value); + StorageEventDispatcher::dispatch(key, oldValue, value, m_storageType, m_securityOrigin.get(), frame); + return oldValue; +} + +String StorageAreaImpl::removeItem(const String& key, Frame* frame) +{ + ASSERT(!m_isShutdown); + blockUntilImportComplete(); + + if (privateBrowsingEnabled(frame)) + return String(); + + String oldValue; + RefPtr<StorageMap> newMap = m_storageMap->removeItem(key, oldValue); + if (newMap) + m_storageMap = newMap.release(); + + if (oldValue.isNull()) + return oldValue; + + if (m_storageAreaSync) + m_storageAreaSync->scheduleItemForSync(key, String()); + StorageEventDispatcher::dispatch(key, oldValue, String(), m_storageType, m_securityOrigin.get(), frame); + return oldValue; +} + +bool StorageAreaImpl::clear(Frame* frame) +{ + ASSERT(!m_isShutdown); + blockUntilImportComplete(); + + if (privateBrowsingEnabled(frame)) + return false; + + if (!m_storageMap->length()) + return false; + + unsigned quota = m_storageMap->quota(); + m_storageMap = StorageMap::create(quota); + + if (m_storageAreaSync) + m_storageAreaSync->scheduleClear(); + StorageEventDispatcher::dispatch(String(), String(), String(), m_storageType, m_securityOrigin.get(), frame); + return true; +} + +bool StorageAreaImpl::contains(const String& key) const +{ + ASSERT(!m_isShutdown); + blockUntilImportComplete(); + + return m_storageMap->contains(key); +} + +void StorageAreaImpl::importItem(const String& key, const String& value) +{ + ASSERT(!m_isShutdown); + m_storageMap->importItem(key, value); +} + +void StorageAreaImpl::close() +{ + if (m_storageAreaSync) + m_storageAreaSync->scheduleFinalSync(); + +#ifndef NDEBUG + m_isShutdown = true; +#endif +} + +void StorageAreaImpl::blockUntilImportComplete() const +{ + if (m_storageAreaSync) + m_storageAreaSync->blockUntilImportComplete(); +} + +} + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/StorageAreaImpl.h b/Source/WebCore/storage/StorageAreaImpl.h new file mode 100644 index 0000000..60d72cb --- /dev/null +++ b/Source/WebCore/storage/StorageAreaImpl.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StorageAreaImpl_h +#define StorageAreaImpl_h + +#if ENABLE(DOM_STORAGE) + +#include "StorageArea.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class SecurityOrigin; + class StorageMap; + class StorageAreaSync; + + class StorageAreaImpl : public StorageArea { + public: + static PassRefPtr<StorageAreaImpl> create(StorageType, PassRefPtr<SecurityOrigin>, PassRefPtr<StorageSyncManager>, unsigned quota); + virtual ~StorageAreaImpl(); + + // The HTML5 DOM Storage API (and contains) + virtual unsigned length() const; + virtual String key(unsigned index) const; + virtual String getItem(const String& key) const; + virtual String setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame); + virtual String removeItem(const String& key, Frame* sourceFrame); + virtual bool clear(Frame* sourceFrame); + virtual bool contains(const String& key) const; + + PassRefPtr<StorageAreaImpl> copy(); + void close(); + + // Only called from a background thread. + void importItem(const String& key, const String& value); + + private: + StorageAreaImpl(StorageType, PassRefPtr<SecurityOrigin>, PassRefPtr<StorageSyncManager>, unsigned quota); + StorageAreaImpl(StorageAreaImpl*); + + void blockUntilImportComplete() const; + + StorageType m_storageType; + RefPtr<SecurityOrigin> m_securityOrigin; + RefPtr<StorageMap> m_storageMap; + + RefPtr<StorageAreaSync> m_storageAreaSync; + RefPtr<StorageSyncManager> m_storageSyncManager; + +#ifndef NDEBUG + bool m_isShutdown; +#endif + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageAreaImpl_h diff --git a/Source/WebCore/storage/StorageAreaSync.cpp b/Source/WebCore/storage/StorageAreaSync.cpp new file mode 100644 index 0000000..f2008ab --- /dev/null +++ b/Source/WebCore/storage/StorageAreaSync.cpp @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "StorageAreaSync.h" + +#if ENABLE(DOM_STORAGE) + +#include "EventNames.h" +#include "FileSystem.h" +#include "HTMLElement.h" +#include "SQLiteFileSystem.h" +#include "SQLiteStatement.h" +#include "SecurityOrigin.h" +#include "StorageAreaImpl.h" +#include "StorageSyncManager.h" +#include "SuddenTermination.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +// If the StorageArea undergoes rapid changes, don't sync each change to disk. +// Instead, queue up a batch of items to sync and actually do the sync at the following interval. +static const double StorageSyncInterval = 1.0; + +// A sane limit on how many items we'll schedule to sync all at once. This makes it +// much harder to starve the rest of LocalStorage and the OS's IO subsystem in general. +static const int MaxiumItemsToSync = 100; + +inline StorageAreaSync::StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea, const String& databaseIdentifier) + : m_syncTimer(this, &StorageAreaSync::syncTimerFired) + , m_itemsCleared(false) + , m_finalSyncScheduled(false) + , m_storageArea(storageArea) + , m_syncManager(storageSyncManager) + , m_databaseIdentifier(databaseIdentifier.crossThreadString()) + , m_clearItemsWhileSyncing(false) + , m_syncScheduled(false) + , m_syncInProgress(false) + , m_databaseOpenFailed(false) + , m_importComplete(false) +{ + ASSERT(isMainThread()); + ASSERT(m_storageArea); + ASSERT(m_syncManager); +} + +PassRefPtr<StorageAreaSync> StorageAreaSync::create(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea, const String& databaseIdentifier) +{ + RefPtr<StorageAreaSync> area = adoptRef(new StorageAreaSync(storageSyncManager, storageArea, databaseIdentifier)); + + // FIXME: If it can't import, then the default WebKit behavior should be that of private browsing, + // not silently ignoring it. https://bugs.webkit.org/show_bug.cgi?id=25894 + if (!area->m_syncManager->scheduleImport(area.get())) + area->m_importComplete = true; + + return area.release(); +} + +StorageAreaSync::~StorageAreaSync() +{ + ASSERT(isMainThread()); + ASSERT(!m_syncTimer.isActive()); + ASSERT(m_finalSyncScheduled); +} + +void StorageAreaSync::scheduleFinalSync() +{ + ASSERT(isMainThread()); + // FIXME: We do this to avoid races, but it'd be better to make things safe without blocking. + blockUntilImportComplete(); + m_storageArea = 0; // This is done in blockUntilImportComplete() but this is here as a form of documentation that we must be absolutely sure the ref count cycle is broken. + + if (m_syncTimer.isActive()) + m_syncTimer.stop(); + else { + // The following is balanced by the call to enableSuddenTermination in the + // syncTimerFired function. + disableSuddenTermination(); + } + // FIXME: This is synchronous. We should do it on the background process, but + // we should do it safely. + m_finalSyncScheduled = true; + syncTimerFired(&m_syncTimer); + m_syncManager->scheduleDeleteEmptyDatabase(this); +} + +void StorageAreaSync::scheduleItemForSync(const String& key, const String& value) +{ + ASSERT(isMainThread()); + ASSERT(!m_finalSyncScheduled); + + m_changedItems.set(key, value); + if (!m_syncTimer.isActive()) { + m_syncTimer.startOneShot(StorageSyncInterval); + + // The following is balanced by the call to enableSuddenTermination in the + // syncTimerFired function. + disableSuddenTermination(); + } +} + +void StorageAreaSync::scheduleClear() +{ + ASSERT(isMainThread()); + ASSERT(!m_finalSyncScheduled); + + m_changedItems.clear(); + m_itemsCleared = true; + if (!m_syncTimer.isActive()) { + m_syncTimer.startOneShot(StorageSyncInterval); + + // The following is balanced by the call to enableSuddenTermination in the + // syncTimerFired function. + disableSuddenTermination(); + } +} + +void StorageAreaSync::syncTimerFired(Timer<StorageAreaSync>*) +{ + ASSERT(isMainThread()); + + bool partialSync = false; + { + MutexLocker locker(m_syncLock); + + // Do not schedule another sync if we're still trying to complete the + // previous one. But, if we're shutting down, schedule it anyway. + if (m_syncInProgress && !m_finalSyncScheduled) { + ASSERT(!m_syncTimer.isActive()); + m_syncTimer.startOneShot(StorageSyncInterval); + return; + } + + if (m_itemsCleared) { + m_itemsPendingSync.clear(); + m_clearItemsWhileSyncing = true; + m_itemsCleared = false; + } + + HashMap<String, String>::iterator changed_it = m_changedItems.begin(); + HashMap<String, String>::iterator changed_end = m_changedItems.end(); + for (int count = 0; changed_it != changed_end; ++count, ++changed_it) { + if (count >= MaxiumItemsToSync && !m_finalSyncScheduled) { + partialSync = true; + break; + } + m_itemsPendingSync.set(changed_it->first.crossThreadString(), changed_it->second.crossThreadString()); + } + + if (partialSync) { + // We can't do the fast path of simply clearing all items, so we'll need to manually + // remove them one by one. Done under lock since m_itemsPendingSync is modified by + // the background thread. + HashMap<String, String>::iterator pending_it = m_itemsPendingSync.begin(); + HashMap<String, String>::iterator pending_end = m_itemsPendingSync.end(); + for (; pending_it != pending_end; ++pending_it) + m_changedItems.remove(pending_it->first); + } + + if (!m_syncScheduled) { + m_syncScheduled = true; + + // The following is balanced by the call to enableSuddenTermination in the + // performSync function. + disableSuddenTermination(); + + m_syncManager->scheduleSync(this); + } + } + + if (partialSync) { + // If we didn't finish syncing, then we need to finish the job later. + ASSERT(!m_syncTimer.isActive()); + m_syncTimer.startOneShot(StorageSyncInterval); + } else { + // The following is balanced by the calls to disableSuddenTermination in the + // scheduleItemForSync, scheduleClear, and scheduleFinalSync functions. + enableSuddenTermination(); + + m_changedItems.clear(); + } +} + +void StorageAreaSync::openDatabase(OpenDatabaseParamType openingStrategy) +{ + ASSERT(!isMainThread()); + ASSERT(!m_database.isOpen()); + ASSERT(!m_databaseOpenFailed); + + String databaseFilename = m_syncManager->fullDatabaseFilename(m_databaseIdentifier); + + if (!fileExists(databaseFilename) && openingStrategy == SkipIfNonExistent) + return; + + if (databaseFilename.isEmpty()) { + LOG_ERROR("Filename for local storage database is empty - cannot open for persistent storage"); + markImported(); + m_databaseOpenFailed = true; + return; + } + + if (!m_database.open(databaseFilename)) { + LOG_ERROR("Failed to open database file %s for local storage", databaseFilename.utf8().data()); + markImported(); + m_databaseOpenFailed = true; + return; + } + + if (!m_database.executeCommand("CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value TEXT NOT NULL ON CONFLICT FAIL)")) { + LOG_ERROR("Failed to create table ItemTable for local storage"); + markImported(); + m_databaseOpenFailed = true; + return; + } +} + +void StorageAreaSync::performImport() +{ + ASSERT(!isMainThread()); + ASSERT(!m_database.isOpen()); + + openDatabase(SkipIfNonExistent); + if (!m_database.isOpen()) { + markImported(); + return; + } + + SQLiteStatement query(m_database, "SELECT key, value FROM ItemTable"); + if (query.prepare() != SQLResultOk) { + LOG_ERROR("Unable to select items from ItemTable for local storage"); + markImported(); + return; + } + + HashMap<String, String> itemMap; + + int result = query.step(); + while (result == SQLResultRow) { + itemMap.set(query.getColumnText(0), query.getColumnText(1)); + result = query.step(); + } + + if (result != SQLResultDone) { + LOG_ERROR("Error reading items from ItemTable for local storage"); + markImported(); + return; + } + + HashMap<String, String>::iterator it = itemMap.begin(); + HashMap<String, String>::iterator end = itemMap.end(); + + for (; it != end; ++it) + m_storageArea->importItem(it->first, it->second); + + markImported(); +} + +void StorageAreaSync::markImported() +{ + MutexLocker locker(m_importLock); + m_importComplete = true; + m_importCondition.signal(); +} + +// FIXME: In the future, we should allow use of StorageAreas while it's importing (when safe to do so). +// Blocking everything until the import is complete is by far the simplest and safest thing to do, but +// there is certainly room for safe optimization: Key/length will never be able to make use of such an +// optimization (since the order of iteration can change as items are being added). Get can return any +// item currently in the map. Get/remove can work whether or not it's in the map, but we'll need a list +// of items the import should not overwrite. Clear can also work, but it'll need to kill the import +// job first. +void StorageAreaSync::blockUntilImportComplete() +{ + ASSERT(isMainThread()); + + // Fast path. We set m_storageArea to 0 only after m_importComplete being true. + if (!m_storageArea) + return; + + MutexLocker locker(m_importLock); + while (!m_importComplete) + m_importCondition.wait(m_importLock); + m_storageArea = 0; +} + +void StorageAreaSync::sync(bool clearItems, const HashMap<String, String>& items) +{ + ASSERT(!isMainThread()); + + if (items.isEmpty() && !clearItems) + return; + if (m_databaseOpenFailed) + return; + if (!m_database.isOpen()) + openDatabase(CreateIfNonExistent); + if (!m_database.isOpen()) + return; + + // If the clear flag is set, then we clear all items out before we write any new ones in. + if (clearItems) { + SQLiteStatement clear(m_database, "DELETE FROM ItemTable"); + if (clear.prepare() != SQLResultOk) { + LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database"); + return; + } + + int result = clear.step(); + if (result != SQLResultDone) { + LOG_ERROR("Failed to clear all items in the local storage database - %i", result); + return; + } + } + + SQLiteStatement insert(m_database, "INSERT INTO ItemTable VALUES (?, ?)"); + if (insert.prepare() != SQLResultOk) { + LOG_ERROR("Failed to prepare insert statement - cannot write to local storage database"); + return; + } + + SQLiteStatement remove(m_database, "DELETE FROM ItemTable WHERE key=?"); + if (remove.prepare() != SQLResultOk) { + LOG_ERROR("Failed to prepare delete statement - cannot write to local storage database"); + return; + } + + HashMap<String, String>::const_iterator end = items.end(); + + for (HashMap<String, String>::const_iterator it = items.begin(); it != end; ++it) { + // Based on the null-ness of the second argument, decide whether this is an insert or a delete. + SQLiteStatement& query = it->second.isNull() ? remove : insert; + + query.bindText(1, it->first); + + // If the second argument is non-null, we're doing an insert, so bind it as the value. + if (!it->second.isNull()) + query.bindText(2, it->second); + + int result = query.step(); + if (result != SQLResultDone) { + LOG_ERROR("Failed to update item in the local storage database - %i", result); + break; + } + + query.reset(); + } +} + +void StorageAreaSync::performSync() +{ + ASSERT(!isMainThread()); + + bool clearItems; + HashMap<String, String> items; + { + MutexLocker locker(m_syncLock); + + ASSERT(m_syncScheduled); + + clearItems = m_clearItemsWhileSyncing; + m_itemsPendingSync.swap(items); + + m_clearItemsWhileSyncing = false; + m_syncScheduled = false; + m_syncInProgress = true; + } + + sync(clearItems, items); + + { + MutexLocker locker(m_syncLock); + m_syncInProgress = false; + } + + // The following is balanced by the call to disableSuddenTermination in the + // syncTimerFired function. + enableSuddenTermination(); +} + +void StorageAreaSync::deleteEmptyDatabase() +{ + ASSERT(!isMainThread()); + if (!m_database.isOpen()) + return; + + SQLiteStatement query(m_database, "SELECT COUNT(*) FROM ItemTable"); + if (query.prepare() != SQLResultOk) { + LOG_ERROR("Unable to count number of rows in ItemTable for local storage"); + return; + } + + int result = query.step(); + if (result != SQLResultRow) { + LOG_ERROR("No results when counting number of rows in ItemTable for local storage"); + return; + } + + int count = query.getColumnInt(0); + if (!count) { + query.finalize(); + m_database.close(); + String databaseFilename = m_syncManager->fullDatabaseFilename(m_databaseIdentifier); + if (!SQLiteFileSystem::deleteDatabaseFile(databaseFilename)) + LOG_ERROR("Failed to delete database file %s\n", databaseFilename.utf8().data()); + } +} + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/StorageAreaSync.h b/Source/WebCore/storage/StorageAreaSync.h new file mode 100644 index 0000000..90aa6a7 --- /dev/null +++ b/Source/WebCore/storage/StorageAreaSync.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2008, 2009, 2010 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StorageAreaSync_h +#define StorageAreaSync_h + +#if ENABLE(DOM_STORAGE) + +#include "SQLiteDatabase.h" +#include "Timer.h" +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + + class Frame; + class StorageAreaImpl; + class StorageSyncManager; + + class StorageAreaSync : public RefCounted<StorageAreaSync> { + public: + static PassRefPtr<StorageAreaSync> create(PassRefPtr<StorageSyncManager>, PassRefPtr<StorageAreaImpl>, const String& databaseIdentifier); + ~StorageAreaSync(); + + void scheduleFinalSync(); + void blockUntilImportComplete(); + + void scheduleItemForSync(const String& key, const String& value); + void scheduleClear(); + + private: + StorageAreaSync(PassRefPtr<StorageSyncManager>, PassRefPtr<StorageAreaImpl>, const String& databaseIdentifier); + + void dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame); + + Timer<StorageAreaSync> m_syncTimer; + HashMap<String, String> m_changedItems; + bool m_itemsCleared; + + bool m_finalSyncScheduled; + + RefPtr<StorageAreaImpl> m_storageArea; + RefPtr<StorageSyncManager> m_syncManager; + + // The database handle will only ever be opened and used on the background thread. + SQLiteDatabase m_database; + + // The following members are subject to thread synchronization issues. + public: + // Called from the background thread + void performImport(); + void performSync(); + void deleteEmptyDatabase(); + + private: + enum OpenDatabaseParamType { + CreateIfNonExistent, + SkipIfNonExistent + }; + + void syncTimerFired(Timer<StorageAreaSync>*); + void openDatabase(OpenDatabaseParamType openingStrategy); + void sync(bool clearItems, const HashMap<String, String>& items); + + const String m_databaseIdentifier; + + Mutex m_syncLock; + HashMap<String, String> m_itemsPendingSync; + bool m_clearItemsWhileSyncing; + bool m_syncScheduled; + bool m_syncInProgress; + bool m_databaseOpenFailed; + + mutable Mutex m_importLock; + mutable ThreadCondition m_importCondition; + mutable bool m_importComplete; + void markImported(); + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageAreaSync_h diff --git a/Source/WebCore/storage/StorageEvent.cpp b/Source/WebCore/storage/StorageEvent.cpp new file mode 100644 index 0000000..a08cde2 --- /dev/null +++ b/Source/WebCore/storage/StorageEvent.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "StorageEvent.h" + +#if ENABLE(DOM_STORAGE) + +#include "Storage.h" + +namespace WebCore { + +PassRefPtr<StorageEvent> StorageEvent::create() +{ + return adoptRef(new StorageEvent); +} + +StorageEvent::StorageEvent() +{ +} + +StorageEvent::~StorageEvent() +{ +} + +PassRefPtr<StorageEvent> StorageEvent::create(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& url, Storage* storageArea) +{ + return adoptRef(new StorageEvent(type, key, oldValue, newValue, url, storageArea)); +} + +StorageEvent::StorageEvent(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& url, Storage* storageArea) + : Event(type, false, false) + , m_key(key) + , m_oldValue(oldValue) + , m_newValue(newValue) + , m_url(url) + , m_storageArea(storageArea) +{ +} + +void StorageEvent::initStorageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& key, const String& oldValue, const String& newValue, const String& url, Storage* storageArea) +{ + if (dispatched()) + return; + + initEvent(type, canBubble, cancelable); + + m_key = key; + m_oldValue = oldValue; + m_newValue = newValue; + m_url = url; + m_storageArea = storageArea; +} + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/StorageEvent.h b/Source/WebCore/storage/StorageEvent.h new file mode 100644 index 0000000..8e558a8 --- /dev/null +++ b/Source/WebCore/storage/StorageEvent.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StorageEvent_h +#define StorageEvent_h + +#if ENABLE(DOM_STORAGE) + +#include "Event.h" +#include "PlatformString.h" + +namespace WebCore { + + class Storage; + + class StorageEvent : public Event { + public: + static PassRefPtr<StorageEvent> create(); + static PassRefPtr<StorageEvent> create(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& url, Storage* storageArea); + virtual ~StorageEvent(); + + const String& key() const { return m_key; } + const String& oldValue() const { return m_oldValue; } + const String& newValue() const { return m_newValue; } + const String& url() const { return m_url; } + Storage* storageArea() const { return m_storageArea.get(); } + + void initStorageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& key, const String& oldValue, const String& newValue, const String& url, Storage* storageArea); + + // Needed once we support init<blank>EventNS + // void initStorageEventNS(in DOMString namespaceURI, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString urlArg, Storage storageAreaArg); + + virtual bool isStorageEvent() const { return true; } + + private: + StorageEvent(); + StorageEvent(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& url, Storage* storageArea); + + String m_key; + String m_oldValue; + String m_newValue; + String m_url; + RefPtr<Storage> m_storageArea; + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageEvent_h diff --git a/Source/WebCore/storage/StorageEvent.idl b/Source/WebCore/storage/StorageEvent.idl new file mode 100644 index 0000000..3464caa --- /dev/null +++ b/Source/WebCore/storage/StorageEvent.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +module storage { + + interface [ + Conditional=DOM_STORAGE + ] StorageEvent : Event { + readonly attribute [ConvertNullStringTo=Null] DOMString key; + readonly attribute [ConvertNullStringTo=Null] DOMString oldValue; + readonly attribute [ConvertNullStringTo=Null] DOMString newValue; + readonly attribute DOMString url; + readonly attribute Storage storageArea; + void initStorageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in [ConvertNullToNullString] DOMString oldValueArg, in [ConvertNullToNullString] DOMString newValueArg, in DOMString urlArg, in Storage storageAreaArg); + + // Needed once we support init<blank>EventNS + // void initStorageEventNS(in DOMString namespaceURI, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString urlArg, in Storage storageAreaArg); + }; + +} diff --git a/Source/WebCore/storage/StorageEventDispatcher.cpp b/Source/WebCore/storage/StorageEventDispatcher.cpp new file mode 100644 index 0000000..e662b03 --- /dev/null +++ b/Source/WebCore/storage/StorageEventDispatcher.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "StorageEventDispatcher.h" + +#if ENABLE(DOM_STORAGE) + +#include "DOMWindow.h" +#include "EventNames.h" +#include "Frame.h" +#include "Page.h" +#include "PageGroup.h" +#include "SecurityOrigin.h" +#include "StorageEvent.h" + +namespace WebCore { + +void StorageEventDispatcher::dispatch(const String& key, const String& oldValue, const String& newValue, StorageType storageType, SecurityOrigin* securityOrigin, Frame* sourceFrame) +{ +#ifdef ANDROID + if (!sourceFrame) + return; +#endif + Page* page = sourceFrame->page(); + if (!page) + return; + + // We need to copy all relevant frames from every page to a vector since sending the event to one frame might mutate the frame tree + // of any given page in the group or mutate the page group itself. + Vector<RefPtr<Frame> > frames; + if (storageType == SessionStorage) { + // Send events only to our page. + for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (sourceFrame != frame && frame->document()->securityOrigin()->equal(securityOrigin)) + frames.append(frame); + } + + for (unsigned i = 0; i < frames.size(); ++i) { + ExceptionCode ec = 0; + Storage* storage = frames[i]->domWindow()->sessionStorage(ec); + if (!ec) + frames[i]->document()->enqueueWindowEvent(StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, sourceFrame->document()->url(), storage)); + } + } else { + // Send events to every page. + const HashSet<Page*>& pages = page->group().pages(); + HashSet<Page*>::const_iterator end = pages.end(); + for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) { + for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (sourceFrame != frame && frame->document()->securityOrigin()->equal(securityOrigin)) + frames.append(frame); + } + } + + for (unsigned i = 0; i < frames.size(); ++i) { + ExceptionCode ec = 0; + Storage* storage = frames[i]->domWindow()->localStorage(ec); + if (!ec) + frames[i]->document()->enqueueWindowEvent(StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, sourceFrame->document()->url(), storage)); + } + } +} + +} + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/StorageEventDispatcher.h b/Source/WebCore/storage/StorageEventDispatcher.h new file mode 100644 index 0000000..f4a98ef --- /dev/null +++ b/Source/WebCore/storage/StorageEventDispatcher.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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. + */ + +#ifndef StorageEventDispatcher_h +#define StorageEventDispatcher_h + +#if ENABLE(DOM_STORAGE) + +#include "PlatformString.h" +#include "StorageArea.h" + +namespace WebCore { + + // This is in its own class since Chromium must override it. + class StorageEventDispatcher { + public: + static void dispatch(const String& key, const String& oldValue, const String& newValue, StorageType, SecurityOrigin*, Frame* sourceFrame); + + private: + // Do not instantiate. + StorageEventDispatcher(); + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageEventDispatcher_h diff --git a/Source/WebCore/storage/StorageMap.cpp b/Source/WebCore/storage/StorageMap.cpp new file mode 100644 index 0000000..790fde2 --- /dev/null +++ b/Source/WebCore/storage/StorageMap.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "StorageMap.h" + +#if ENABLE(DOM_STORAGE) + +namespace WebCore { + +PassRefPtr<StorageMap> StorageMap::create(unsigned quota) +{ + return adoptRef(new StorageMap(quota)); +} + +StorageMap::StorageMap(unsigned quota) + : m_iterator(m_map.end()) + , m_iteratorIndex(UINT_MAX) + , m_quotaSize(quota) // quota measured in bytes + , m_currentLength(0) +{ +} + +PassRefPtr<StorageMap> StorageMap::copy() +{ + RefPtr<StorageMap> newMap = create(m_quotaSize); + newMap->m_map = m_map; + newMap->m_currentLength = m_currentLength; + return newMap.release(); +} + +void StorageMap::invalidateIterator() +{ + m_iterator = m_map.end(); + m_iteratorIndex = UINT_MAX; +} + +void StorageMap::setIteratorToIndex(unsigned index) +{ + // FIXME: Once we have bidirectional iterators for HashMap we can be more intelligent about this. + // The requested index will be closest to begin(), our current iterator, or end(), and we + // can take the shortest route. + // Until that mechanism is available, we'll always increment our iterator from begin() or current. + + if (m_iteratorIndex == index) + return; + + if (index < m_iteratorIndex) { + m_iteratorIndex = 0; + m_iterator = m_map.begin(); + ASSERT(m_iterator != m_map.end()); + } + + while (m_iteratorIndex < index) { + ++m_iteratorIndex; + ++m_iterator; + ASSERT(m_iterator != m_map.end()); + } +} + +unsigned StorageMap::length() const +{ + return m_map.size(); +} + +String StorageMap::key(unsigned index) +{ + if (index >= length()) + return String(); + + setIteratorToIndex(index); + return m_iterator->first; +} + +String StorageMap::getItem(const String& key) const +{ + return m_map.get(key); +} + +PassRefPtr<StorageMap> StorageMap::setItem(const String& key, const String& value, String& oldValue, bool& quotaException) +{ + ASSERT(!value.isNull()); + quotaException = false; + + // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects + // so if more than one Storage object refs this map, copy it before mutating it. + if (refCount() > 1) { + RefPtr<StorageMap> newStorageMap = copy(); + newStorageMap->setItem(key, value, oldValue, quotaException); + return newStorageMap.release(); + } + + // Quota tracking. This is done in a couple of steps to keep the overflow tracking simple. + unsigned newLength = m_currentLength; + bool overflow = newLength + value.length() < newLength; + newLength += value.length(); + + oldValue = m_map.get(key); + overflow |= newLength - oldValue.length() > newLength; + newLength -= oldValue.length(); + + unsigned adjustedKeyLength = oldValue.isNull() ? key.length() : 0; + overflow |= newLength + adjustedKeyLength < newLength; + newLength += adjustedKeyLength; + + ASSERT(!overflow); // Overflow is bad...even if quotas are off. + bool overQuota = newLength > m_quotaSize / sizeof(UChar); + if (m_quotaSize != noQuota && (overflow || overQuota)) { + quotaException = true; + return 0; + } + m_currentLength = newLength; + + pair<HashMap<String, String>::iterator, bool> addResult = m_map.add(key, value); + if (!addResult.second) + addResult.first->second = value; + + invalidateIterator(); + + return 0; +} + +PassRefPtr<StorageMap> StorageMap::removeItem(const String& key, String& oldValue) +{ + // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects + // so if more than one Storage object refs this map, copy it before mutating it. + if (refCount() > 1) { + RefPtr<StorageMap> newStorage = copy(); + newStorage->removeItem(key, oldValue); + return newStorage.release(); + } + + oldValue = m_map.take(key); + if (!oldValue.isNull()) { + invalidateIterator(); + ASSERT(m_currentLength - key.length() <= m_currentLength); + m_currentLength -= key.length(); + } + ASSERT(m_currentLength - oldValue.length() <= m_currentLength); + m_currentLength -= oldValue.length(); + + return 0; +} + +bool StorageMap::contains(const String& key) const +{ + return m_map.contains(key); +} + +void StorageMap::importItem(const String& key, const String& value) +{ + // Be sure to copy the keys/values as items imported on a background thread are destined + // to cross a thread boundary + pair<HashMap<String, String>::iterator, bool> result = m_map.add(key.threadsafeCopy(), value.threadsafeCopy()); + ASSERT(result.second); // True if the key didn't exist previously. + + ASSERT(m_currentLength + key.length() >= m_currentLength); + m_currentLength += key.length(); + ASSERT(m_currentLength + value.length() >= m_currentLength); + m_currentLength += value.length(); +} + +} + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/StorageMap.h b/Source/WebCore/storage/StorageMap.h new file mode 100644 index 0000000..cc09d0f --- /dev/null +++ b/Source/WebCore/storage/StorageMap.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StorageMap_h +#define StorageMap_h + +#if ENABLE(DOM_STORAGE) + +#include "PlatformString.h" +#include <wtf/HashMap.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + + class StorageMap : public RefCounted<StorageMap> { + public: + // Quota size measured in bytes. + static PassRefPtr<StorageMap> create(unsigned quotaSize); + + unsigned length() const; + String key(unsigned index); + String getItem(const String&) const; + PassRefPtr<StorageMap> setItem(const String& key, const String& value, String& oldValue, bool& quota_exception); + PassRefPtr<StorageMap> removeItem(const String&, String& oldValue); + + bool contains(const String& key) const; + + void importItem(const String& key, const String& value); + + unsigned quota() const { return m_quotaSize; } + + static const unsigned noQuota = UINT_MAX; + + private: + StorageMap(unsigned quota); + PassRefPtr<StorageMap> copy(); + void invalidateIterator(); + void setIteratorToIndex(unsigned); + + HashMap<String, String> m_map; + HashMap<String, String>::iterator m_iterator; + unsigned m_iteratorIndex; + + unsigned m_quotaSize; // Measured in bytes. + unsigned m_currentLength; // Measured in UChars. + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageMap_h diff --git a/Source/WebCore/storage/StorageNamespace.cpp b/Source/WebCore/storage/StorageNamespace.cpp new file mode 100644 index 0000000..ce36608 --- /dev/null +++ b/Source/WebCore/storage/StorageNamespace.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "StorageNamespace.h" + +#include "StorageNamespaceImpl.h" + +#if PLATFORM(CHROMIUM) +#error "Chromium should not compile this file and instead define its own version of these factories that navigate the multi-process boundry." +#endif + +#if ENABLE(DOM_STORAGE) + +namespace WebCore { + +PassRefPtr<StorageNamespace> StorageNamespace::localStorageNamespace(const String& path, unsigned quota) +{ + return StorageNamespaceImpl::localStorageNamespace(path, quota); +} + +// The page argument is only used by the Chromium port. +PassRefPtr<StorageNamespace> StorageNamespace::sessionStorageNamespace(Page*, unsigned quota) +{ + return StorageNamespaceImpl::sessionStorageNamespace(quota); +} + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/StorageNamespace.h b/Source/WebCore/storage/StorageNamespace.h new file mode 100644 index 0000000..f7dad1e --- /dev/null +++ b/Source/WebCore/storage/StorageNamespace.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StorageNamespace_h +#define StorageNamespace_h + +#if ENABLE(DOM_STORAGE) + +#include "PlatformString.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class Page; +class SecurityOrigin; +class StorageArea; + +// This interface is required for Chromium since these actions need to be proxied between processes. +class StorageNamespace : public RefCounted<StorageNamespace> { +public: + static PassRefPtr<StorageNamespace> localStorageNamespace(const String& path, unsigned quota); + static PassRefPtr<StorageNamespace> sessionStorageNamespace(Page*, unsigned quota); + + virtual ~StorageNamespace() { } + virtual PassRefPtr<StorageArea> storageArea(PassRefPtr<SecurityOrigin>) = 0; + virtual PassRefPtr<StorageNamespace> copy() = 0; + virtual void close() = 0; + virtual void unlock() = 0; + +#ifdef ANDROID + virtual void clear(Page*) = 0; +#endif +}; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageNamespace_h diff --git a/Source/WebCore/storage/StorageNamespaceImpl.cpp b/Source/WebCore/storage/StorageNamespaceImpl.cpp new file mode 100644 index 0000000..68508a7 --- /dev/null +++ b/Source/WebCore/storage/StorageNamespaceImpl.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "StorageNamespaceImpl.h" + +#if ENABLE(DOM_STORAGE) + +#include "SecurityOriginHash.h" +#include "StorageAreaImpl.h" +#include "StorageMap.h" +#include "StorageSyncManager.h" +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringHash.h> + +#ifdef ANDROID +#include "Page.h" +#endif + +namespace WebCore { + +typedef HashMap<String, StorageNamespace*> LocalStorageNamespaceMap; + +static LocalStorageNamespaceMap& localStorageNamespaceMap() +{ + DEFINE_STATIC_LOCAL(LocalStorageNamespaceMap, localStorageNamespaceMap, ()); + return localStorageNamespaceMap; +} + +PassRefPtr<StorageNamespace> StorageNamespaceImpl::localStorageNamespace(const String& path, unsigned quota) +{ + const String lookupPath = path.isNull() ? String("") : path; + LocalStorageNamespaceMap::iterator it = localStorageNamespaceMap().find(lookupPath); + if (it == localStorageNamespaceMap().end()) { + RefPtr<StorageNamespace> storageNamespace = adoptRef(new StorageNamespaceImpl(LocalStorage, lookupPath, quota)); + localStorageNamespaceMap().set(lookupPath, storageNamespace.get()); + return storageNamespace.release(); + } + + return it->second; +} + +PassRefPtr<StorageNamespace> StorageNamespaceImpl::sessionStorageNamespace(unsigned quota) +{ + return adoptRef(new StorageNamespaceImpl(SessionStorage, String(), quota)); +} + +StorageNamespaceImpl::StorageNamespaceImpl(StorageType storageType, const String& path, unsigned quota) + : m_storageType(storageType) + , m_path(path.crossThreadString()) + , m_syncManager(0) + , m_quota(quota) + , m_isShutdown(false) +{ + if (m_storageType == LocalStorage && !m_path.isEmpty()) + m_syncManager = StorageSyncManager::create(m_path); +} + +StorageNamespaceImpl::~StorageNamespaceImpl() +{ + ASSERT(isMainThread()); + + if (m_storageType == LocalStorage) { + ASSERT(localStorageNamespaceMap().get(m_path) == this); + localStorageNamespaceMap().remove(m_path); + } + + if (!m_isShutdown) + close(); +} + +PassRefPtr<StorageNamespace> StorageNamespaceImpl::copy() +{ + ASSERT(isMainThread()); + ASSERT(!m_isShutdown); + ASSERT(m_storageType == SessionStorage); + + RefPtr<StorageNamespaceImpl> newNamespace = adoptRef(new StorageNamespaceImpl(m_storageType, m_path, m_quota)); + + StorageAreaMap::iterator end = m_storageAreaMap.end(); + for (StorageAreaMap::iterator i = m_storageAreaMap.begin(); i != end; ++i) + newNamespace->m_storageAreaMap.set(i->first, i->second->copy()); + return newNamespace.release(); +} + +PassRefPtr<StorageArea> StorageNamespaceImpl::storageArea(PassRefPtr<SecurityOrigin> prpOrigin) +{ + ASSERT(isMainThread()); + ASSERT(!m_isShutdown); + + RefPtr<SecurityOrigin> origin = prpOrigin; + RefPtr<StorageAreaImpl> storageArea; + if ((storageArea = m_storageAreaMap.get(origin))) + return storageArea.release(); + + storageArea = StorageAreaImpl::create(m_storageType, origin, m_syncManager, m_quota); + m_storageAreaMap.set(origin.release(), storageArea); + return storageArea.release(); +} + +void StorageNamespaceImpl::close() +{ + ASSERT(isMainThread()); + + if (m_isShutdown) + return; + + // If we're session storage, we shouldn't need to do any work here. + if (m_storageType == SessionStorage) { + ASSERT(!m_syncManager); + return; + } + + StorageAreaMap::iterator end = m_storageAreaMap.end(); + for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it) + it->second->close(); + + if (m_syncManager) + m_syncManager->close(); + + m_isShutdown = true; +} + +#ifdef ANDROID +void StorageNamespaceImpl::clear(Page* page) +{ + ASSERT(isMainThread()); + if (m_isShutdown) + return; + + // Clear all the keys for each of the storage areas. + StorageAreaMap::iterator end = m_storageAreaMap.end(); + for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it) { + // if there is no page provided, then the user tried to clear storage + // with only pages in private browsing mode open. So we do not need to + // provide a Frame* here (as the frame is only used to dispatch storage events + // and private browsing pages won't be using them). + it->second->clear(page ? page->mainFrame() : 0); + } +} +#endif + +void StorageNamespaceImpl::unlock() +{ + // Because there's a single event loop per-process, this is a no-op. +} + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/StorageNamespaceImpl.h b/Source/WebCore/storage/StorageNamespaceImpl.h new file mode 100644 index 0000000..c2361fa --- /dev/null +++ b/Source/WebCore/storage/StorageNamespaceImpl.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StorageNamespaceImpl_h +#define StorageNamespaceImpl_h + +#if ENABLE(DOM_STORAGE) + +#include "PlatformString.h" +#include "SecurityOriginHash.h" +#include "StorageArea.h" +#include "StorageNamespace.h" + +#include <wtf/HashMap.h> +#include <wtf/RefPtr.h> + +namespace WebCore { +#ifdef ANDROID + class Page; +#endif + class StorageAreaImpl; + + class StorageNamespaceImpl : public StorageNamespace { + public: + static PassRefPtr<StorageNamespace> localStorageNamespace(const String& path, unsigned quota); + static PassRefPtr<StorageNamespace> sessionStorageNamespace(unsigned quota); + + virtual ~StorageNamespaceImpl(); + virtual PassRefPtr<StorageArea> storageArea(PassRefPtr<SecurityOrigin>); + virtual PassRefPtr<StorageNamespace> copy(); + virtual void close(); + virtual void unlock(); + +#ifdef ANDROID + virtual void clear(Page*); +#endif + + private: + StorageNamespaceImpl(StorageType, const String& path, unsigned quota); + + typedef HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageAreaImpl>, SecurityOriginHash> StorageAreaMap; + StorageAreaMap m_storageAreaMap; + + StorageType m_storageType; + + // Only used if m_storageType == LocalStorage and the path was not "" in our constructor. + String m_path; + RefPtr<StorageSyncManager> m_syncManager; + + unsigned m_quota; // The default quota for each new storage area. + bool m_isShutdown; + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageNamespaceImpl_h diff --git a/Source/WebCore/storage/StorageSyncManager.cpp b/Source/WebCore/storage/StorageSyncManager.cpp new file mode 100644 index 0000000..1cf8306 --- /dev/null +++ b/Source/WebCore/storage/StorageSyncManager.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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 "StorageSyncManager.h" + +#if ENABLE(DOM_STORAGE) + +#include "EventNames.h" +#include "FileSystem.h" +#include "Frame.h" +#include "FrameTree.h" +#include "LocalStorageTask.h" +#include "LocalStorageThread.h" +#include "Page.h" +#include "PageGroup.h" +#include "StorageAreaSync.h" +#include <wtf/text/CString.h> +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +PassRefPtr<StorageSyncManager> StorageSyncManager::create(const String& path) +{ + return adoptRef(new StorageSyncManager(path)); +} + +StorageSyncManager::StorageSyncManager(const String& path) + : m_thread(LocalStorageThread::create()) + , m_path(path.crossThreadString()) +{ + ASSERT(isMainThread()); + ASSERT(!m_path.isEmpty()); + m_thread->start(); +} + +StorageSyncManager::~StorageSyncManager() +{ + ASSERT(isMainThread()); + ASSERT(!m_thread); +} + +// Called on a background thread. +String StorageSyncManager::fullDatabaseFilename(const String& databaseIdentifier) +{ + if (!makeAllDirectories(m_path)) { + LOG_ERROR("Unabled to create LocalStorage database path %s", m_path.utf8().data()); + return String(); + } + + return pathByAppendingComponent(m_path, databaseIdentifier + ".localstorage"); +} + +void StorageSyncManager::close() +{ + ASSERT(isMainThread()); + + if (m_thread) { + m_thread->terminate(); + m_thread = 0; + } +} + +bool StorageSyncManager::scheduleImport(PassRefPtr<StorageAreaSync> area) +{ + ASSERT(isMainThread()); + ASSERT(m_thread); + if (m_thread) + m_thread->scheduleTask(LocalStorageTask::createImport(area.get())); + return m_thread; +} + +void StorageSyncManager::scheduleSync(PassRefPtr<StorageAreaSync> area) +{ + ASSERT(isMainThread()); + ASSERT(m_thread); + if (m_thread) + m_thread->scheduleTask(LocalStorageTask::createSync(area.get())); +} + +void StorageSyncManager::scheduleDeleteEmptyDatabase(PassRefPtr<StorageAreaSync> area) +{ + ASSERT(isMainThread()); + ASSERT(m_thread); + if (m_thread) + m_thread->scheduleTask(LocalStorageTask::createDeleteEmptyDatabase(area.get())); +} +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) diff --git a/Source/WebCore/storage/StorageSyncManager.h b/Source/WebCore/storage/StorageSyncManager.h new file mode 100644 index 0000000..6fbb75d --- /dev/null +++ b/Source/WebCore/storage/StorageSyncManager.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE INC. ``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 APPLE INC. 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. + */ + +#ifndef StorageSyncManager_h +#define StorageSyncManager_h + +#if ENABLE(DOM_STORAGE) + +#include "PlatformString.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/OwnPtr.h> + +namespace WebCore { + + class LocalStorageThread; + class SecurityOrigin; + class StorageAreaSync; + + class StorageSyncManager : public RefCounted<StorageSyncManager> { + public: + static PassRefPtr<StorageSyncManager> create(const String& path); + ~StorageSyncManager(); + + bool scheduleImport(PassRefPtr<StorageAreaSync>); + void scheduleSync(PassRefPtr<StorageAreaSync>); + void scheduleDeleteEmptyDatabase(PassRefPtr<StorageAreaSync>); + + void close(); + + private: + StorageSyncManager(const String& path); + + OwnPtr<LocalStorageThread> m_thread; + + // The following members are subject to thread synchronization issues + public: + // To be called from the background thread: + String fullDatabaseFilename(const String& databaseIdentifier); + + private: + String m_path; + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageSyncManager_h diff --git a/Source/WebCore/storage/chromium/DatabaseObserver.h b/Source/WebCore/storage/chromium/DatabaseObserver.h new file mode 100644 index 0000000..deb8036 --- /dev/null +++ b/Source/WebCore/storage/chromium/DatabaseObserver.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef DatabaseObserver_h +#define DatabaseObserver_h + +#if ENABLE(DATABASE) + +#include <wtf/Forward.h> + +namespace WebCore { + +class AbstractDatabase; +class ScriptExecutionContext; + +// The implementation of this class is in the WebKit API (Chromium source tree) +// in WebKit/chromium/src/DatabaseObserver.cpp. +class DatabaseObserver { +public: + static bool canEstablishDatabase(ScriptExecutionContext*, const String&, const String&, unsigned long); + static void databaseOpened(AbstractDatabase*); + static void databaseModified(AbstractDatabase*); + static void databaseClosed(AbstractDatabase*); +}; + +} + +#endif // ENABLE(DATABASE) + +#endif // DatabaseObserver_h diff --git a/Source/WebCore/storage/chromium/DatabaseTrackerChromium.cpp b/Source/WebCore/storage/chromium/DatabaseTrackerChromium.cpp new file mode 100644 index 0000000..361e203 --- /dev/null +++ b/Source/WebCore/storage/chromium/DatabaseTrackerChromium.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 "DatabaseTracker.h" + +#if ENABLE(DATABASE) + +#include "AbstractDatabase.h" +#include "DatabaseObserver.h" +#include "QuotaTracker.h" +#include "PlatformString.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include "SecurityOriginHash.h" +#include "SQLiteFileSystem.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +DatabaseTracker& DatabaseTracker::tracker() +{ + DEFINE_STATIC_LOCAL(DatabaseTracker, tracker, ("")); + return tracker; +} + +DatabaseTracker::DatabaseTracker(const String&) +{ + SQLiteFileSystem::registerSQLiteVFS(); +} + +bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext* scriptExecutionContext, const String& name, const String& displayName, unsigned long estimatedSize) +{ + return DatabaseObserver::canEstablishDatabase(scriptExecutionContext, name, displayName, estimatedSize); +} + +void DatabaseTracker::setDatabaseDetails(SecurityOrigin*, const String&, const String&, unsigned long) +{ + // Chromium sets the database details when the database is opened +} + +String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String& name, bool) +{ + return origin->databaseIdentifier() + "/" + name + "#"; +} + +void DatabaseTracker::addOpenDatabase(AbstractDatabase* database) +{ + ASSERT(database->scriptExecutionContext()->isContextThread()); + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + if (!m_openDatabaseMap) + m_openDatabaseMap.set(new DatabaseOriginMap()); + + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); + if (!nameMap) { + nameMap = new DatabaseNameMap(); + m_openDatabaseMap->set(database->securityOrigin(), nameMap); + } + + String name(database->stringIdentifier()); + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) { + databaseSet = new DatabaseSet(); + nameMap->set(name, databaseSet); + } + + databaseSet->add(database); + + DatabaseObserver::databaseOpened(database); +} + +class TrackerRemoveOpenDatabaseTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<TrackerRemoveOpenDatabaseTask> create(PassRefPtr<AbstractDatabase> database) + { + return new TrackerRemoveOpenDatabaseTask(database); + } + + virtual void performTask(ScriptExecutionContext* context) + { + DatabaseTracker::tracker().removeOpenDatabase(m_database.get()); + } + +private: + TrackerRemoveOpenDatabaseTask(PassRefPtr<AbstractDatabase> database) + : m_database(database) + { + } + + RefPtr<AbstractDatabase> m_database; +}; + +void DatabaseTracker::removeOpenDatabase(AbstractDatabase* database) +{ + if (!database->scriptExecutionContext()->isContextThread()) { + database->scriptExecutionContext()->postTask(TrackerRemoveOpenDatabaseTask::create(database)); + return; + } + + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + ASSERT(m_openDatabaseMap); + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); + ASSERT(nameMap); + String name(database->stringIdentifier()); + DatabaseSet* databaseSet = nameMap->get(name); + ASSERT(databaseSet); + databaseSet->remove(database); + + if (databaseSet->isEmpty()) { + nameMap->remove(name); + delete databaseSet; + if (nameMap->isEmpty()) { + m_openDatabaseMap->remove(database->securityOrigin()); + delete nameMap; + } + } + + DatabaseObserver::databaseClosed(database); +} + + +void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<AbstractDatabase> >* databases) +{ + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + if (!m_openDatabaseMap) + return; + + DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); + if (!nameMap) + return; + + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) + return; + + for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) + databases->add(*it); +} + +unsigned long long DatabaseTracker::getMaxSizeForDatabase(const AbstractDatabase* database) +{ + unsigned long long spaceAvailable = 0; + unsigned long long databaseSize = 0; + QuotaTracker::instance().getDatabaseSizeAndSpaceAvailableToOrigin( + database->securityOrigin()->databaseIdentifier(), + database->stringIdentifier(), &databaseSize, &spaceAvailable); + return databaseSize + spaceAvailable; +} + +void DatabaseTracker::interruptAllDatabasesForContext(const ScriptExecutionContext* context) +{ + Vector<RefPtr<AbstractDatabase> > openDatabases; + { + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + + if (!m_openDatabaseMap) + return; + + DatabaseNameMap* nameMap = m_openDatabaseMap->get(context->securityOrigin()); + if (!nameMap) + return; + + DatabaseNameMap::const_iterator dbNameMapEndIt = nameMap->end(); + for (DatabaseNameMap::const_iterator dbNameMapIt = nameMap->begin(); dbNameMapIt != dbNameMapEndIt; ++dbNameMapIt) { + DatabaseSet* databaseSet = dbNameMapIt->second; + DatabaseSet::const_iterator dbSetEndIt = databaseSet->end(); + for (DatabaseSet::const_iterator dbSetIt = databaseSet->begin(); dbSetIt != dbSetEndIt; ++dbSetIt) { + if ((*dbSetIt)->scriptExecutionContext() == context) + openDatabases.append(*dbSetIt); + } + } + } + + Vector<RefPtr<AbstractDatabase> >::const_iterator openDatabasesEndIt = openDatabases.end(); + for (Vector<RefPtr<AbstractDatabase> >::const_iterator openDatabasesIt = openDatabases.begin(); openDatabasesIt != openDatabasesEndIt; ++openDatabasesIt) + (*openDatabasesIt)->interrupt(); +} + +} + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/chromium/IDBFactoryBackendInterface.cpp b/Source/WebCore/storage/chromium/IDBFactoryBackendInterface.cpp new file mode 100644 index 0000000..c0fb444 --- /dev/null +++ b/Source/WebCore/storage/chromium/IDBFactoryBackendInterface.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBFactoryBackendInterface.h" + +#include "ChromiumBridge.h" + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +PassRefPtr<IDBFactoryBackendInterface> IDBFactoryBackendInterface::create() +{ + return ChromiumBridge::idbFactory(); +} + +IDBFactoryBackendInterface::~IDBFactoryBackendInterface() +{ + ChromiumBridge::idbShutdown(); +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) diff --git a/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp b/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp new file mode 100644 index 0000000..0f10875 --- /dev/null +++ b/Source/WebCore/storage/chromium/IDBKeyPathBackendImpl.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 APPLE AND ITS CONTRIBUTORS "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 APPLE OR ITS 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 "IDBKeyPathBackendImpl.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "ChromiumBridge.h" + +namespace WebCore { + +void IDBKeyPathBackendImpl::createIDBKeysFromSerializedValuesAndKeyPath(const Vector<RefPtr<SerializedScriptValue>, 0>& values, const String& keyPath, Vector<RefPtr<IDBKey>, 0>& keys) +{ + ChromiumBridge::createIDBKeysFromSerializedValuesAndKeyPath(values, keyPath, keys); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/storage/chromium/QuotaTracker.cpp b/Source/WebCore/storage/chromium/QuotaTracker.cpp new file mode 100644 index 0000000..3f48682 --- /dev/null +++ b/Source/WebCore/storage/chromium/QuotaTracker.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 "QuotaTracker.h" + +#if ENABLE(DATABASE) + +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +QuotaTracker& QuotaTracker::instance() +{ + DEFINE_STATIC_LOCAL(QuotaTracker, tracker, ()); + return tracker; +} + +void QuotaTracker::getDatabaseSizeAndSpaceAvailableToOrigin( + const String& originIdentifier, const String& databaseName, + unsigned long long* databaseSize, unsigned long long* spaceAvailable) +{ + MutexLocker lockData(m_dataGuard); + ASSERT(m_databaseSizes.contains(originIdentifier)); + HashMap<String, SizeMap>::const_iterator it = m_databaseSizes.find(originIdentifier); + ASSERT(it->second.contains(databaseName)); + *databaseSize = it->second.get(databaseName); + + ASSERT(m_spaceAvailableToOrigins.contains(originIdentifier)); + *spaceAvailable = m_spaceAvailableToOrigins.get(originIdentifier); +} + +void QuotaTracker::updateDatabaseSizeAndSpaceAvailableToOrigin( + const String& originIdentifier, const String& databaseName, + unsigned long long databaseSize, unsigned long long spaceAvailable) +{ + MutexLocker lockData(m_dataGuard); + m_spaceAvailableToOrigins.set(originIdentifier, spaceAvailable); + HashMap<String, SizeMap>::iterator it = m_databaseSizes.add(originIdentifier, SizeMap()).first; + it->second.set(databaseName, databaseSize); +} + +} + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/chromium/QuotaTracker.h b/Source/WebCore/storage/chromium/QuotaTracker.h new file mode 100644 index 0000000..774475e --- /dev/null +++ b/Source/WebCore/storage/chromium/QuotaTracker.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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. + */ + +#ifndef QuotaTracker_h +#define QuotaTracker_h + +#if ENABLE(DATABASE) + +#include "PlatformString.h" +#include "SecurityOrigin.h" +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +class QuotaTracker { +public: + static QuotaTracker& instance(); + + void getDatabaseSizeAndSpaceAvailableToOrigin( + const String& originIdentifier, const String& databaseName, + unsigned long long* databaseSize, unsigned long long* spaceAvailable); + void updateDatabaseSizeAndSpaceAvailableToOrigin( + const String& originIdentifier, const String& databaseName, + unsigned long long databaseSize, unsigned long long spaceAvailable); + +private: + QuotaTracker() { } + + typedef HashMap<String, unsigned long long> SizeMap; + SizeMap m_spaceAvailableToOrigins; + HashMap<String, SizeMap> m_databaseSizes; + Mutex m_dataGuard; +}; + +} + +#endif // ENABLE(DATABASE) + +#endif // QuotaTracker_h diff --git a/Source/WebCore/storage/chromium/SQLTransactionClientChromium.cpp b/Source/WebCore/storage/chromium/SQLTransactionClientChromium.cpp new file mode 100644 index 0000000..6a10821 --- /dev/null +++ b/Source/WebCore/storage/chromium/SQLTransactionClientChromium.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * 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. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 "SQLTransactionClient.h" + +#if ENABLE(DATABASE) + +#include "AbstractDatabase.h" +#include "DatabaseObserver.h" +#include "ScriptExecutionContext.h" + +namespace WebCore { + +class NotifyDatabaseChangedTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<NotifyDatabaseChangedTask> create(AbstractDatabase *database) + { + return new NotifyDatabaseChangedTask(database); + } + + virtual void performTask(ScriptExecutionContext*) + { + WebCore::DatabaseObserver::databaseModified(m_database.get()); + } + +private: + NotifyDatabaseChangedTask(PassRefPtr<AbstractDatabase> database) + : m_database(database) + { + } + + RefPtr<AbstractDatabase> m_database; +}; + +void SQLTransactionClient::didCommitWriteTransaction(AbstractDatabase* database) +{ + if (!database->scriptExecutionContext()->isContextThread()) { + database->scriptExecutionContext()->postTask(NotifyDatabaseChangedTask::create(database)); + return; + } + + WebCore::DatabaseObserver::databaseModified(database); +} + +void SQLTransactionClient::didExecuteStatement(AbstractDatabase* database) +{ + // This method is called after executing every statement that changes the DB. + // Chromium doesn't need to do anything at that point. +} + +bool SQLTransactionClient::didExceedQuota(AbstractDatabase* database) +{ + // Chromium does not allow users to manually change the quota for an origin (for now, at least). + // Don't do anything. + ASSERT(database->scriptExecutionContext()->isContextThread()); + return false; +} + +} + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/storage/wince/DatabaseThreadWinCE.cpp b/Source/WebCore/storage/wince/DatabaseThreadWinCE.cpp new file mode 100644 index 0000000..d81145d --- /dev/null +++ b/Source/WebCore/storage/wince/DatabaseThreadWinCE.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 library is distributed in the hope that i will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "DatabaseThread.h" + +#include "Database.h" +#include "DatabaseTask.h" + +namespace WebCore { + +DatabaseThread::DatabaseThread() +: m_timer(this, &DatabaseThread::timerFired) +{ +} + +DatabaseThread::~DatabaseThread() +{ +} + +void DatabaseThread::requestTermination() +{ + m_queue.clear(); +} + +bool DatabaseThread::terminationRequested() const +{ + return m_queue.isEmpty(); +} + +void DatabaseThread::timerFired(Timer<DatabaseThread>*) +{ + if (!m_queue.isEmpty()) { + RefPtr<DatabaseTask> task = m_queue.first(); + task->performTask(); + m_queue.removeFirst(); + if (!m_queue.isEmpty()) + m_timer.startOneShot(0); + } +} + +void DatabaseThread::scheduleTask(PassRefPtr<DatabaseTask> task) +{ + m_queue.append(task); + if (!m_timer.isActive()) + m_timer.startOneShot(0); +} + +void DatabaseThread::scheduleImmediateTask(PassRefPtr<DatabaseTask> task) +{ + task->performTask(); +} + +void DatabaseThread::unscheduleDatabaseTasks(Database* database) +{ + Deque<RefPtr<DatabaseTask> > reservedTasks; + for (Deque<RefPtr<DatabaseTask> >::const_iterator i = m_queue.begin(); i != m_queue.end(); ++i) { + if ((*i)->database() != database) + reservedTasks.append(*i); + } + + m_queue.swap(reservedTasks); +} + +void DatabaseThread::recordDatabaseOpen(Database* database) +{ + notImplemented(); +} + +void DatabaseThread::recordDatabaseClosed(Database* database) +{ + notImplemented(); +} + +} // namespace WebCore diff --git a/Source/WebCore/storage/wince/DatabaseThreadWinCE.h b/Source/WebCore/storage/wince/DatabaseThreadWinCE.h new file mode 100644 index 0000000..1921cc1 --- /dev/null +++ b/Source/WebCore/storage/wince/DatabaseThreadWinCE.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 library is distributed in the hope that i will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef DatabaseThreadWinCE_h +#define DatabaseThreadWinCE_h + +#include <wtf/Deque.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class Database; + class DatabaseTask; + + class DatabaseThread: public WTF::RefCounted<DatabaseThread> { + + public: + static PassRefPtr<DatabaseThread> create() { return adoptRef(new DatabaseThread); } + ~DatabaseThread(); + + bool start() { return true; } + void requestTermination(); + bool terminationRequested() const; + + void scheduleTask(PassRefPtr<DatabaseTask>); + void scheduleImmediateTask(PassRefPtr<DatabaseTask>); + void unscheduleDatabaseTasks(Database*); + void recordDatabaseOpen(Database*); + void recordDatabaseClosed(Database*); +#ifndef NDEBUG + ThreadIdentifier getThreadID() const { return currentThread(); } +#endif + + private: + DatabaseThread(); + + void timerFired(Timer<DatabaseThread>*); + + Deque<RefPtr<DatabaseTask> > m_queue; + Timer<DatabaseThread> m_timer; + }; + +} // namespace WebCore + +#endif // DatabaseThreadWinCE_h diff --git a/Source/WebCore/storage/wince/LocalStorageThreadWinCE.cpp b/Source/WebCore/storage/wince/LocalStorageThreadWinCE.cpp new file mode 100644 index 0000000..90c4e89 --- /dev/null +++ b/Source/WebCore/storage/wince/LocalStorageThreadWinCE.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 library is distributed in the hope that i will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "LocalStorageThread.h" + +#include "LocalStorageTask.h" +#include "StorageAreaSync.h" + +namespace WebCore { + +LocalStorageThread::LocalStorageThread() +: m_timer(this, &LocalStorageThread::timerFired) +{ +} + +LocalStorageThread::~LocalStorageThread() +{ +} + +bool LocalStorageThread::start() +{ + return true; +} + +void LocalStorageThread::timerFired(Timer<LocalStorageThread>*) +{ + if (!m_queue.isEmpty()) { + RefPtr<LocalStorageTask> task = m_queue.first(); + task->performTask(); + m_queue.removeFirst(); + if (!m_queue.isEmpty()) + m_timer.startOneShot(0); + } +} + +void LocalStorageThread::scheduleImport(PassRefPtr<StorageAreaSync> area) +{ + m_queue.append(LocalStorageTask::createImport(area)); + if (!m_timer.isActive()) + m_timer.startOneShot(0); +} + +void LocalStorageThread::scheduleSync(PassRefPtr<StorageAreaSync> area) +{ + m_queue.append(LocalStorageTask::createSync(area)); + if (!m_timer.isActive()) + m_timer.startOneShot(0); +} + +void LocalStorageThread::terminate() +{ + m_queue.clear(); + m_timer.stop(); +} + +void LocalStorageThread::performTerminate() +{ + m_queue.clear(); + m_timer.stop(); +} + +} // namespace WebCore diff --git a/Source/WebCore/storage/wince/LocalStorageThreadWinCE.h b/Source/WebCore/storage/wince/LocalStorageThreadWinCE.h new file mode 100644 index 0000000..06e7d45 --- /dev/null +++ b/Source/WebCore/storage/wince/LocalStorageThreadWinCE.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 library is distributed in the hope that i will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef LocalStorageThreadWinCE_h +#define LocalStorageThreadWinCE_h + +#include <wtf/Deque.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + + class StorageAreaSync; + class LocalStorageTask; + + class LocalStorageThread : public RefCounted<LocalStorageThread> { + public: + static PassRefPtr<LocalStorageThread> create() { return adoptRef(new LocalStorageThread); } + + ~LocalStorageThread(); + bool start(); + void scheduleImport(PassRefPtr<StorageAreaSync>); + void scheduleSync(PassRefPtr<StorageAreaSync>); + void terminate(); + void performTerminate(); + + private: + LocalStorageThread(); + + void timerFired(Timer<LocalStorageThread>*); + + Deque<RefPtr<LocalStorageTask> > m_queue; + Timer<LocalStorageThread> m_timer; + }; + +} // namespace WebCore + +#endif // LocalStorageThreadWinCE_h |