summaryrefslogtreecommitdiffstats
path: root/WebCore/storage
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/storage')
-rw-r--r--WebCore/storage/Database.cpp80
-rw-r--r--WebCore/storage/Database.h4
-rw-r--r--WebCore/storage/DatabaseTask.cpp76
-rw-r--r--WebCore/storage/DatabaseTask.h79
-rw-r--r--WebCore/storage/DatabaseThread.cpp13
-rw-r--r--WebCore/storage/DatabaseThread.h7
-rw-r--r--WebCore/storage/DatabaseTracker.h39
-rw-r--r--WebCore/storage/LocalStorageTask.h11
-rw-r--r--WebCore/storage/LocalStorageThread.cpp74
-rw-r--r--WebCore/storage/LocalStorageThread.h28
-rw-r--r--WebCore/storage/OriginUsageRecord.h2
-rw-r--r--WebCore/storage/SQLTransactionClient.h4
-rw-r--r--WebCore/storage/SQLTransactionCoordinator.h2
-rw-r--r--WebCore/storage/StorageAreaImpl.cpp41
-rw-r--r--WebCore/storage/StorageAreaImpl.h3
-rw-r--r--WebCore/storage/StorageAreaSync.cpp32
-rw-r--r--WebCore/storage/StorageAreaSync.h8
-rw-r--r--WebCore/storage/StorageMap.cpp33
-rw-r--r--WebCore/storage/StorageSyncManager.cpp20
-rw-r--r--WebCore/storage/StorageSyncManager.h6
-rw-r--r--WebCore/storage/chromium/DatabaseObserver.h49
-rw-r--r--WebCore/storage/chromium/DatabaseTrackerChromium.cpp100
-rw-r--r--WebCore/storage/chromium/QuotaTracker.cpp69
-rw-r--r--WebCore/storage/chromium/QuotaTracker.h63
-rw-r--r--WebCore/storage/chromium/SQLTransactionClientChromium.cpp73
25 files changed, 646 insertions, 270 deletions
diff --git a/WebCore/storage/Database.cpp b/WebCore/storage/Database.cpp
index 5aaa26f..29dec26 100644
--- a/WebCore/storage/Database.cpp
+++ b/WebCore/storage/Database.cpp
@@ -149,11 +149,12 @@ PassRefPtr<Database> Database::openDatabase(Document* document, const String& na
Database::Database(Document* document, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
: m_transactionInProgress(false)
+ , m_isTransactionQueueEnabled(true)
, m_document(document)
, m_name(name.crossThreadString())
, m_guid(0)
- , m_expectedVersion(expectedVersion)
- , m_displayName(displayName)
+ , m_expectedVersion(expectedVersion.crossThreadString())
+ , m_displayName(displayName.crossThreadString())
, m_estimatedSize(estimatedSize)
, m_deleted(false)
, m_stopped(false)
@@ -190,6 +191,11 @@ Database::Database(Document* document, const String& name, const String& expecte
m_document->addOpenDatabase(this);
}
+static void derefDocument(void* document)
+{
+ static_cast<Document*>(document)->deref();
+}
+
Database::~Database()
{
if (m_document->databaseThread())
@@ -197,6 +203,9 @@ Database::~Database()
DatabaseTracker::tracker().removeOpenDatabase(this);
m_document->removeOpenDatabase(this);
+
+ // Deref m_document on the main thread.
+ callOnMainThread(derefDocument, m_document.release().releaseRef());
}
bool Database::openAndVerifyVersion(ExceptionCode& e)
@@ -205,15 +214,14 @@ bool Database::openAndVerifyVersion(ExceptionCode& e)
return false;
m_databaseAuthorizer = DatabaseAuthorizer::create();
- RefPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this);
+ bool success = false;
+ DatabaseTaskSynchronizer synchronizer;
+ OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, &synchronizer, e, success);
- task->lockForSynchronousScheduling();
- m_document->databaseThread()->scheduleImmediateTask(task);
- task->waitForSynchronousCompletion();
+ m_document->databaseThread()->scheduleImmediateTask(task.release());
+ synchronizer.waitForTaskCompletion();
- ASSERT(task->isComplete());
- e = task->exceptionCode();
- return task->openSuccessful();
+ return success;
}
@@ -318,11 +326,11 @@ void Database::markAsDeletedAndClose()
m_document->databaseThread()->unscheduleDatabaseTasks(this);
- RefPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this);
+ DatabaseTaskSynchronizer synchronizer;
+ OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer);
- task->lockForSynchronousScheduling();
- m_document->databaseThread()->scheduleImmediateTask(task);
- task->waitForSynchronousCompletion();
+ m_document->databaseThread()->scheduleImmediateTask(task.release());
+ synchronizer.waitForTaskCompletion();
}
void Database::close()
@@ -362,7 +370,7 @@ void Database::stop()
{
MutexLocker locker(m_transactionInProgressMutex);
- m_transactionQueue.kill();
+ m_isTransactionQueueEnabled = false;
m_transactionInProgress = false;
}
}
@@ -497,15 +505,13 @@ bool Database::performOpenAndVerify(ExceptionCode& e)
currentVersion = "";
}
- // FIXME: For now, the spec says that if the database has no version, it is valid for any "Expected version" string. That seems silly and I think it should be
- // changed, and here's where we would change it
- if (m_expectedVersion.length()) {
- if (currentVersion.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;
- return false;
- }
+ // 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_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;
+ return false;
}
return true;
@@ -534,8 +540,14 @@ void Database::scheduleTransaction()
{
ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
RefPtr<SQLTransaction> transaction;
- if (m_transactionQueue.tryGetMessage(transaction) && m_document->databaseThread()) {
- RefPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
+
+ if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty()) {
+ transaction = m_transactionQueue.first();
+ m_transactionQueue.removeFirst();
+ }
+
+ if (transaction && m_document->databaseThread()) {
+ OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
m_transactionInProgress = true;
m_document->databaseThread()->scheduleTask(task.release());
@@ -548,7 +560,7 @@ void Database::scheduleTransactionStep(SQLTransaction* transaction, bool immedia
if (!m_document->databaseThread())
return;
- RefPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
+ OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
if (immediately)
m_document->databaseThread()->scheduleImmediateTask(task.release());
@@ -618,15 +630,19 @@ void Database::deliverPendingCallback(void* context)
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;
if (!m_document->databaseThread())
- return Vector<String>();
- RefPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this);
+ return result;
+
+ DatabaseTaskSynchronizer synchronizer;
+ OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
- task->lockForSynchronousScheduling();
- m_document->databaseThread()->scheduleImmediateTask(task);
- task->waitForSynchronousCompletion();
+ m_document->databaseThread()->scheduleImmediateTask(task.release());
+ synchronizer.waitForTaskCompletion();
- return task->tableNames();
+ return result;
}
void Database::setExpectedVersion(const String& version)
diff --git a/WebCore/storage/Database.h b/WebCore/storage/Database.h
index f275027..61c9b66 100644
--- a/WebCore/storage/Database.h
+++ b/WebCore/storage/Database.h
@@ -30,7 +30,6 @@
#define Database_h
#if ENABLE(DATABASE)
-#include <wtf/MessageQueue.h>
#include "PlatformString.h"
#include "SecurityOrigin.h"
#include "SQLiteDatabase.h"
@@ -133,9 +132,10 @@ private:
void scheduleTransactionCallback(SQLTransaction*);
void scheduleTransactionStep(SQLTransaction* transaction, bool immediately = false);
- MessageQueue<RefPtr<SQLTransaction> > m_transactionQueue;
+ Deque<RefPtr<SQLTransaction> > m_transactionQueue;
Mutex m_transactionInProgressMutex;
bool m_transactionInProgress;
+ bool m_isTransactionQueueEnabled;
static void deliverPendingCallback(void*);
diff --git a/WebCore/storage/DatabaseTask.cpp b/WebCore/storage/DatabaseTask.cpp
index 755da7c..702c96b 100644
--- a/WebCore/storage/DatabaseTask.cpp
+++ b/WebCore/storage/DatabaseTask.cpp
@@ -35,9 +35,33 @@
namespace WebCore {
-DatabaseTask::DatabaseTask(Database* database)
+DatabaseTaskSynchronizer::DatabaseTaskSynchronizer()
+ : m_taskCompleted(false)
+{
+}
+
+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
{
}
@@ -56,43 +80,19 @@ void DatabaseTask::performTask()
doPerformTask();
m_database->performPolicyChecks();
- if (m_synchronousMutex)
- m_synchronousMutex->lock();
-
- m_complete = true;
-
- if (m_synchronousMutex) {
- m_synchronousCondition->signal();
- m_synchronousMutex->unlock();
- }
-}
-
-void DatabaseTask::lockForSynchronousScheduling()
-{
- // Called from main thread.
- ASSERT(!m_synchronousMutex);
- ASSERT(!m_synchronousCondition);
- m_synchronousMutex.set(new Mutex);
- m_synchronousCondition.set(new ThreadCondition);
-}
-
-void DatabaseTask::waitForSynchronousCompletion()
-{
- // Called from main thread.
- m_synchronousMutex->lock();
- if (!m_complete)
- m_synchronousCondition->wait(*m_synchronousMutex);
- m_synchronousMutex->unlock();
+ if (m_synchronizer)
+ m_synchronizer->taskCompleted();
}
// *** DatabaseOpenTask ***
// Opens the database file and verifies the version matches the expected version.
-DatabaseOpenTask::DatabaseOpenTask(Database* database)
- : DatabaseTask(database)
- , m_code(0)
- , m_success(false)
+DatabaseOpenTask::DatabaseOpenTask(Database* database, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success)
+ : DatabaseTask(database, synchronizer)
+ , m_code(code)
+ , m_success(success)
{
+ ASSERT(synchronizer); // A task with output parameters is supposed to be synchronous.
}
void DatabaseOpenTask::doPerformTask()
@@ -110,8 +110,8 @@ const char* DatabaseOpenTask::debugTaskName() const
// *** DatabaseCloseTask ***
// Closes the database.
-DatabaseCloseTask::DatabaseCloseTask(Database* database)
- : DatabaseTask(database)
+DatabaseCloseTask::DatabaseCloseTask(Database* database, DatabaseTaskSynchronizer* synchronizer)
+ : DatabaseTask(database, synchronizer)
{
}
@@ -131,7 +131,7 @@ const char* DatabaseCloseTask::debugTaskName() const
// Starts a transaction that will report its results via a callback.
DatabaseTransactionTask::DatabaseTransactionTask(PassRefPtr<SQLTransaction> transaction)
- : DatabaseTask(transaction->database())
+ : DatabaseTask(transaction->database(), 0)
, m_transaction(transaction)
{
}
@@ -159,9 +159,11 @@ const char* DatabaseTransactionTask::debugTaskName() const
// *** DatabaseTableNamesTask ***
// Retrieves a list of all tables in the database - for WebInspector support.
-DatabaseTableNamesTask::DatabaseTableNamesTask(Database* database)
- : DatabaseTask(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()
diff --git a/WebCore/storage/DatabaseTask.h b/WebCore/storage/DatabaseTask.h
index 4aef892..97a23c7 100644
--- a/WebCore/storage/DatabaseTask.h
+++ b/WebCore/storage/DatabaseTask.h
@@ -32,6 +32,7 @@
#include "ExceptionCode.h"
#include "PlatformString.h"
#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
#include <wtf/PassRefPtr.h>
#include <wtf/Threading.h>
#include <wtf/Vector.h>
@@ -39,13 +40,33 @@
namespace WebCore {
class Database;
+class DatabaseTask;
class DatabaseThread;
class SQLValue;
class SQLCallback;
class SQLTransaction;
class VersionChangeCallback;
-class DatabaseTask : public ThreadSafeShared<DatabaseTask> {
+// 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 {
+ friend class DatabaseTask;
+public:
+ DatabaseTaskSynchronizer();
+
+ // Called from main thread to wait until task is completed.
+ void waitForTaskCompletion();
+
+private:
+ // Called by the task.
+ void taskCompleted();
+
+ bool m_taskCompleted;
+ Mutex m_synchronousMutex;
+ ThreadCondition m_synchronousCondition;
+};
+
+class DatabaseTask : public Noncopyable {
friend class Database;
public:
virtual ~DatabaseTask();
@@ -53,53 +74,50 @@ public:
void performTask();
Database* database() const { return m_database; }
- bool isComplete() const { return m_complete; }
protected:
- DatabaseTask(Database*);
+ DatabaseTask(Database*, DatabaseTaskSynchronizer*);
private:
virtual void doPerformTask() = 0;
-#ifndef NDEBUG
- virtual const char* debugTaskName() const = 0;
-#endif
-
- void lockForSynchronousScheduling();
- void waitForSynchronousCompletion();
Database* m_database;
+ DatabaseTaskSynchronizer* m_synchronizer;
- bool m_complete;
-
- OwnPtr<Mutex> m_synchronousMutex;
- OwnPtr<ThreadCondition> m_synchronousCondition;
+#ifndef NDEBUG
+ virtual const char* debugTaskName() const = 0;
+ bool m_complete;
+#endif
};
class DatabaseOpenTask : public DatabaseTask {
public:
- static PassRefPtr<DatabaseOpenTask> create(Database* db) { return adoptRef(new DatabaseOpenTask(db)); }
-
- ExceptionCode exceptionCode() const { return m_code; }
- bool openSuccessful() const { return m_success; }
+ static PassOwnPtr<DatabaseOpenTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer, ExceptionCode& code, bool& success)
+ {
+ return new DatabaseOpenTask(db, synchronizer, code, success);
+ }
private:
- DatabaseOpenTask(Database*);
+ DatabaseOpenTask(Database*, DatabaseTaskSynchronizer*, ExceptionCode&, bool& success);
virtual void doPerformTask();
#ifndef NDEBUG
virtual const char* debugTaskName() const;
#endif
- ExceptionCode m_code;
- bool m_success;
+ ExceptionCode& m_code;
+ bool& m_success;
};
class DatabaseCloseTask : public DatabaseTask {
public:
- static PassRefPtr<DatabaseCloseTask> create(Database* db) { return adoptRef(new DatabaseCloseTask(db)); }
+ static PassOwnPtr<DatabaseCloseTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer)
+ {
+ return new DatabaseCloseTask(db, synchronizer);
+ }
private:
- DatabaseCloseTask(Database*);
+ DatabaseCloseTask(Database*, DatabaseTaskSynchronizer*);
virtual void doPerformTask();
#ifndef NDEBUG
@@ -109,7 +127,11 @@ private:
class DatabaseTransactionTask : public DatabaseTask {
public:
- static PassRefPtr<DatabaseTransactionTask> create(PassRefPtr<SQLTransaction> transaction) { return adoptRef(new DatabaseTransactionTask(transaction)); }
+ // Transaction task is never synchronous, so no 'synchronizer' parameter.
+ static PassOwnPtr<DatabaseTransactionTask> create(PassRefPtr<SQLTransaction> transaction)
+ {
+ return new DatabaseTransactionTask(transaction);
+ }
SQLTransaction* transaction() const { return m_transaction.get(); }
@@ -127,19 +149,20 @@ private:
class DatabaseTableNamesTask : public DatabaseTask {
public:
- static PassRefPtr<DatabaseTableNamesTask> create(Database* db) { return adoptRef(new DatabaseTableNamesTask(db)); }
-
- Vector<String>& tableNames() { return m_tableNames; }
+ static PassOwnPtr<DatabaseTableNamesTask> create(Database* db, DatabaseTaskSynchronizer* synchronizer, Vector<String>& names)
+ {
+ return new DatabaseTableNamesTask(db, synchronizer, names);
+ }
private:
- DatabaseTableNamesTask(Database*);
+ DatabaseTableNamesTask(Database*, DatabaseTaskSynchronizer*, Vector<String>& names);
virtual void doPerformTask();
#ifndef NDEBUG
virtual const char* debugTaskName() const;
#endif
- Vector<String> m_tableNames;
+ Vector<String>& m_tableNames;
};
} // namespace WebCore
diff --git a/WebCore/storage/DatabaseThread.cpp b/WebCore/storage/DatabaseThread.cpp
index 40c83ee..12e9251 100644
--- a/WebCore/storage/DatabaseThread.cpp
+++ b/WebCore/storage/DatabaseThread.cpp
@@ -91,13 +91,8 @@ void* DatabaseThread::databaseThread()
}
AutodrainedPool pool;
- while (true) {
- RefPtr<DatabaseTask> task;
- if (!m_queue.waitForMessage(task))
- break;
-
+ while (OwnPtr<DatabaseTask> task = m_queue.waitForMessage()) {
task->performTask();
-
pool.cycle();
}
@@ -142,12 +137,12 @@ void DatabaseThread::recordDatabaseClosed(Database* database)
m_openDatabaseSet.remove(database);
}
-void DatabaseThread::scheduleTask(PassRefPtr<DatabaseTask> task)
+void DatabaseThread::scheduleTask(PassOwnPtr<DatabaseTask> task)
{
m_queue.append(task);
}
-void DatabaseThread::scheduleImmediateTask(PassRefPtr<DatabaseTask> task)
+void DatabaseThread::scheduleImmediateTask(PassOwnPtr<DatabaseTask> task)
{
m_queue.prepend(task);
}
@@ -155,7 +150,7 @@ void DatabaseThread::scheduleImmediateTask(PassRefPtr<DatabaseTask> task)
class SameDatabasePredicate {
public:
SameDatabasePredicate(const Database* database) : m_database(database) { }
- bool operator()(RefPtr<DatabaseTask>& task) const { return task->database() == m_database; }
+ bool operator()(DatabaseTask* task) const { return task->database() == m_database; }
private:
const Database* m_database;
};
diff --git a/WebCore/storage/DatabaseThread.h b/WebCore/storage/DatabaseThread.h
index 83b1baf..269a633 100644
--- a/WebCore/storage/DatabaseThread.h
+++ b/WebCore/storage/DatabaseThread.h
@@ -34,6 +34,7 @@
#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>
@@ -55,8 +56,8 @@ public:
void requestTermination();
bool terminationRequested() const;
- void scheduleTask(PassRefPtr<DatabaseTask>);
- void scheduleImmediateTask(PassRefPtr<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 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*);
@@ -76,7 +77,7 @@ private:
ThreadIdentifier m_threadID;
RefPtr<DatabaseThread> m_selfRef;
- MessageQueue<RefPtr<DatabaseTask> > m_queue;
+ MessageQueue<DatabaseTask> m_queue;
// This set keeps track of the open databases that have been used on this thread.
typedef HashSet<RefPtr<Database> > DatabaseSet;
diff --git a/WebCore/storage/DatabaseTracker.h b/WebCore/storage/DatabaseTracker.h
index 85e4858..c87b26a 100644
--- a/WebCore/storage/DatabaseTracker.h
+++ b/WebCore/storage/DatabaseTracker.h
@@ -31,41 +31,56 @@
#if ENABLE(DATABASE)
-#include "DatabaseDetails.h"
#include "PlatformString.h"
+
+#if !PLATFORM(CHROMIUM)
+#include "DatabaseDetails.h"
#include "SQLiteDatabase.h"
#include "StringHash.h"
#include <wtf/HashSet.h>
#include <wtf/OwnPtr.h>
+#endif // !PLATFORM(CHROMIUM)
namespace WebCore {
class Database;
-class DatabaseTrackerClient;
class Document;
-class OriginQuotaManager;
class SecurityOrigin;
+#if !PLATFORM(CHROMIUM)
+class DatabaseTrackerClient;
+class OriginQuotaManager;
+
struct SecurityOriginHash;
struct SecurityOriginTraits;
+#endif // !PLATFORM(CHROMIUM)
-class DatabaseTracker {
+class DatabaseTracker : public Noncopyable {
public:
- void setDatabaseDirectoryPath(const String&);
- const String& databaseDirectoryPath() const;
+ static DatabaseTracker& tracker();
bool canEstablishDatabase(Document*, 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(Database*);
+ void removeOpenDatabase(Database*);
+
+ unsigned long long getMaxSizeForDatabase(const Database*);
+
+private:
+ DatabaseTracker();
+
+#if !PLATFORM(CHROMIUM)
+public:
+ void setDatabaseDirectoryPath(const String&);
+ const String& databaseDirectoryPath() const;
+
void origins(Vector<RefPtr<SecurityOrigin> >& result);
bool databaseNamesForOrigin(SecurityOrigin*, Vector<String>& result);
DatabaseDetails detailsForNameAndOrigin(const String&, SecurityOrigin*);
- void addOpenDatabase(Database*);
- void removeOpenDatabase(Database*);
-
unsigned long long usageForDatabase(const String&, SecurityOrigin*);
unsigned long long usageForOrigin(SecurityOrigin*);
unsigned long long quotaForOrigin(SecurityOrigin*);
@@ -82,15 +97,10 @@ public:
OriginQuotaManager& originQuotaManager();
- static DatabaseTracker& tracker();
bool hasEntryForOrigin(SecurityOrigin*);
- unsigned long long getMaxSizeForDatabase(const Database*);
-
private:
- DatabaseTracker();
-
String trackerDatabasePath() const;
void openTrackerDatabase(bool createIfDoesNotExist);
@@ -130,6 +140,7 @@ private:
static void scheduleForNotification();
static void notifyDatabasesChanged(void*);
+#endif // !PLATFORM(CHROMIUM)
};
} // namespace WebCore
diff --git a/WebCore/storage/LocalStorageTask.h b/WebCore/storage/LocalStorageTask.h
index f03d851..dc3e7e2 100644
--- a/WebCore/storage/LocalStorageTask.h
+++ b/WebCore/storage/LocalStorageTask.h
@@ -28,8 +28,7 @@
#if ENABLE(DOM_STORAGE)
-#include <wtf/PassRefPtr.h>
-#include <wtf/RefPtr.h>
+#include <wtf/PassOwnPtr.h>
#include <wtf/Threading.h>
namespace WebCore {
@@ -38,15 +37,15 @@ namespace WebCore {
class LocalStorageThread;
// FIXME: Rename this class to StorageTask
- class LocalStorageTask : public ThreadSafeShared<LocalStorageTask> {
+ class LocalStorageTask : public Noncopyable {
public:
enum Type { AreaImport, AreaSync, TerminateThread };
~LocalStorageTask();
- static PassRefPtr<LocalStorageTask> createImport(StorageAreaSync* area) { return adoptRef(new LocalStorageTask(AreaImport, area)); }
- static PassRefPtr<LocalStorageTask> createSync(StorageAreaSync* area) { return adoptRef(new LocalStorageTask(AreaSync, area)); }
- static PassRefPtr<LocalStorageTask> createTerminate(LocalStorageThread* thread) { return adoptRef(new LocalStorageTask(TerminateThread, thread)); }
+ static PassOwnPtr<LocalStorageTask> createImport(StorageAreaSync* area) { return new LocalStorageTask(AreaImport, area); }
+ static PassOwnPtr<LocalStorageTask> createSync(StorageAreaSync* area) { return new LocalStorageTask(AreaSync, area); }
+ static PassOwnPtr<LocalStorageTask> createTerminate(LocalStorageThread* thread) { return new LocalStorageTask(TerminateThread, thread); }
void performTask();
diff --git a/WebCore/storage/LocalStorageThread.cpp b/WebCore/storage/LocalStorageThread.cpp
index 78640a9..d4a7b4c 100644
--- a/WebCore/storage/LocalStorageThread.cpp
+++ b/WebCore/storage/LocalStorageThread.cpp
@@ -33,96 +33,70 @@
namespace WebCore {
-PassRefPtr<LocalStorageThread> LocalStorageThread::create()
+PassOwnPtr<LocalStorageThread> LocalStorageThread::create()
{
- return adoptRef(new LocalStorageThread);
+ return new LocalStorageThread;
}
LocalStorageThread::LocalStorageThread()
: m_threadID(0)
{
- m_selfRef = this;
}
-bool LocalStorageThread::start()
+LocalStorageThread::~LocalStorageThread()
{
- MutexLocker lock(m_threadCreationMutex);
-
- if (m_threadID)
- return true;
-
- m_threadID = createThread(LocalStorageThread::localStorageThreadStart, this, "WebCore: LocalStorage");
+ 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::localStorageThreadStart(void* thread)
+void* LocalStorageThread::threadEntryPointCallback(void* thread)
{
- return static_cast<LocalStorageThread*>(thread)->localStorageThread();
+ return static_cast<LocalStorageThread*>(thread)->threadEntryPoint();
}
-void* LocalStorageThread::localStorageThread()
+void* LocalStorageThread::threadEntryPoint()
{
- {
- // Wait for LocalStorageThread::start() to complete.
- MutexLocker lock(m_threadCreationMutex);
- }
-
- while (true) {
- RefPtr<LocalStorageTask> task;
- if (!m_queue.waitForMessage(task))
- break;
-
+ ASSERT(!isMainThread());
+ while (OwnPtr<LocalStorageTask> task = m_queue.waitForMessage())
task->performTask();
- }
-
- // Detach the thread so its resources are no longer of any concern to anyone else
- detachThread(m_threadID);
- m_threadID = 0;
-
- // Clear the self refptr, possibly resulting in deletion
- m_selfRef = 0;
return 0;
}
-void LocalStorageThread::scheduleImport(StorageAreaSync* area)
-{
- ASSERT(!m_queue.killed() && m_threadID);
- m_queue.append(LocalStorageTask::createImport(area));
-}
-
-void LocalStorageThread::scheduleSync(StorageAreaSync* area)
+void LocalStorageThread::scheduleTask(PassOwnPtr<LocalStorageTask> task)
{
+ ASSERT(isMainThread());
ASSERT(!m_queue.killed() && m_threadID);
- m_queue.append(LocalStorageTask::createSync(area));
+ m_queue.append(task);
}
void LocalStorageThread::terminate()
{
ASSERT(isMainThread());
-
- // Ideally we'd never be killing a thread that wasn't live, so ASSERT it.
- // But if we do in a release build, make sure to not wait on a condition that will never get signalled
ASSERT(!m_queue.killed() && m_threadID);
+ // Even in weird, exceptional cases, don't wait on a nonexistent thread to terminate.
if (!m_threadID)
return;
- MutexLocker locker(m_terminateLock);
-
+ void* returnValue;
m_queue.append(LocalStorageTask::createTerminate(this));
-
- m_terminateCondition.wait(m_terminateLock);
+ waitForThreadCompletion(m_threadID, &returnValue);
+ ASSERT(m_queue.killed());
+ m_threadID = 0;
}
void LocalStorageThread::performTerminate()
{
ASSERT(!isMainThread());
-
m_queue.kill();
-
- MutexLocker locker(m_terminateLock);
- m_terminateCondition.signal();
}
}
diff --git a/WebCore/storage/LocalStorageThread.h b/WebCore/storage/LocalStorageThread.h
index e9e2b58..6f05911 100644
--- a/WebCore/storage/LocalStorageThread.h
+++ b/WebCore/storage/LocalStorageThread.h
@@ -30,6 +30,7 @@
#include <wtf/HashSet.h>
#include <wtf/MessageQueue.h>
+#include <wtf/PassOwnPtr.h>
#include <wtf/PassRefPtr.h>
#include <wtf/Threading.h>
@@ -39,34 +40,27 @@ namespace WebCore {
class LocalStorageTask;
// FIXME: Rename this class to StorageThread
- class LocalStorageThread : public ThreadSafeShared<LocalStorageThread> {
+ class LocalStorageThread : public Noncopyable {
public:
- static PassRefPtr<LocalStorageThread> create();
+ static PassOwnPtr<LocalStorageThread> create();
+ ~LocalStorageThread();
bool start();
-
- void scheduleImport(StorageAreaSync*);
- void scheduleSync(StorageAreaSync*);
-
- // Called from the main thread to synchronously shut down this thread
void terminate();
- // Background thread part of the terminate procedure
+ void scheduleTask(PassOwnPtr<LocalStorageTask>);
+
+ // Background thread part of the terminate procedure.
void performTerminate();
private:
LocalStorageThread();
- static void* localStorageThreadStart(void*);
- void* localStorageThread();
+ // Called on background thread.
+ static void* threadEntryPointCallback(void*);
+ void* threadEntryPoint();
- Mutex m_threadCreationMutex;
ThreadIdentifier m_threadID;
- RefPtr<LocalStorageThread> m_selfRef;
-
- MessageQueue<RefPtr<LocalStorageTask> > m_queue;
-
- Mutex m_terminateLock;
- ThreadCondition m_terminateCondition;
+ MessageQueue<LocalStorageTask> m_queue;
};
} // namespace WebCore
diff --git a/WebCore/storage/OriginUsageRecord.h b/WebCore/storage/OriginUsageRecord.h
index 609a793..25bddf2 100644
--- a/WebCore/storage/OriginUsageRecord.h
+++ b/WebCore/storage/OriginUsageRecord.h
@@ -40,7 +40,7 @@ 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 {
+class OriginUsageRecord : public Noncopyable {
public:
OriginUsageRecord();
diff --git a/WebCore/storage/SQLTransactionClient.h b/WebCore/storage/SQLTransactionClient.h
index 941c163..e822594 100644
--- a/WebCore/storage/SQLTransactionClient.h
+++ b/WebCore/storage/SQLTransactionClient.h
@@ -31,13 +31,15 @@
#ifndef SQLTransactionClient_h
#define SQLTransactionClient_h
+#include <wtf/Noncopyable.h>
+
namespace WebCore {
class SQLTransaction;
// A client to the SQLTransaction class. Allows SQLTransaction to notify interested
// parties that certain things have happened in a transaction.
- class SQLTransactionClient {
+ class SQLTransactionClient : public Noncopyable {
public:
void didCommitTransaction(SQLTransaction*);
void didExecuteStatement(SQLTransaction*);
diff --git a/WebCore/storage/SQLTransactionCoordinator.h b/WebCore/storage/SQLTransactionCoordinator.h
index 20cc863..ae5674b 100644
--- a/WebCore/storage/SQLTransactionCoordinator.h
+++ b/WebCore/storage/SQLTransactionCoordinator.h
@@ -42,7 +42,7 @@ namespace WebCore {
class SQLTransaction;
- class SQLTransactionCoordinator {
+ class SQLTransactionCoordinator : public Noncopyable {
public:
void acquireLock(SQLTransaction*);
void releaseLock(SQLTransaction*);
diff --git a/WebCore/storage/StorageAreaImpl.cpp b/WebCore/storage/StorageAreaImpl.cpp
index 612cb5f..8c2a29c 100644
--- a/WebCore/storage/StorageAreaImpl.cpp
+++ b/WebCore/storage/StorageAreaImpl.cpp
@@ -30,6 +30,7 @@
#include "ExceptionCode.h"
#include "Frame.h"
+#include "Page.h"
#include "Settings.h"
#include "StorageAreaSync.h"
#include "StorageEventDispatcher.h"
@@ -64,7 +65,7 @@ StorageAreaImpl::StorageAreaImpl(StorageType storageType, PassRefPtr<SecurityOri
// 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 (m_storageSyncManager) {
- m_storageAreaSync = StorageAreaSync::create(m_storageSyncManager, this);
+ m_storageAreaSync = StorageAreaSync::create(m_storageSyncManager, this, m_securityOrigin->databaseIdentifier());
ASSERT(m_storageAreaSync);
}
}
@@ -106,6 +107,8 @@ static bool privateBrowsingEnabled(Frame* frame)
unsigned StorageAreaImpl::length() const
{
ASSERT(!m_isShutdown);
+ blockUntilImportComplete();
+
return m_storageMap->length();
}
@@ -113,6 +116,7 @@ String StorageAreaImpl::key(unsigned index) const
{
ASSERT(!m_isShutdown);
blockUntilImportComplete();
+
return m_storageMap->key(index);
}
@@ -138,21 +142,20 @@ void StorageAreaImpl::setItem(const String& key, const String& value, ExceptionC
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;
}
- if (newMap)
- m_storageMap = newMap.release();
+ if (oldValue == value)
+ return;
- // Only notify the client if an item was actually changed
- if (oldValue != value) {
- if (m_storageAreaSync)
- m_storageAreaSync->scheduleItemForSync(key, value);
- StorageEventDispatcher::dispatch(key, oldValue, value, m_storageType, m_securityOrigin.get(), frame);
- }
+ if (m_storageAreaSync)
+ m_storageAreaSync->scheduleItemForSync(key, value);
+ StorageEventDispatcher::dispatch(key, oldValue, value, m_storageType, m_securityOrigin.get(), frame);
}
void StorageAreaImpl::removeItem(const String& key, Frame* frame)
@@ -168,12 +171,12 @@ void StorageAreaImpl::removeItem(const String& key, Frame* frame)
if (newMap)
m_storageMap = newMap.release();
- // Only notify the client if an item was actually removed
- if (!oldValue.isNull()) {
- if (m_storageAreaSync)
- m_storageAreaSync->scheduleItemForSync(key, String());
- StorageEventDispatcher::dispatch(key, oldValue, String(), m_storageType, m_securityOrigin.get(), frame);
- }
+ if (oldValue.isNull())
+ return;
+
+ if (m_storageAreaSync)
+ m_storageAreaSync->scheduleItemForSync(key, String());
+ StorageEventDispatcher::dispatch(key, oldValue, String(), m_storageType, m_securityOrigin.get(), frame);
}
void StorageAreaImpl::clear(Frame* frame)
@@ -184,6 +187,9 @@ void StorageAreaImpl::clear(Frame* frame)
if (privateBrowsingEnabled(frame))
return;
+ if (!m_storageMap->length())
+ return;
+
unsigned quota = m_storageMap->quota();
m_storageMap = StorageMap::create(quota);
@@ -206,11 +212,6 @@ void StorageAreaImpl::importItem(const String& key, const String& value)
m_storageMap->importItem(key, value);
}
-SecurityOrigin* StorageAreaImpl::securityOrigin()
-{
- return m_securityOrigin.get();
-}
-
void StorageAreaImpl::close()
{
if (m_storageAreaSync)
diff --git a/WebCore/storage/StorageAreaImpl.h b/WebCore/storage/StorageAreaImpl.h
index fe21a45..0b2d34d 100644
--- a/WebCore/storage/StorageAreaImpl.h
+++ b/WebCore/storage/StorageAreaImpl.h
@@ -56,9 +56,8 @@ namespace WebCore {
PassRefPtr<StorageAreaImpl> copy();
void close();
- // Could be called from a background thread.
+ // Only called from a background thread.
void importItem(const String& key, const String& value);
- SecurityOrigin* securityOrigin();
private:
StorageAreaImpl(StorageType, PassRefPtr<SecurityOrigin>, PassRefPtr<StorageSyncManager>, unsigned quota);
diff --git a/WebCore/storage/StorageAreaSync.cpp b/WebCore/storage/StorageAreaSync.cpp
index ad41e28..d4eba76 100644
--- a/WebCore/storage/StorageAreaSync.cpp
+++ b/WebCore/storage/StorageAreaSync.cpp
@@ -31,6 +31,7 @@
#include "CString.h"
#include "EventNames.h"
#include "HTMLElement.h"
+#include "SecurityOrigin.h"
#include "SQLiteStatement.h"
#include "StorageAreaImpl.h"
#include "StorageSyncManager.h"
@@ -42,17 +43,18 @@ namespace WebCore {
// Instead, queue up a batch of items to sync and actually do the sync at the following interval.
static const double StorageSyncInterval = 1.0;
-PassRefPtr<StorageAreaSync> StorageAreaSync::create(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea)
+PassRefPtr<StorageAreaSync> StorageAreaSync::create(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea, String databaseIdentifier)
{
- return adoptRef(new StorageAreaSync(storageSyncManager, storageArea));
+ return adoptRef(new StorageAreaSync(storageSyncManager, storageArea, databaseIdentifier));
}
-StorageAreaSync::StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea)
+StorageAreaSync::StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea, 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_importComplete(false)
@@ -71,6 +73,7 @@ StorageAreaSync::~StorageAreaSync()
{
ASSERT(isMainThread());
ASSERT(!m_syncTimer.isActive());
+ ASSERT(m_finalSyncScheduled);
}
void StorageAreaSync::scheduleFinalSync()
@@ -78,6 +81,7 @@ 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();
@@ -165,7 +169,7 @@ void StorageAreaSync::performImport()
ASSERT(!isMainThread());
ASSERT(!m_database.isOpen());
- String databaseFilename = m_syncManager->fullDatabaseFilename(m_storageArea->securityOrigin());
+ String databaseFilename = m_syncManager->fullDatabaseFilename(m_databaseIdentifier);
if (databaseFilename.isEmpty()) {
LOG_ERROR("Filename for local storage database is empty - cannot open for persistent storage");
@@ -206,27 +210,18 @@ void StorageAreaSync::performImport()
return;
}
- MutexLocker locker(m_importLock);
-
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);
- // Break the (ref count) cycle.
- m_storageArea = 0;
- m_importComplete = true;
- m_importCondition.signal();
+ markImported();
}
void StorageAreaSync::markImported()
{
- ASSERT(!isMainThread());
-
MutexLocker locker(m_importLock);
- // Break the (ref count) cycle.
- m_storageArea = 0;
m_importComplete = true;
m_importCondition.signal();
}
@@ -238,19 +233,18 @@ void StorageAreaSync::markImported()
// 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() const
+void StorageAreaSync::blockUntilImportComplete()
{
ASSERT(isMainThread());
- // Fast path to avoid locking.
- if (m_importComplete)
+ // 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);
- ASSERT(m_importComplete);
- ASSERT(!m_storageArea);
+ m_storageArea = 0;
}
void StorageAreaSync::sync(bool clearItems, const HashMap<String, String>& items)
diff --git a/WebCore/storage/StorageAreaSync.h b/WebCore/storage/StorageAreaSync.h
index 9d6436f..9afdfde 100644
--- a/WebCore/storage/StorageAreaSync.h
+++ b/WebCore/storage/StorageAreaSync.h
@@ -42,17 +42,17 @@ namespace WebCore {
class StorageAreaSync : public RefCounted<StorageAreaSync> {
public:
- static PassRefPtr<StorageAreaSync> create(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea);
+ static PassRefPtr<StorageAreaSync> create(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea, String databaseIdentifier);
~StorageAreaSync();
void scheduleFinalSync();
- void blockUntilImportComplete() const;
+ void blockUntilImportComplete();
void scheduleItemForSync(const String& key, const String& value);
void scheduleClear();
private:
- StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea);
+ StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea, String databaseIdentifier);
void dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame);
@@ -78,6 +78,8 @@ namespace WebCore {
void syncTimerFired(Timer<StorageAreaSync>*);
void sync(bool clearItems, const HashMap<String, String>& items);
+ const String m_databaseIdentifier;
+
Mutex m_syncLock;
HashMap<String, String> m_itemsPendingSync;
bool m_clearItemsWhileSyncing;
diff --git a/WebCore/storage/StorageMap.cpp b/WebCore/storage/StorageMap.cpp
index 5498d9e..bb08671 100644
--- a/WebCore/storage/StorageMap.cpp
+++ b/WebCore/storage/StorageMap.cpp
@@ -111,12 +111,21 @@ PassRefPtr<StorageMap> StorageMap::setItem(const String& key, const String& valu
return newStorageMap.release();
}
- // Quota tracking. If the quota is enabled and this would go over it, bail.
+ // 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);
- unsigned newLength = m_currentLength + value.length() - oldValue.length();
+ 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);
- bool overflow = (newLength > m_currentLength) != (value.length() > oldValue.length());
- ASSERT(!overflow); // If we're debugging, make a fuss. But it's still worth checking this in the following if statement.
if (m_quotaSize != noQuota && (overflow || overQuota)) {
quotaException = true;
return 0;
@@ -143,10 +152,11 @@ PassRefPtr<StorageMap> StorageMap::removeItem(const String& key, String& oldValu
}
oldValue = m_map.take(key);
- if (!oldValue.isNull())
+ if (!oldValue.isNull()) {
invalidateIterator();
-
- // Update quota.
+ ASSERT(m_currentLength - key.length() <= m_currentLength);
+ m_currentLength -= key.length();
+ }
ASSERT(m_currentLength - oldValue.length() <= m_currentLength);
m_currentLength -= oldValue.length();
@@ -162,12 +172,11 @@ 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(), String());
-
- if (result.second)
- result.first->second = value.threadsafeCopy();
+ 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.
- // Update quota.
+ ASSERT(m_currentLength + key.length() >= m_currentLength);
+ m_currentLength += key.length();
ASSERT(m_currentLength + value.length() >= m_currentLength);
m_currentLength += value.length();
}
diff --git a/WebCore/storage/StorageSyncManager.cpp b/WebCore/storage/StorageSyncManager.cpp
index f9276dd..d9641b7 100644
--- a/WebCore/storage/StorageSyncManager.cpp
+++ b/WebCore/storage/StorageSyncManager.cpp
@@ -48,28 +48,29 @@ PassRefPtr<StorageSyncManager> StorageSyncManager::create(const String& path)
}
StorageSyncManager::StorageSyncManager(const String& path)
- : m_path(path.crossThreadString())
+ : m_thread(LocalStorageThread::create())
+ , m_path(path.crossThreadString())
{
ASSERT(isMainThread());
ASSERT(!m_path.isEmpty());
- m_thread = LocalStorageThread::create();
m_thread->start();
}
StorageSyncManager::~StorageSyncManager()
{
ASSERT(isMainThread());
+ ASSERT(!m_thread);
}
-String StorageSyncManager::fullDatabaseFilename(SecurityOrigin* origin)
+// Called on a background thread.
+String StorageSyncManager::fullDatabaseFilename(const String& databaseIdentifier)
{
- ASSERT(origin);
if (!makeAllDirectories(m_path)) {
LOG_ERROR("Unabled to create LocalStorage database path %s", m_path.utf8().data());
return String();
}
- return pathByAppendingComponent(m_path, origin->databaseIdentifier() + ".localstorage");
+ return pathByAppendingComponent(m_path, databaseIdentifier + ".localstorage");
}
void StorageSyncManager::close()
@@ -85,19 +86,18 @@ void StorageSyncManager::close()
bool StorageSyncManager::scheduleImport(PassRefPtr<StorageAreaSync> area)
{
ASSERT(isMainThread());
-
+ ASSERT(m_thread);
if (m_thread)
- m_thread->scheduleImport(area.get());
-
+ 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->scheduleSync(area.get());
+ m_thread->scheduleTask(LocalStorageTask::createSync(area.get()));
}
} // namespace WebCore
diff --git a/WebCore/storage/StorageSyncManager.h b/WebCore/storage/StorageSyncManager.h
index fe35e3d..e2dfa76 100644
--- a/WebCore/storage/StorageSyncManager.h
+++ b/WebCore/storage/StorageSyncManager.h
@@ -32,7 +32,7 @@
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
-#include <wtf/RefPtr.h>
+#include <wtf/OwnPtr.h>
namespace WebCore {
@@ -53,12 +53,12 @@ namespace WebCore {
private:
StorageSyncManager(const String& path);
- RefPtr<LocalStorageThread> m_thread;
+ OwnPtr<LocalStorageThread> m_thread;
// The following members are subject to thread synchronization issues
public:
// To be called from the background thread:
- String fullDatabaseFilename(SecurityOrigin*);
+ String fullDatabaseFilename(const String& databaseIdentifier);
private:
String m_path;
diff --git a/WebCore/storage/chromium/DatabaseObserver.h b/WebCore/storage/chromium/DatabaseObserver.h
new file mode 100644
index 0000000..535c0d2
--- /dev/null
+++ b/WebCore/storage/chromium/DatabaseObserver.h
@@ -0,0 +1,49 @@
+/*
+ * 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 DatabaseObserver_h
+#define DatabaseObserver_h
+
+namespace WebCore {
+
+class Database;
+
+// The implementation of this class is in the WebKit API (Chromium source tree)
+// in webkit/api/src/DatabaseObserver.cpp.
+class DatabaseObserver {
+public:
+ static void databaseOpened(Database*);
+ static void databaseModified(Database*);
+ static void databaseClosed(Database*);
+};
+
+}
+
+#endif // DatabaseObserver_h
diff --git a/WebCore/storage/chromium/DatabaseTrackerChromium.cpp b/WebCore/storage/chromium/DatabaseTrackerChromium.cpp
new file mode 100644
index 0000000..e4b8178
--- /dev/null
+++ b/WebCore/storage/chromium/DatabaseTrackerChromium.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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 "DatabaseTracker.h"
+
+#include "CString.h"
+#include "Database.h"
+#include "DatabaseObserver.h"
+#include "DatabaseThread.h"
+#include "Document.h"
+#include "QuotaTracker.h"
+#include "SecurityOrigin.h"
+#include "SQLiteFileSystem.h"
+#include <wtf/HashSet.h>
+#include <wtf/StdLibExtras.h>
+
+namespace WebCore {
+
+DatabaseTracker& DatabaseTracker::tracker()
+{
+ DEFINE_STATIC_LOCAL(DatabaseTracker, tracker, ());
+ return tracker;
+}
+
+DatabaseTracker::DatabaseTracker()
+{
+ SQLiteFileSystem::registerSQLiteVFS();
+}
+
+bool DatabaseTracker::canEstablishDatabase(Document*, const String&, const String&, unsigned long)
+{
+ // In Chromium, a database can always be established (even though we might not
+ // be able to write anything to it if the quota for this origin was exceeded)
+ return true;
+}
+
+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(Database* database)
+{
+ ASSERT(isMainThread());
+ DatabaseObserver::databaseOpened(database);
+}
+
+void DatabaseTracker::removeOpenDatabase(Database* database)
+{
+ // FIXME: once we know how to use this information, figure out
+ // how to get this method called on the main thread
+ //ASSERT(isMainThread());
+ //DatabaseObserver::databaseClosed(database);
+}
+
+unsigned long long DatabaseTracker::getMaxSizeForDatabase(const Database* database)
+{
+ ASSERT(currentThread() == database->document()->databaseThread()->getThreadID());
+ unsigned long long spaceAvailable = 0;
+ unsigned long long databaseSize = 0;
+ QuotaTracker::instance().getDatabaseSizeAndSpaceAvailableToOrigin(
+ database->securityOrigin()->databaseIdentifier(),
+ database->stringIdentifier(), &databaseSize, &spaceAvailable);
+ return databaseSize + spaceAvailable;
+}
+
+}
diff --git a/WebCore/storage/chromium/QuotaTracker.cpp b/WebCore/storage/chromium/QuotaTracker.cpp
new file mode 100644
index 0000000..b49639d
--- /dev/null
+++ b/WebCore/storage/chromium/QuotaTracker.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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"
+
+#include "CString.h"
+#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);
+}
+
+}
diff --git a/WebCore/storage/chromium/QuotaTracker.h b/WebCore/storage/chromium/QuotaTracker.h
new file mode 100644
index 0000000..9146910
--- /dev/null
+++ b/WebCore/storage/chromium/QuotaTracker.h
@@ -0,0 +1,63 @@
+/*
+ * 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
+
+#include "CString.h"
+#include "SecurityOrigin.h"
+#include "StringHash.h"
+#include <wtf/HashMap.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 // QuotaTracker_h
diff --git a/WebCore/storage/chromium/SQLTransactionClientChromium.cpp b/WebCore/storage/chromium/SQLTransactionClientChromium.cpp
new file mode 100644
index 0000000..46d73ba
--- /dev/null
+++ b/WebCore/storage/chromium/SQLTransactionClientChromium.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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"
+
+#include "Database.h"
+#include "DatabaseObserver.h"
+#include "DatabaseThread.h"
+#include "Document.h"
+#include "SQLTransaction.h"
+#include <wtf/MainThread.h>
+
+static void notifyDatabaseChanged(void* context) {
+ WebCore::Database* database = static_cast<WebCore::Database*>(context);
+ WebCore::DatabaseObserver::databaseModified(database);
+ database->deref(); // ref()'d in didCommitTransaction()
+}
+
+namespace WebCore {
+
+void SQLTransactionClient::didCommitTransaction(SQLTransaction* transaction)
+{
+ ASSERT(currentThread() == transaction->database()->document()->databaseThread()->getThreadID());
+ if (!transaction->isReadOnly()) {
+ transaction->database()->ref(); // deref()'d in notifyDatabaseChanged()
+ callOnMainThread(notifyDatabaseChanged, transaction->database());
+ }
+}
+
+void SQLTransactionClient::didExecuteStatement(SQLTransaction* transaction)
+{
+ // This method is called after executing every statement that changes the DB.
+ // Chromium doesn't need to do anything at that point.
+ ASSERT(currentThread() == transaction->database()->document()->databaseThread()->getThreadID());
+}
+
+bool SQLTransactionClient::didExceedQuota(SQLTransaction*)
+{
+ // Chromium does not allow users to manually change the quota for an origin (for now, at least).
+ // Don't do anything.
+ ASSERT(isMainThread());
+ return false;
+}
+
+}