summaryrefslogtreecommitdiffstats
path: root/WebCore/storage
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/storage')
-rw-r--r--WebCore/storage/AbstractDatabase.cpp413
-rw-r--r--WebCore/storage/AbstractDatabase.h74
-rw-r--r--WebCore/storage/Database.cpp524
-rw-r--r--WebCore/storage/Database.h88
-rw-r--r--WebCore/storage/DatabaseAuthorizer.cpp14
-rw-r--r--WebCore/storage/DatabaseAuthorizer.h13
-rw-r--r--WebCore/storage/DatabaseSync.cpp99
-rw-r--r--WebCore/storage/DatabaseSync.h32
-rw-r--r--WebCore/storage/DatabaseTask.cpp35
-rw-r--r--WebCore/storage/DatabaseTask.h27
-rw-r--r--WebCore/storage/DatabaseThread.cpp4
-rw-r--r--WebCore/storage/DatabaseTracker.cpp12
-rw-r--r--WebCore/storage/DatabaseTracker.h3
-rw-r--r--WebCore/storage/IDBCallbacks.h2
-rw-r--r--WebCore/storage/IDBKey.cpp62
-rw-r--r--WebCore/storage/IDBKey.h90
-rw-r--r--WebCore/storage/IDBKey.idl34
-rw-r--r--WebCore/storage/IDBKeyTree.h157
-rw-r--r--WebCore/storage/IDBRequest.cpp5
-rw-r--r--WebCore/storage/IDBRequest.h1
-rw-r--r--WebCore/storage/SQLTransaction.cpp4
21 files changed, 1030 insertions, 663 deletions
diff --git a/WebCore/storage/AbstractDatabase.cpp b/WebCore/storage/AbstractDatabase.cpp
index dc3e8e2..ca9a1b5 100644
--- a/WebCore/storage/AbstractDatabase.cpp
+++ b/WebCore/storage/AbstractDatabase.cpp
@@ -30,9 +30,128 @@
#include "AbstractDatabase.h"
#if ENABLE(DATABASE)
+#include "DatabaseAuthorizer.h"
+#include "DatabaseTracker.h"
+#include "ExceptionCode.h"
+#include "Logging.h"
+#include "SQLiteStatement.h"
+#include "ScriptExecutionContext.h"
+#include "SecurityOrigin.h"
+#include "StringHash.h"
+#include <wtf/HashMap.h>
+#include <wtf/HashSet.h>
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefPtr.h>
+#include <wtf/StdLibExtras.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()
@@ -45,10 +164,304 @@ 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();
+}
+
+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();
+}
+
} // namespace WebCore
#endif // ENABLE(DATABASE)
diff --git a/WebCore/storage/AbstractDatabase.h b/WebCore/storage/AbstractDatabase.h
index 5e4e0fa..d38a819 100644
--- a/WebCore/storage/AbstractDatabase.h
+++ b/WebCore/storage/AbstractDatabase.h
@@ -32,13 +32,21 @@
#if ENABLE(DATABASE)
#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;
+typedef int ExceptionCode;
+
class AbstractDatabase : public ThreadSafeShared<AbstractDatabase> {
public:
static bool isAvailable();
@@ -46,15 +54,69 @@ public:
virtual ~AbstractDatabase();
- virtual ScriptExecutionContext* scriptExecutionContext() const = 0;
- virtual SecurityOrigin* securityOrigin() const = 0;
- virtual String stringIdentifier() const = 0;
- virtual String displayName() const = 0;
- virtual unsigned long estimatedSize() const = 0;
- virtual String fileName() const = 0;
+ 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;
+
+ // 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();
+ 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;
+
+ SQLiteDatabase m_sqliteDatabase;
+
+#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;
+
+ RefPtr<DatabaseAuthorizer> m_databaseAuthorizer;
};
} // namespace WebCore
diff --git a/WebCore/storage/Database.cpp b/WebCore/storage/Database.cpp
index ad6b92c..30087ee 100644
--- a/WebCore/storage/Database.cpp
+++ b/WebCore/storage/Database.cpp
@@ -31,7 +31,6 @@
#if ENABLE(DATABASE)
#include "ChangeVersionWrapper.h"
-#include "DatabaseAuthorizer.h"
#include "DatabaseCallback.h"
#include "DatabaseTask.h"
#include "DatabaseThread.h"
@@ -50,7 +49,6 @@
#include "ScriptController.h"
#include "ScriptExecutionContext.h"
#include "SecurityOrigin.h"
-#include "StringHash.h"
#include "VoidCallback.h"
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>
@@ -64,81 +62,27 @@
namespace WebCore {
-// If we sleep for more the 30 seconds while blocked on SQLITE_BUSY, give up.
-static const int maxSqliteBusyWaitTime = 30000;
-
-const String& Database::databaseInfoTableName()
-{
- DEFINE_STATIC_LOCAL(String, name, ("__WebKitDatabaseInfoTable__"));
- return name;
-}
-
-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<Database*>*> GuidDatabaseMap;
-static GuidDatabaseMap& guidToDatabaseMap()
-{
- DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ());
- return map;
-}
-
-static const String& databaseVersionKey()
-{
- DEFINE_STATIC_LOCAL(String, key, ("WebKitDatabaseVersionKey"));
- return key;
-}
-
-static int guidForOriginAndName(const String& origin, const String& name);
-
class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task {
public:
- static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database)
+ static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback)
{
- return new DatabaseCreationCallbackTask(database);
+ return new DatabaseCreationCallbackTask(database, creationCallback);
}
- virtual void performTask(ScriptExecutionContext*)
+ virtual void performTask(ScriptExecutionContext* context)
{
- m_database->performCreationCallback();
+ m_creationCallback->handleEvent(context, m_database.get());
}
private:
- DatabaseCreationCallbackTask(PassRefPtr<Database> database)
+ 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,
@@ -147,16 +91,14 @@ PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, con
ExceptionCode& e)
{
if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) {
- // FIXME: There should be an exception raised here in addition to returning a null Database object. The question has been raised with the WHATWG.
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, creationCallback));
+ RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize));
- if (!database->openAndVerifyVersion(e)) {
+ 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());
- context->removeOpenDatabase(database.get());
DatabaseTracker::tracker().removeOpenDatabase(database.get());
return 0;
}
@@ -176,57 +118,25 @@ PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, con
// 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() && database->m_creationCallback.get()) {
+ 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));
+ 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, PassRefPtr<DatabaseCallback> creationCallback)
- : m_transactionInProgress(false)
+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_scriptExecutionContext(context)
- , m_name(name.crossThreadString())
- , m_guid(0)
- , m_expectedVersion(expectedVersion.crossThreadString())
- , m_displayName(displayName.crossThreadString())
- , m_estimatedSize(estimatedSize)
, m_deleted(false)
- , m_stopped(false)
- , m_opened(false)
- , m_new(false)
- , m_creationCallback(creationCallback)
{
- ASSERT(m_scriptExecutionContext.get());
- m_contextThreadSecurityOrigin = m_scriptExecutionContext->securityOrigin();
m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->threadsafeCopy();
- if (m_name.isNull())
- m_name = "";
ScriptController::initializeThreading();
-
- m_guid = guidForOriginAndName(securityOrigin()->toString(), name);
-
- {
- MutexLocker locker(guidMutex());
-
- HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
- if (!hashSet) {
- hashSet = new HashSet<Database*>;
- guidToDatabaseMap().set(m_guid, hashSet);
- }
-
- hashSet->add(this);
- }
-
ASSERT(m_scriptExecutionContext->databaseThread());
- m_filename = DatabaseTracker::tracker().fullPathForDatabase(securityOrigin(), m_name);
-
- DatabaseTracker::tracker().addOpenDatabase(this);
- context->addOpenDatabase(this);
}
class DerefContextTask : public ScriptExecutionContext::Task {
@@ -253,15 +163,21 @@ Database::~Database()
}
}
-bool Database::openAndVerifyVersion(ExceptionCode& e)
+String Database::version() const
+{
+ if (m_deleted)
+ return String();
+ return AbstractDatabase::version();
+}
+
+bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, ExceptionCode& e)
{
if (!m_scriptExecutionContext->databaseThread())
return false;
- m_databaseAuthorizer = DatabaseAuthorizer::create();
bool success = false;
DatabaseTaskSynchronizer synchronizer;
- OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, &synchronizer, e, success);
+ OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, e, success);
m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
synchronizer.waitForTaskCompletion();
@@ -269,93 +185,6 @@ bool Database::openAndVerifyVersion(ExceptionCode& e)
return success;
}
-
-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;
- } else if (result == SQLResultDone) {
- resultString = String();
- return true;
- } else {
- LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
- return false;
- }
-}
-
-bool Database::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;
-}
-
-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;
-}
-
-bool Database::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 Database::versionMatchesExpected() const
-{
- if (!m_expectedVersion.isEmpty()) {
- MutexLocker locker(guidMutex());
- return m_expectedVersion == guidToVersionMap().get(m_guid);
- }
-
- return true;
-}
-
void Database::markAsDeletedAndClose()
{
if (m_deleted || !m_scriptExecutionContext->databaseThread())
@@ -369,100 +198,38 @@ void Database::markAsDeletedAndClose()
return;
}
- m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this);
-
DatabaseTaskSynchronizer synchronizer;
- OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, DoNotRemoveDatabaseFromContext, &synchronizer);
+ OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer);
m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
synchronizer.waitForTaskCompletion();
-
- // DatabaseCloseTask tells Database::close not to do these two removals so that we can do them here synchronously.
- m_scriptExecutionContext->removeOpenDatabase(this);
- DatabaseTracker::tracker().removeOpenDatabase(this);
}
-class ContextRemoveOpenDatabaseTask : public ScriptExecutionContext::Task {
-public:
- static PassOwnPtr<ContextRemoveOpenDatabaseTask> create(PassRefPtr<Database> database)
- {
- return new ContextRemoveOpenDatabaseTask(database);
- }
-
- virtual void performTask(ScriptExecutionContext* context)
- {
- context->removeOpenDatabase(m_database.get());
- DatabaseTracker::tracker().removeOpenDatabase(m_database.get());
- }
-
- virtual bool isCleanupTask() const { return true; }
-
-private:
- ContextRemoveOpenDatabaseTask(PassRefPtr<Database> database)
- : m_database(database)
- {
- }
-
- RefPtr<Database> m_database;
-};
-
-void Database::close(ClosePolicy policy)
+void Database::close()
{
- RefPtr<Database> protect = this;
-
- if (!m_opened)
- return;
-
ASSERT(m_scriptExecutionContext->databaseThread());
ASSERT(currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID());
- m_sqliteDatabase.close();
- // Must ref() before calling databaseThread()->recordDatabaseClosed().
- m_scriptExecutionContext->databaseThread()->recordDatabaseClosed(this);
- m_opened = false;
{
- MutexLocker locker(guidMutex());
-
- HashSet<Database*>* 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);
- }
+ 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);
- if (policy == RemoveDatabaseFromContext)
- m_scriptExecutionContext->postTask(ContextRemoveOpenDatabaseTask::create(this));
+ DatabaseTracker::tracker().removeOpenDatabase(this);
}
void Database::closeImmediately()
{
DatabaseThread* databaseThread = scriptExecutionContext()->databaseThread();
- if (databaseThread && !databaseThread->terminationRequested()) {
- stop();
- databaseThread->scheduleTask(DatabaseCloseTask::create(this, Database::RemoveDatabaseFromContext, 0));
- }
-}
-
-void Database::stop()
-{
- // FIXME: The net effect of the following code is to remove all pending transactions and statements, but allow the current statement
- // to run to completion. In the future we can use the sqlite3_progress_handler or sqlite3_interrupt interfaces to cancel the current
- // statement in response to close(), as well.
-
- // This method is meant to be used as an analog to cancelling a loader, and is used when a document is shut down as the result of
- // a page load or closing the page
- m_stopped = true;
-
- {
- MutexLocker locker(m_transactionInProgressMutex);
- m_isTransactionQueueEnabled = false;
- m_transactionInProgress = false;
- }
+ if (databaseThread && !databaseThread->terminationRequested() && opened())
+ databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0));
}
unsigned long long Database::maximumSize() const
@@ -470,179 +237,16 @@ unsigned long long Database::maximumSize() const
return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
}
-void Database::disableAuthorizer()
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->disable();
-}
-
-void Database::enableAuthorizer()
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->enable();
-}
-
-void Database::setAuthorizerReadOnly()
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->setReadOnly();
-}
-
-bool Database::lastActionChangedDatabase()
-{
- ASSERT(m_databaseAuthorizer);
- return m_databaseAuthorizer->lastActionChangedDatabase();
-}
-
-bool Database::lastActionWasInsert()
-{
- ASSERT(m_databaseAuthorizer);
- return m_databaseAuthorizer->lastActionWasInsert();
-}
-
-void Database::resetDeletes()
-{
- ASSERT(m_databaseAuthorizer);
- m_databaseAuthorizer->resetDeletes();
-}
-
-bool Database::hadDeletes()
-{
- ASSERT(m_databaseAuthorizer);
- return m_databaseAuthorizer->hadDeletes();
-}
-
-static int guidForOriginAndName(const String& origin, const String& name)
-{
- String stringID;
- if (origin.endsWith("/"))
- stringID = origin + name;
- else
- 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;
-}
-
-void Database::resetAuthorizer()
+bool Database::performOpenAndVerify(bool setVersionInNewDatabase, ExceptionCode& e)
{
- if (m_databaseAuthorizer)
- m_databaseAuthorizer->reset();
-}
-
-void Database::performPolicyChecks()
-{
- // FIXME: Code similar to the following will need to be run to enforce the per-origin size limit the spec mandates.
- // Additionally, we might need a way to pause the database thread while the UA prompts the user for permission to
- // increase the size limit
-
- /*
- if (m_databaseAuthorizer->lastActionIncreasedSize())
- DatabaseTracker::scheduleFileSizeCheckOnMainThread(this);
- */
-
- notImplemented();
-}
+ if (AbstractDatabase::performOpenAndVerify(setVersionInNewDatabase, e)) {
+ if (m_scriptExecutionContext->databaseThread())
+ m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this);
-bool Database::performOpenAndVerify(ExceptionCode& e)
-{
- if (!m_sqliteDatabase.open(m_filename, true)) {
- LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data());
- e = 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());
- e = 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());
- e = 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 || !m_creationCallback) {
- 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());
- e = 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 || !m_creationCallback) && 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());
- e = INVALID_STATE_ERR;
- // Close the handle to the database file.
- m_sqliteDatabase.close();
- return false;
+ return true;
}
- // All checks passed and we still have a handle to this database file.
- // Make sure DatabaseThread closes it when DatabaseThread goes away.
- m_opened = true;
- if (m_scriptExecutionContext->databaseThread())
- m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this);
-
- return true;
+ return false;
}
void Database::changeVersion(const String& oldVersion, const String& newVersion,
@@ -757,11 +361,6 @@ Vector<String> Database::performGetTableNames()
return tableNames;
}
-void Database::performCreationCallback()
-{
- m_creationCallback->handleEvent(m_scriptExecutionContext.get(), this);
-}
-
SQLTransactionClient* Database::transactionClient() const
{
return m_scriptExecutionContext->databaseThread()->transactionClient();
@@ -772,14 +371,6 @@ SQLTransactionCoordinator* Database::transactionCoordinator() const
return m_scriptExecutionContext->databaseThread()->transactionCoordinator();
}
-String Database::version() const
-{
- if (m_deleted)
- return String();
- MutexLocker locker(guidMutex());
- return guidToVersionMap().get(m_guid).threadsafeCopy();
-}
-
Vector<String> Database::tableNames()
{
// FIXME: Not using threadsafeCopy on these strings looks ok since threads take strict turns
@@ -797,14 +388,6 @@ Vector<String> Database::tableNames()
return result;
}
-void Database::setExpectedVersion(const String& version)
-{
- m_expectedVersion = version.threadsafeCopy();
- // Update the in memory database version map.
- MutexLocker locker(guidMutex());
- updateGuidVersionMap(m_guid, version);
-}
-
SecurityOrigin* Database::securityOrigin() const
{
if (m_scriptExecutionContext->isContextThread())
@@ -814,29 +397,6 @@ SecurityOrigin* Database::securityOrigin() const
return 0;
}
-String Database::stringIdentifier() const
-{
- // Return a deep copy for ref counting thread safety
- return m_name.threadsafeCopy();
-}
-
-String Database::displayName() const
-{
- // Return a deep copy for ref counting thread safety
- return m_displayName.threadsafeCopy();
-}
-
-unsigned long Database::estimatedSize() const
-{
- return m_estimatedSize;
-}
-
-String Database::fileName() const
-{
- // Return a deep copy for ref counting thread safety
- return m_filename.threadsafeCopy();
-}
-
void Database::incrementalVacuumIfNeeded()
{
int64_t freeSpaceSize = m_sqliteDatabase.freeSpaceSize();
diff --git a/WebCore/storage/Database.h b/WebCore/storage/Database.h
index 495f98f..22ef55e 100644
--- a/WebCore/storage/Database.h
+++ b/WebCore/storage/Database.h
@@ -33,18 +33,13 @@
#include "AbstractDatabase.h"
#include "PlatformString.h"
#include "SQLiteDatabase.h"
-#ifndef NDEBUG
-#include "SecurityOrigin.h"
-#endif
#include <wtf/Deque.h>
#include <wtf/Forward.h>
namespace WebCore {
-class DatabaseAuthorizer;
class DatabaseCallback;
-class DatabaseThread;
class ScriptExecutionContext;
class SecurityOrigin;
class SQLTransaction;
@@ -54,8 +49,6 @@ class SQLTransactionCoordinator;
class SQLTransactionErrorCallback;
class VoidCallback;
-typedef int ExceptionCode;
-
class Database : public AbstractDatabase {
public:
virtual ~Database();
@@ -63,113 +56,62 @@ public:
// 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&);
- String version() const;
+ 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, bool readOnly);
// Internal engine support
- static const String& databaseInfoTableName();
-
- void disableAuthorizer();
- void enableAuthorizer();
- void setAuthorizerReadOnly();
- bool lastActionChangedDatabase();
- bool lastActionWasInsert();
- void resetDeletes();
- bool hadDeletes();
-
Vector<String> tableNames();
- virtual ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext.get(); }
virtual SecurityOrigin* securityOrigin() const;
SQLiteDatabase& sqliteDatabase() { return m_sqliteDatabase; }
- virtual String stringIdentifier() const;
- virtual String displayName() const;
- virtual unsigned long estimatedSize() const;
- virtual String fileName() const;
-
- bool getVersionFromDatabase(String&);
- bool setVersionInDatabase(const String&);
- void setExpectedVersion(const String&);
- bool versionMatchesExpected() const;
virtual void markAsDeletedAndClose();
bool deleted() const { return m_deleted; }
- enum ClosePolicy { DoNotRemoveDatabaseFromContext, RemoveDatabaseFromContext };
- void close(ClosePolicy);
+ void close();
virtual void closeImmediately();
- bool opened() const { return m_opened; }
-
- void stop();
- bool stopped() const { return m_stopped; }
-
- bool isNew() const { return m_new; }
unsigned long long databaseSize() const;
unsigned long long maximumSize() const;
- // Called from DatabaseThread, must be prepared to work on the background thread
- void resetAuthorizer();
- void performPolicyChecks();
-
- bool performOpenAndVerify(ExceptionCode&);
-
- void inProgressTransactionCompleted();
void scheduleTransactionCallback(SQLTransaction*);
void scheduleTransactionStep(SQLTransaction*, bool immediately = false);
- Vector<String> performGetTableNames();
- void performCreationCallback();
-
SQLTransactionClient* transactionClient() const;
SQLTransactionCoordinator* transactionCoordinator() const;
void incrementalVacuumIfNeeded();
private:
+ class DatabaseOpenTask;
+ class DatabaseCloseTask;
+ class DatabaseTransactionTask;
+ class DatabaseTableNamesTask;
+
Database(ScriptExecutionContext*, const String& name, const String& expectedVersion,
- const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>);
+ const String& displayName, unsigned long estimatedSize);
- bool openAndVerifyVersion(ExceptionCode&);
+ 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;
- static void deliverPendingCallback(void*);
-
- RefPtr<ScriptExecutionContext> m_scriptExecutionContext;
- RefPtr<SecurityOrigin> m_contextThreadSecurityOrigin;
RefPtr<SecurityOrigin> m_databaseThreadSecurityOrigin;
- String m_name;
- int m_guid;
- String m_expectedVersion;
- String m_displayName;
- unsigned long m_estimatedSize;
- String m_filename;
bool m_deleted;
-
- bool m_stopped;
-
- bool m_opened;
-
- bool m_new;
-
- SQLiteDatabase m_sqliteDatabase;
- RefPtr<DatabaseAuthorizer> m_databaseAuthorizer;
-
- RefPtr<DatabaseCallback> m_creationCallback;
-
-#ifndef NDEBUG
- String databaseDebugName() const { return m_contextThreadSecurityOrigin->toString() + "::" + m_name; }
-#endif
};
} // namespace WebCore
diff --git a/WebCore/storage/DatabaseAuthorizer.cpp b/WebCore/storage/DatabaseAuthorizer.cpp
index 1ea99d9..d155d6c 100644
--- a/WebCore/storage/DatabaseAuthorizer.cpp
+++ b/WebCore/storage/DatabaseAuthorizer.cpp
@@ -30,13 +30,19 @@
#include "DatabaseAuthorizer.h"
#if ENABLE(DATABASE)
-#include "Database.h"
#include "PlatformString.h"
+#include <wtf/PassRefPtr.h>
namespace WebCore {
-DatabaseAuthorizer::DatabaseAuthorizer()
+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();
@@ -388,7 +394,7 @@ void DatabaseAuthorizer::setReadOnly()
m_readOnly = true;
}
-int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName)
+int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) const
{
if (!m_securityEnabled)
return SQLAuthAllow;
@@ -399,7 +405,7 @@ int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName)
// equalIgnoringCase(tableName, "sqlite_sequence") || equalIgnoringCase(tableName, Database::databaseInfoTableName()))
// return SQLAuthDeny;
- if (equalIgnoringCase(tableName, Database::databaseInfoTableName()))
+ if (equalIgnoringCase(tableName, m_databaseInfoTableName))
return SQLAuthDeny;
return SQLAuthAllow;
diff --git a/WebCore/storage/DatabaseAuthorizer.h b/WebCore/storage/DatabaseAuthorizer.h
index 7da0143..af6e016 100644
--- a/WebCore/storage/DatabaseAuthorizer.h
+++ b/WebCore/storage/DatabaseAuthorizer.h
@@ -28,10 +28,11 @@
#ifndef DatabaseAuthorizer_h
#define DatabaseAuthorizer_h
+#include "PlatformString.h"
#include "StringHash.h"
+#include <wtf/Forward.h>
#include <wtf/HashSet.h>
-#include <wtf/PassRefPtr.h>
-#include <wtf/Threading.h>
+#include <wtf/ThreadSafeShared.h>
namespace WebCore {
@@ -43,7 +44,7 @@ extern const int SQLAuthDeny;
class DatabaseAuthorizer : public ThreadSafeShared<DatabaseAuthorizer> {
public:
- static PassRefPtr<DatabaseAuthorizer> create() { return adoptRef(new DatabaseAuthorizer); }
+ static PassRefPtr<DatabaseAuthorizer> create(const String& databaseInfoTableName);
int createTable(const String& tableName);
int createTempTable(const String& tableName);
@@ -97,9 +98,9 @@ public:
bool hadDeletes() const { return m_hadDeletes; }
private:
- DatabaseAuthorizer();
+ DatabaseAuthorizer(const String& databaseInfoTableName);
void addWhitelistedFunctions();
- int denyBasedOnTableName(const String&);
+ int denyBasedOnTableName(const String&) const;
int updateDeletesBasedOnTableName(const String&);
bool m_securityEnabled : 1;
@@ -108,6 +109,8 @@ private:
bool m_readOnly : 1;
bool m_hadDeletes : 1;
+ const String m_databaseInfoTableName;
+
HashSet<String, CaseFoldingHash> m_whitelistedFunctions;
};
diff --git a/WebCore/storage/DatabaseSync.cpp b/WebCore/storage/DatabaseSync.cpp
index 0d3bed5..8de9680 100644
--- a/WebCore/storage/DatabaseSync.cpp
+++ b/WebCore/storage/DatabaseSync.cpp
@@ -31,52 +31,60 @@
#if ENABLE(DATABASE)
#include "DatabaseCallback.h"
+#include "DatabaseTracker.h"
#include "ExceptionCode.h"
+#include "Logging.h"
#include "SQLTransactionSyncCallback.h"
#include "ScriptExecutionContext.h"
+#include "SecurityOrigin.h"
#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
-#include <wtf/StdLibExtras.h>
namespace WebCore {
-const String& DatabaseSync::databaseInfoTableName()
+PassRefPtr<DatabaseSync> DatabaseSync::openDatabaseSync(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName,
+ unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback, ExceptionCode& ec)
{
- DEFINE_STATIC_LOCAL(String, name, ("__WebKitDatabaseInfoTable__"));
- return name;
-}
+ ASSERT(context->isContextThread());
-PassRefPtr<DatabaseSync> DatabaseSync::openDatabaseSync(ScriptExecutionContext*, const String&, const String&, const String&,
- unsigned long, PassRefPtr<DatabaseCallback>, ExceptionCode& ec)
-{
- // FIXME: uncomment the assert once we use the ScriptExecutionContext* parameter
- //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);
- ec = SECURITY_ERR;
- return 0;
+ if (database->isNew() && creationCallback.get()) {
+ database->m_expectedVersion = "";
+ LOG(StorageAPI, "Invoking the creation callback for database %p\n", database.get());
+ creationCallback->handleEvent(context, database.get());
+ }
+
+ return database;
}
DatabaseSync::DatabaseSync(ScriptExecutionContext* context, const String& name, const String& expectedVersion,
- const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback)
- : m_scriptExecutionContext(context)
- , m_name(name.crossThreadString())
- , m_expectedVersion(expectedVersion.crossThreadString())
- , m_displayName(displayName.crossThreadString())
- , m_estimatedSize(estimatedSize)
- , m_creationCallback(creationCallback)
+ const String& displayName, unsigned long estimatedSize)
+ : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize)
{
- ASSERT(context->isContextThread());
}
DatabaseSync::~DatabaseSync()
{
ASSERT(m_scriptExecutionContext->isContextThread());
-}
-String DatabaseSync::version() const
-{
- ASSERT(m_scriptExecutionContext->isContextThread());
- return String();
+ if (opened()) {
+ DatabaseTracker::tracker().removeOpenDatabase(this);
+ closeDatabase();
+ }
}
void DatabaseSync::changeVersion(const String&, const String&, PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&)
@@ -89,10 +97,45 @@ void DatabaseSync::transaction(PassRefPtr<SQLTransactionSyncCallback>, bool, Exc
ASSERT(m_scriptExecutionContext->isContextThread());
}
-ScriptExecutionContext* DatabaseSync::scriptExecutionContext() const
+void DatabaseSync::markAsDeletedAndClose()
{
- ASSERT(m_scriptExecutionContext->isContextThread());
- return m_scriptExecutionContext.get();
+ // 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 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
diff --git a/WebCore/storage/DatabaseSync.h b/WebCore/storage/DatabaseSync.h
index 321b620..96b4f7d 100644
--- a/WebCore/storage/DatabaseSync.h
+++ b/WebCore/storage/DatabaseSync.h
@@ -30,48 +30,36 @@
#define DatabaseSync_h
#if ENABLE(DATABASE)
+#include "AbstractDatabase.h"
#include "PlatformString.h"
#include <wtf/Forward.h>
+#ifndef NDEBUG
+#include "SecurityOrigin.h"
+#endif
namespace WebCore {
class DatabaseCallback;
class SQLTransactionSyncCallback;
class ScriptExecutionContext;
-
-typedef int ExceptionCode;
+class SecurityOrigin;
// Instances of this class should be created and used only on the worker's context thread.
-class DatabaseSync : public RefCounted<DatabaseSync> {
+class DatabaseSync : public AbstractDatabase {
public:
- ~DatabaseSync();
+ virtual ~DatabaseSync();
- // Direct support for the DOM API
static PassRefPtr<DatabaseSync> openDatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion,
const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>, ExceptionCode&);
- String version() const;
void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionSyncCallback>, ExceptionCode&);
void transaction(PassRefPtr<SQLTransactionSyncCallback>, bool readOnly, ExceptionCode&);
- // Internal engine support
- ScriptExecutionContext* scriptExecutionContext() const;
-
- static const String& databaseInfoTableName();
+ virtual void markAsDeletedAndClose();
+ virtual void closeImmediately();
private:
DatabaseSync(ScriptExecutionContext*, const String& name, const String& expectedVersion,
- const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback>);
-
- RefPtr<ScriptExecutionContext> m_scriptExecutionContext;
- String m_name;
- String m_expectedVersion;
- String m_displayName;
- unsigned long m_estimatedSize;
- RefPtr<DatabaseCallback> m_creationCallback;
-
-#ifndef NDEBUG
- String databaseDebugName() const { return String(); }
-#endif
+ const String& displayName, unsigned long estimatedSize);
};
} // namespace WebCore
diff --git a/WebCore/storage/DatabaseTask.cpp b/WebCore/storage/DatabaseTask.cpp
index 8d5f0a8..3526d9d 100644
--- a/WebCore/storage/DatabaseTask.cpp
+++ b/WebCore/storage/DatabaseTask.cpp
@@ -78,7 +78,6 @@ void DatabaseTask::performTask()
m_database->resetAuthorizer();
doPerformTask();
- m_database->performPolicyChecks();
if (m_synchronizer)
m_synchronizer->taskCompleted();
@@ -87,21 +86,22 @@ void DatabaseTask::performTask()
// *** DatabaseOpenTask ***
// Opens the database file and verifies the version matches the expected version.
-DatabaseOpenTask::DatabaseOpenTask(Database* database, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success)
+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 DatabaseOpenTask::doPerformTask()
+void Database::DatabaseOpenTask::doPerformTask()
{
- m_success = database()->performOpenAndVerify(m_code);
+ m_success = database()->performOpenAndVerify(m_setVersionInNewDatabase, m_code);
}
#ifndef NDEBUG
-const char* DatabaseOpenTask::debugTaskName() const
+const char* Database::DatabaseOpenTask::debugTaskName() const
{
return "DatabaseOpenTask";
}
@@ -110,19 +110,18 @@ const char* DatabaseOpenTask::debugTaskName() const
// *** DatabaseCloseTask ***
// Closes the database.
-DatabaseCloseTask::DatabaseCloseTask(Database* database, Database::ClosePolicy closePolicy, DatabaseTaskSynchronizer* synchronizer)
+Database::DatabaseCloseTask::DatabaseCloseTask(Database* database, DatabaseTaskSynchronizer* synchronizer)
: DatabaseTask(database, synchronizer)
- , m_closePolicy(closePolicy)
{
}
-void DatabaseCloseTask::doPerformTask()
+void Database::DatabaseCloseTask::doPerformTask()
{
- database()->close(m_closePolicy);
+ database()->close();
}
#ifndef NDEBUG
-const char* DatabaseCloseTask::debugTaskName() const
+const char* Database::DatabaseCloseTask::debugTaskName() const
{
return "DatabaseCloseTask";
}
@@ -131,24 +130,20 @@ const char* DatabaseCloseTask::debugTaskName() const
// *** DatabaseTransactionTask ***
// Starts a transaction that will report its results via a callback.
-DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransaction> transaction)
+Database::DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransaction> transaction)
: DatabaseTask(transaction->database(), 0)
, m_transaction(transaction)
{
}
-DatabaseTransactionTask::~DatabaseTransactionTask()
-{
-}
-
-void DatabaseTransactionTask::doPerformTask()
+void Database::DatabaseTransactionTask::doPerformTask()
{
if (m_transaction->performNextStep())
m_transaction->database()->inProgressTransactionCompleted();
}
#ifndef NDEBUG
-const char* DatabaseTransactionTask::debugTaskName() const
+const char* Database::DatabaseTransactionTask::debugTaskName() const
{
return "DatabaseTransactionTask";
}
@@ -157,20 +152,20 @@ const char* DatabaseTransactionTask::debugTaskName() const
// *** DatabaseTableNamesTask ***
// Retrieves a list of all tables in the database - for WebInspector support.
-DatabaseTableNamesTask::DatabaseTableNamesTask(Database* database, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names)
+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 DatabaseTableNamesTask::doPerformTask()
+void Database::DatabaseTableNamesTask::doPerformTask()
{
m_tableNames = database()->performGetTableNames();
}
#ifndef NDEBUG
-const char* DatabaseTableNamesTask::debugTaskName() const
+const char* Database::DatabaseTableNamesTask::debugTaskName() const
{
return "DatabaseTableNamesTask";
}
diff --git a/WebCore/storage/DatabaseTask.h b/WebCore/storage/DatabaseTask.h
index 1a820a5..9673a26 100644
--- a/WebCore/storage/DatabaseTask.h
+++ b/WebCore/storage/DatabaseTask.h
@@ -60,7 +60,6 @@ private:
};
class DatabaseTask : public Noncopyable {
- friend class Database;
public:
virtual ~DatabaseTask();
@@ -83,44 +82,43 @@ private:
#endif
};
-class DatabaseOpenTask : public DatabaseTask {
+class Database::DatabaseOpenTask : public DatabaseTask {
public:
- static PassOwnPtr<DatabaseOpenTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success)
+ static PassOwnPtr<DatabaseOpenTask> create(Database* db, bool setVersionInNewDatabase, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success)
{
- return new DatabaseOpenTask(db, synchronizer, code, success);
+ return new DatabaseOpenTask(db, setVersionInNewDatabase, synchronizer, code, success);
}
private:
- DatabaseOpenTask(Database*, DatabaseTaskSynchronizer*, ExceptionCode&, bool& success);
+ 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 DatabaseCloseTask : public DatabaseTask {
+class Database::DatabaseCloseTask : public DatabaseTask {
public:
- static PassOwnPtr<DatabaseCloseTask> create(Database* db, Database::ClosePolicy closePolicy, DatabaseTaskSynchronizer* synchronizer)
- {
- return new DatabaseCloseTask(db, closePolicy, synchronizer);
+ static PassOwnPtr<DatabaseCloseTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer)
+ {
+ return new DatabaseCloseTask(db, synchronizer);
}
private:
- DatabaseCloseTask(Database*, Database::ClosePolicy, DatabaseTaskSynchronizer*);
+ DatabaseCloseTask(Database*, DatabaseTaskSynchronizer*);
virtual void doPerformTask();
#ifndef NDEBUG
virtual const char* debugTaskName() const;
#endif
-
- Database::ClosePolicy m_closePolicy;
};
-class DatabaseTransactionTask : public DatabaseTask {
+class Database::DatabaseTransactionTask : public DatabaseTask {
public:
// Transaction task is never synchronous, so no 'synchronizer' parameter.
static PassOwnPtr<DatabaseTransactionTask> create(PassRefPtr<SQLTransaction> transaction)
@@ -130,7 +128,6 @@ public:
SQLTransaction* transaction() const { return m_transaction.get(); }
- virtual ~DatabaseTransactionTask();
private:
DatabaseTransactionTask(PassRefPtr<SQLTransaction>);
@@ -142,7 +139,7 @@ private:
RefPtr<SQLTransaction> m_transaction;
};
-class DatabaseTableNamesTask : public DatabaseTask {
+class Database::DatabaseTableNamesTask : public DatabaseTask {
public:
static PassOwnPtr<DatabaseTableNamesTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names)
{
diff --git a/WebCore/storage/DatabaseThread.cpp b/WebCore/storage/DatabaseThread.cpp
index ec6241c..ae2a6c0 100644
--- a/WebCore/storage/DatabaseThread.cpp
+++ b/WebCore/storage/DatabaseThread.cpp
@@ -113,14 +113,14 @@ void* DatabaseThread::databaseThread()
openSetCopy.swap(m_openDatabaseSet);
DatabaseSet::iterator end = openSetCopy.end();
for (DatabaseSet::iterator it = openSetCopy.begin(); it != end; ++it)
- (*it)->close(Database::RemoveDatabaseFromContext);
+ (*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;
diff --git a/WebCore/storage/DatabaseTracker.cpp b/WebCore/storage/DatabaseTracker.cpp
index cf56ff6..8d23a4f 100644
--- a/WebCore/storage/DatabaseTracker.cpp
+++ b/WebCore/storage/DatabaseTracker.cpp
@@ -506,6 +506,12 @@ void DatabaseTracker::addOpenDatabase(AbstractDatabase* database)
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);
@@ -553,10 +559,10 @@ void DatabaseTracker::removeOpenDatabase(AbstractDatabase* database)
m_openDatabaseMap->remove(database->securityOrigin());
delete nameMap;
- }
- Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
- originQuotaManager().removeOrigin(database->securityOrigin());
+ Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager());
+ originQuotaManager().removeOrigin(database->securityOrigin());
+ }
}
void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<AbstractDatabase> >* databases)
diff --git a/WebCore/storage/DatabaseTracker.h b/WebCore/storage/DatabaseTracker.h
index b1267bc..094ee66 100644
--- a/WebCore/storage/DatabaseTracker.h
+++ b/WebCore/storage/DatabaseTracker.h
@@ -63,7 +63,8 @@ public:
// 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.
- // no other lock is taken in the code locked on m_openDatabaseMapGuard.
+ // 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);
diff --git a/WebCore/storage/IDBCallbacks.h b/WebCore/storage/IDBCallbacks.h
index c90acc5..37fdc46 100644
--- a/WebCore/storage/IDBCallbacks.h
+++ b/WebCore/storage/IDBCallbacks.h
@@ -32,6 +32,7 @@
#include "IDBDatabase.h"
#include "IDBDatabaseError.h"
#include "IDBIndex.h"
+#include "IDBKey.h"
#include "IDBObjectStore.h"
#include "SerializedScriptValue.h"
#include <wtf/RefCounted.h>
@@ -48,6 +49,7 @@ public:
virtual void onSuccess() = 0; // For "null".
virtual void onSuccess(PassRefPtr<IDBDatabase>) = 0;
virtual void onSuccess(PassRefPtr<IDBIndex>) = 0;
+ virtual void onSuccess(PassRefPtr<IDBKey>) = 0;
virtual void onSuccess(PassRefPtr<IDBObjectStore>) = 0;
virtual void onSuccess(PassRefPtr<SerializedScriptValue>) = 0;
};
diff --git a/WebCore/storage/IDBKey.cpp b/WebCore/storage/IDBKey.cpp
new file mode 100644
index 0000000..36a83a6
--- /dev/null
+++ b/WebCore/storage/IDBKey.cpp
@@ -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.
+ */
+
+#include "config.h"
+#include "IDBKey.h"
+
+#include "SerializedScriptValue.h"
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+IDBKey::IDBKey()
+ : m_type(NullType)
+{
+}
+
+IDBKey::~IDBKey()
+{
+}
+
+IDBKey::IDBKey(int32_t number)
+ : m_type(NumberType)
+ , m_number(number)
+{
+}
+
+IDBKey::IDBKey(const String& string)
+ : m_type(StringType)
+ , m_string(string)
+{
+}
+
+IDBKey::~IDBKey()
+{
+}
+
+} // namespace WebCore
+
+#endif
diff --git a/WebCore/storage/IDBKey.h b/WebCore/storage/IDBKey.h
new file mode 100644
index 0000000..0cd205b
--- /dev/null
+++ b/WebCore/storage/IDBKey.h
@@ -0,0 +1,90 @@
+/*
+ * 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
+
+#include "PlatformString.h"
+#include <wtf/PassRefPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+// FIXME: Add dates.
+class IDBKey : public RefCounted<IDBKey> {
+public:
+ static PassRefPtr<IDBKey> create()
+ {
+ return adoptRef(new IDBKey());
+ }
+ static PassRefPtr<IDBKey> create(int32_t number)
+ {
+ return adoptRef(new IDBKey(number));
+ }
+ static PassRefPtr<IDBKey> create(const String& string)
+ {
+ return adoptRef(new IDBKey(string));
+ }
+ ~IDBKey() { }
+
+ // In order of the least to the most precident in terms of sort order.
+ enum Type {
+ NullType = 0,
+ StringType,
+ NumberType
+ };
+
+ Type type() const { return m_type; }
+
+ const String& string() const
+ {
+ ASSERT(m_type == StringType);
+ return m_string;
+ }
+
+ int32_t number() const
+ {
+ ASSERT(m_type == NumberType);
+ return m_number;
+ }
+
+private:
+ IDBKey();
+ explicit IDBKey(int32_t);
+ explicit IDBKey(const String&);
+
+ Type m_type;
+ String m_string;
+ int32_t m_number;
+};
+
+}
+
+#endif // ENABLE(INDEXED_DATABASE)
+
+#endif // IDBKey_h
diff --git a/WebCore/storage/IDBKey.idl b/WebCore/storage/IDBKey.idl
new file mode 100644
index 0000000..04995f3
--- /dev/null
+++ b/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/WebCore/storage/IDBKeyTree.h b/WebCore/storage/IDBKeyTree.h
new file mode 100644
index 0000000..0e39e77
--- /dev/null
+++ b/WebCore/storage/IDBKeyTree.h
@@ -0,0 +1,157 @@
+/*
+ * 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 IDBKeyTree_h
+#define IDBKeyTree_h
+
+#include "IDBKey.h"
+#include <wtf/AVLTree.h>
+#include <wtf/Vector.h>
+
+#if ENABLE(INDEXED_DATABASE)
+
+namespace WebCore {
+
+template <typename ValueType>
+class IDBKeyTree : public RefCounted<IDBKeyTree<ValueType> > {
+public:
+ static PassRefPtr<IDBKeyTree> create()
+ {
+ return adoptRef(new IDBKeyTree());
+ }
+ ~IDBKeyTree();
+
+ PassRefPtr<ValueType> get(PassRefPtr<IDBKey> key);
+ void put(PassRefPtr<IDBKey> key, PassRefPtr<ValueType> value);
+ void remove(PassRefPtr<IDBKey> key);
+
+private:
+ struct TreeNode {
+ RefPtr<ValueType> value;
+ RefPtr<IDBKey> key;
+
+ TreeNode* less;
+ TreeNode* greater;
+ int balanceFactor;
+ };
+
+ struct AVLTreeAbstractor {
+ typedef TreeNode* handle;
+ typedef size_t size;
+ typedef IDBKey* key;
+
+ handle get_less(handle h) { return h->less; }
+ void set_less(handle h, handle lh) { h->less = lh; }
+ handle get_greater(handle h) { return h->greater; }
+ void set_greater(handle h, handle gh) { h->greater = gh; }
+ int get_balance_factor(handle h) { return h->balanceFactor; }
+ void set_balance_factor(handle h, int bf) { h->balanceFactor = bf; }
+
+ static handle null() { return 0; }
+
+ int compare_key_key(key va, key vb);
+ int compare_key_node(key k, handle h) { return compare_key_key(k, h->key.get()); }
+ int compare_node_node(handle h1, handle h2) { return compare_key_key(h1->key.get(), h2->key.get()); }
+ };
+
+ IDBKeyTree();
+
+ typedef WTF::AVLTree<AVLTreeAbstractor> TreeType;
+ TreeType m_tree;
+};
+
+template <typename ValueType>
+IDBKeyTree<ValueType>::IDBKeyTree()
+{
+}
+
+template <typename ValueType>
+IDBKeyTree<ValueType>::~IDBKeyTree()
+{
+ typename TreeType::Iterator iter;
+ iter.start_iter_least(m_tree);
+ for (; *iter; ++iter)
+ delete *iter;
+ m_tree.purge();
+}
+
+template <typename ValueType>
+int IDBKeyTree<ValueType>::AVLTreeAbstractor::compare_key_key(key va, key vb)
+{
+ if (va->type() != vb->type())
+ return vb->type() - va->type();
+
+ switch (va->type()) {
+ case IDBKey::NullType:
+ return 0;
+ case IDBKey::NumberType:
+ return vb->number() - va->number();
+ case IDBKey::StringType:
+ return codePointCompare(va->string(), vb->string());
+ // FIXME: Handle dates. Oh, and test this thoroughly.
+ default:
+ ASSERT_NOT_REACHED();
+ return 0;
+ }
+}
+
+template <typename ValueType>
+PassRefPtr<ValueType> IDBKeyTree<ValueType>::get(PassRefPtr<IDBKey> prpKey)
+{
+ RefPtr<IDBKey> key = prpKey;
+ TreeNode* node = m_tree.search(key.get());
+ if (!node)
+ return 0;
+ return node->value;
+}
+
+
+template <typename ValueType>
+void IDBKeyTree<ValueType>::put(PassRefPtr<IDBKey> prpKey, PassRefPtr<ValueType> value)
+{
+ RefPtr<IDBKey> key = prpKey;
+ TreeNode* node = m_tree.search(key.get());
+ if (!node) {
+ node = new TreeNode();
+ node->key = key.release();
+ m_tree.insert(node);
+ }
+ node->value = value;
+}
+
+template <typename ValueType>
+void IDBKeyTree<ValueType>::remove(PassRefPtr<IDBKey> prpKey)
+{
+ RefPtr<IDBKey> key = prpKey;
+ TreeNode* node = m_tree.remove(key.get());
+ if (node)
+ delete node;
+}
+
+}
+
+#endif // ENABLE(INDEXED_DATABASE)
+
+#endif // IDBKeyTree_h
diff --git a/WebCore/storage/IDBRequest.cpp b/WebCore/storage/IDBRequest.cpp
index f0ba25b..7ec8ee6 100644
--- a/WebCore/storage/IDBRequest.cpp
+++ b/WebCore/storage/IDBRequest.cpp
@@ -85,6 +85,11 @@ void IDBRequest::onSuccess(PassRefPtr<IDBIndex> idbIndex)
m_result->set(IDBIndexRequest::create(idbIndex));
}
+void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
+{
+ ASSERT_NOT_REACHED();
+}
+
void IDBRequest::onSuccess(PassRefPtr<IDBObjectStore> idbObjectStore)
{
onEventCommon();
diff --git a/WebCore/storage/IDBRequest.h b/WebCore/storage/IDBRequest.h
index 4fb4eed..c763d96 100644
--- a/WebCore/storage/IDBRequest.h
+++ b/WebCore/storage/IDBRequest.h
@@ -66,6 +66,7 @@ public:
virtual void onSuccess(); // For "null".
virtual void onSuccess(PassRefPtr<IDBDatabase>);
virtual void onSuccess(PassRefPtr<IDBIndex>);
+ virtual void onSuccess(PassRefPtr<IDBKey>);
virtual void onSuccess(PassRefPtr<IDBObjectStore>);
virtual void onSuccess(PassRefPtr<SerializedScriptValue>);
diff --git a/WebCore/storage/SQLTransaction.cpp b/WebCore/storage/SQLTransaction.cpp
index 5a1e2b0..960427b 100644
--- a/WebCore/storage/SQLTransaction.cpp
+++ b/WebCore/storage/SQLTransaction.cpp
@@ -89,7 +89,7 @@ SQLTransaction::~SQLTransaction()
void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e)
{
- if (!m_executeSqlAllowed || m_database->stopped()) {
+ if (!m_executeSqlAllowed || !m_database->opened()) {
e = INVALID_STATE_ERR;
return;
}
@@ -149,7 +149,7 @@ const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod
void SQLTransaction::checkAndHandleClosedDatabase()
{
- if (!m_database->stopped())
+ if (m_database->opened())
return;
// If the database was stopped, don't do anything and cancel queued work