diff options
Diffstat (limited to 'WebCore/storage')
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; +} + +} |