diff options
Diffstat (limited to 'WebCore/storage')
74 files changed, 2724 insertions, 866 deletions
diff --git a/WebCore/storage/ChangeVersionWrapper.cpp b/WebCore/storage/ChangeVersionWrapper.cpp index a2be615..17a9407 100644 --- a/WebCore/storage/ChangeVersionWrapper.cpp +++ b/WebCore/storage/ChangeVersionWrapper.cpp @@ -34,29 +34,29 @@ namespace WebCore { ChangeVersionWrapper::ChangeVersionWrapper(const String& oldVersion, const String& newVersion) - : m_oldVersion(oldVersion.copy()) - , m_newVersion(newVersion.copy()) + : m_oldVersion(oldVersion.crossThreadString()) + , m_newVersion(newVersion.crossThreadString()) { } bool ChangeVersionWrapper::performPreflight(SQLTransaction* transaction) { ASSERT(transaction && transaction->database()); - + String actualVersion; - + if (!transaction->database()->getVersionFromDatabase(actualVersion)) { LOG_ERROR("Unable to retrieve actual current version from database"); m_sqlError = SQLError::create(0, "unable to verify current version of database"); return false; } - + if (actualVersion != m_oldVersion) { LOG_ERROR("Old version doesn't match actual version"); m_sqlError = SQLError::create(2, "current version of the database and `oldVersion` argument do not match"); return false; } - + return true; } @@ -71,10 +71,10 @@ bool ChangeVersionWrapper::performPostflight(SQLTransaction* transaction) } transaction->database()->setExpectedVersion(m_newVersion); - + return true; } - + } // namespace WebCore #endif // ENABLE(DATABASE) diff --git a/WebCore/storage/Database.cpp b/WebCore/storage/Database.cpp index 5504cb1..2130631 100644 --- a/WebCore/storage/Database.cpp +++ b/WebCore/storage/Database.cpp @@ -46,18 +46,18 @@ #include "NotImplemented.h" #include "Page.h" #include "OriginQuotaManager.h" +#include "ScriptController.h" #include "SQLiteDatabase.h" #include "SQLiteFileSystem.h" #include "SQLiteStatement.h" #include "SQLResultSet.h" -#include <wtf/MainThread.h> -#endif +#include "SQLTransactionClient.h" +#include "SQLTransactionCoordinator.h" + +#endif // ENABLE(DATABASE) #if USE(JSC) #include "JSDOMWindow.h" -#include <runtime/InitializeThreading.h> -#elif USE(V8) -#include "InitializeThreading.h" #endif namespace WebCore { @@ -73,6 +73,18 @@ const String& Database::databaseInfoTableName() #if ENABLE(DATABASE) +static bool isDatabaseAvailable = true; + +void Database::setIsAvailable(bool available) +{ + isDatabaseAvailable = available; +} + +bool Database::isAvailable() +{ + return isDatabaseAvailable; +} + static Mutex& guidMutex() { // Note: We don't have to use AtomicallyInitializedStatic here because @@ -89,6 +101,22 @@ static GuidVersionMap& guidToVersionMap() return map; } +// NOTE: Caller must lock guidMutex(). +static inline void updateGuidVersionMap(int guid, String newVersion) +{ + // Ensure the the mutex is locked. + ASSERT(!guidMutex().tryLock()); + + // Note: It is not safe to put an empty string into the guidToVersionMap() map. + // That's because the map is cross-thread, but empty strings are per-thread. + // The copy() function makes a version of the string you can use on the current + // thread, but we need a string we can keep in a cross-thread data structure. + // FIXME: This is a quite-awkward restriction to have to program with. + + // Map null string to empty string (see comment above). + guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.threadsafeCopy()); +} + typedef HashMap<int, HashSet<Database*>*> GuidDatabaseMap; static GuidDatabaseMap& guidToDatabaseMap() { @@ -104,57 +132,59 @@ static const String& databaseVersionKey() static int guidForOriginAndName(const String& origin, const String& name); -PassRefPtr<Database> Database::openDatabase(Document* document, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, ExceptionCode& e) +PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, ExceptionCode& e) { - if (!DatabaseTracker::tracker().canEstablishDatabase(document, name, displayName, estimatedSize)) { + if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) { // FIXME: There should be an exception raised here in addition to returning a null Database object. The question has been raised with the WHATWG. - LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), document->securityOrigin()->toString().ascii().data()); + LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data()); return 0; } - - RefPtr<Database> database = adoptRef(new Database(document, name, expectedVersion)); + + RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize)); if (!database->openAndVerifyVersion(e)) { - LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data()); - return 0; + LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data()); + context->removeOpenDatabase(database.get()); + DatabaseTracker::tracker().removeOpenDatabase(database.get()); + return 0; } - - DatabaseTracker::tracker().setDatabaseDetails(document->securityOrigin(), name, displayName, estimatedSize); - document->setHasOpenDatabases(); + DatabaseTracker::tracker().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize); - if (Page* page = document->frame()->page()) - page->inspectorController()->didOpenDatabase(database.get(), document->securityOrigin()->host(), name, expectedVersion); + context->setHasOpenDatabases(); +#if ENABLE(INSPECTOR) + if (context->isDocument()) { + Document* document = static_cast<Document*>(context); + if (Page* page = document->page()) + page->inspectorController()->didOpenDatabase(database.get(), context->securityOrigin()->host(), name, expectedVersion); + } +#endif return database; } -Database::Database(Document* document, const String& name, const String& expectedVersion) +Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) : m_transactionInProgress(false) - , m_document(document) - , m_name(name.copy()) + , m_isTransactionQueueEnabled(true) + , m_scriptExecutionContext(context) + , m_name(name.crossThreadString()) , m_guid(0) - , m_expectedVersion(expectedVersion) + , m_expectedVersion(expectedVersion.crossThreadString()) + , m_displayName(displayName.crossThreadString()) + , m_estimatedSize(estimatedSize) , m_deleted(false) , m_stopped(false) , m_opened(false) { - ASSERT(document); - m_securityOrigin = document->securityOrigin(); - + ASSERT(m_scriptExecutionContext.get()); + m_mainThreadSecurityOrigin = m_scriptExecutionContext->securityOrigin(); + m_databaseThreadSecurityOrigin = m_mainThreadSecurityOrigin->threadsafeCopy(); if (m_name.isNull()) m_name = ""; -#if USE(JSC) - JSC::initializeThreading(); - // Database code violates the normal JSCore contract by calling jsUnprotect from a secondary thread, and thus needs additional locking. - JSDOMWindow::commonJSGlobalData()->heap.setGCProtectNeedsLocking(); -#elif USE(V8) - // TODO(benm): do we need the extra locking in V8 too? (See JSC comment above) - V8::initializeThreading(); -#endif + ScriptController::initializeThreading(); - m_guid = guidForOriginAndName(m_securityOrigin->toString(), name); + m_guid = guidForOriginAndName(securityOrigin()->toString(), name); { MutexLocker locker(guidMutex()); @@ -168,52 +198,51 @@ Database::Database(Document* document, const String& name, const String& expecte hashSet->add(this); } - ASSERT(m_document->databaseThread()); - - m_filename = DatabaseTracker::tracker().fullPathForDatabase(m_securityOrigin.get(), m_name); + ASSERT(m_scriptExecutionContext->databaseThread()); + m_filename = DatabaseTracker::tracker().fullPathForDatabase(securityOrigin(), m_name); DatabaseTracker::tracker().addOpenDatabase(this); - m_document->addOpenDatabase(this); + context->addOpenDatabase(this); } -Database::~Database() -{ +class DerefContextTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<DerefContextTask> create() { - MutexLocker locker(guidMutex()); + return new DerefContextTask(); + } - HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid); - ASSERT(hashSet); - ASSERT(hashSet->contains(this)); - hashSet->remove(this); - if (hashSet->isEmpty()) { - guidToDatabaseMap().remove(m_guid); - delete hashSet; - guidToVersionMap().remove(m_guid); - } + virtual void performTask(ScriptExecutionContext* context) + { + context->deref(); } - if (m_document->databaseThread()) - m_document->databaseThread()->unscheduleDatabaseTasks(this); + virtual bool isCleanupTask() const { return true; } +}; - DatabaseTracker::tracker().removeOpenDatabase(this); - m_document->removeOpenDatabase(this); +Database::~Database() +{ + // The reference to the ScriptExecutionContext needs to be cleared on the JavaScript thread. If we're on that thread already, we can just let the RefPtr's destruction do the dereffing. + if (!m_scriptExecutionContext->isContextThread()) { + m_scriptExecutionContext->postTask(DerefContextTask::create()); + m_scriptExecutionContext.release().releaseRef(); + } } bool Database::openAndVerifyVersion(ExceptionCode& e) { - if (!m_document->databaseThread()) + if (!m_scriptExecutionContext->databaseThread()) 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_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); + synchronizer.waitForTaskCompletion(); - ASSERT(task->isComplete()); - e = task->exceptionCode(); - return task->openSuccessful(); + return success; } @@ -246,7 +275,7 @@ bool Database::getVersionFromDatabase(String& version) m_databaseAuthorizer->disable(); - bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQuery.copy(), version); + bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQuery.threadsafeCopy(), version); if (!result) LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data()); @@ -278,11 +307,13 @@ static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, cons bool Database::setVersionInDatabase(const String& version) { + // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE + // clause in the CREATE statement (see Database::performOpenAndVerify()). DEFINE_STATIC_LOCAL(String, setVersionQuery, ("INSERT INTO " + databaseInfoTableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);")); m_databaseAuthorizer->disable(); - bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.copy(), version); + bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.threadsafeCopy(), version); if (!result) LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), setVersionQuery.ascii().data()); @@ -297,41 +328,86 @@ bool Database::versionMatchesExpected() const MutexLocker locker(guidMutex()); return m_expectedVersion == guidToVersionMap().get(m_guid); } - + return true; } void Database::markAsDeletedAndClose() { - if (m_deleted || !m_document->databaseThread()) + if (m_deleted || !m_scriptExecutionContext->databaseThread()) return; LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this); m_deleted = true; - if (m_document->databaseThread()->terminationRequested()) { + if (m_scriptExecutionContext->databaseThread()->terminationRequested()) { LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this); return; } - m_document->databaseThread()->unscheduleDatabaseTasks(this); + m_scriptExecutionContext->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_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); + synchronizer.waitForTaskCompletion(); } +class ContextRemoveOpenDatabaseTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<ContextRemoveOpenDatabaseTask> create(PassRefPtr<Database> database) + { + return new ContextRemoveOpenDatabaseTask(database); + } + + virtual void performTask(ScriptExecutionContext* context) + { + context->removeOpenDatabase(m_database.get()); + DatabaseTracker::tracker().removeOpenDatabase(m_database.get()); + } + + virtual bool isCleanupTask() const { return true; } + +private: + ContextRemoveOpenDatabaseTask(PassRefPtr<Database> database) + : m_database(database) + { + } + + RefPtr<Database> m_database; +}; + void Database::close() { - if (m_opened) { - ASSERT(m_document->databaseThread()); - ASSERT(currentThread() == document()->databaseThread()->getThreadID()); - m_sqliteDatabase.close(); - m_document->databaseThread()->recordDatabaseClosed(this); - m_opened = false; + RefPtr<Database> protect = this; + + if (!m_opened) + return; + + ASSERT(m_scriptExecutionContext->databaseThread()); + ASSERT(currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID()); + m_sqliteDatabase.close(); + // Must ref() before calling databaseThread()->recordDatabaseClosed(). + m_scriptExecutionContext->databaseThread()->recordDatabaseClosed(this); + m_opened = false; + + { + MutexLocker locker(guidMutex()); + + HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid); + ASSERT(hashSet); + ASSERT(hashSet->contains(this)); + hashSet->remove(this); + if (hashSet->isEmpty()) { + guidToDatabaseMap().remove(m_guid); + delete hashSet; + guidToVersionMap().remove(m_guid); + } } + + m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this); + m_scriptExecutionContext->postTask(ContextRemoveOpenDatabaseTask::create(this)); } void Database::stop() @@ -339,32 +415,21 @@ void Database::stop() // FIXME: The net effect of the following code is to remove all pending transactions and statements, but allow the current statement // to run to completion. In the future we can use the sqlite3_progress_handler or sqlite3_interrupt interfaces to cancel the current // statement in response to close(), as well. - - // This method is meant to be used as an analog to cancelling a loader, and is used when a document is shut down as the result of + + // This method is meant to be used as an analog to cancelling a loader, and is used when a document is shut down as the result of // a page load or closing the page m_stopped = true; { MutexLocker locker(m_transactionInProgressMutex); - m_transactionQueue.kill(); + m_isTransactionQueueEnabled = false; m_transactionInProgress = false; } } -unsigned long long Database::databaseSize() const -{ - return SQLiteFileSystem::getDatabaseFileSize(m_filename); -} - unsigned long long Database::maximumSize() const { - // The maximum size for this database is the full quota for this origin, minus the current usage within this origin, - // except for the current usage of this database - - OriginQuotaManager& manager(DatabaseTracker::tracker().originQuotaManager()); - Locker<OriginQuotaManager> locker(manager); - - return DatabaseTracker::tracker().quotaForOrigin(m_securityOrigin.get()) - manager.diskUsage(m_securityOrigin.get()) + databaseSize(); + return DatabaseTracker::tracker().getMaxSizeForDatabase(this); } void Database::disableAuthorizer() @@ -438,42 +503,37 @@ bool Database::performOpenAndVerify(ExceptionCode& e) return false; } - m_opened = true; - if (m_document->databaseThread()) - m_document->databaseThread()->recordDatabaseOpen(this); - ASSERT(m_databaseAuthorizer); m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); - if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) { - if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseInfoTableName() + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { - LOG_ERROR("Unable to create table %s in database %s", databaseInfoTableName().ascii().data(), databaseDebugName().ascii().data()); - e = INVALID_STATE_ERR; - return false; - } - } - String currentVersion; { MutexLocker locker(guidMutex()); - // Note: It is not safe to put an empty string into the guidToVersionMap() map. - // That's because the map is cross-thread, but empty strings are per-thread. - // The copy() function makes a version of the string you can use on the current - // thread, but we need a string we can keep in a cross-thread data structure. - // FIXME: This is a quite-awkward restriction to have to program with. - GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); if (entry != guidToVersionMap().end()) { - // Map null string to empty string (see comment above). + // Map null string to empty string (see updateGuidVersionMap()). currentVersion = entry->second.isNull() ? String("") : entry->second; LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); } else { LOG(StorageAPI, "No cached version for guid %i", m_guid); + + if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) { + if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + databaseInfoTableName() + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { + LOG_ERROR("Unable to create table %s in database %s", databaseInfoTableName().ascii().data(), databaseDebugName().ascii().data()); + e = INVALID_STATE_ERR; + // Close the handle to the database file. + m_sqliteDatabase.close(); + return false; + } + } + if (!getVersionFromDatabase(currentVersion)) { LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data()); e = INVALID_STATE_ERR; + // Close the handle to the database file. + m_sqliteDatabase.close(); return false; } if (currentVersion.length()) { @@ -483,13 +543,14 @@ bool Database::performOpenAndVerify(ExceptionCode& e) if (!setVersionInDatabase(m_expectedVersion)) { LOG_ERROR("Failed to set version %s in database %s", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); e = INVALID_STATE_ERR; + // Close the handle to the database file. + m_sqliteDatabase.close(); return false; } currentVersion = m_expectedVersion; } - // Map empty string to null string (see comment above). - guidToVersionMap().set(m_guid, currentVersion.isEmpty() ? String() : currentVersion.copy()); + updateGuidVersionMap(m_guid, currentVersion); } } @@ -498,21 +559,27 @@ 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; + // Close the handle to the database file. + m_sqliteDatabase.close(); + return false; } + // All checks passed and we still have a handle to this database file. + // Make sure DatabaseThread closes it when DatabaseThread goes away. + m_opened = true; + if (m_scriptExecutionContext->databaseThread()) + m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this); + return true; } -void Database::changeVersion(const String& oldVersion, const String& newVersion, +void Database::changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback) { @@ -523,9 +590,9 @@ void Database::changeVersion(const String& oldVersion, const String& newVersion, } void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, - PassRefPtr<VoidCallback> successCallback) + PassRefPtr<VoidCallback> successCallback, bool readOnly) { - m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, 0)); + m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly)); MutexLocker locker(m_transactionInProgressMutex); if (!m_transactionInProgress) scheduleTransaction(); @@ -535,28 +602,58 @@ 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_scriptExecutionContext->databaseThread()) { + OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction()); m_transactionInProgress = true; - m_document->databaseThread()->scheduleTask(task.release()); + m_scriptExecutionContext->databaseThread()->scheduleTask(task.release()); } else m_transactionInProgress = false; } -void Database::scheduleTransactionStep(SQLTransaction* transaction) +void Database::scheduleTransactionStep(SQLTransaction* transaction, bool immediately) { - if (m_document->databaseThread()) { - RefPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); - LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get()); - m_document->databaseThread()->scheduleTask(task.release()); - } + if (!m_scriptExecutionContext->databaseThread()) + return; + + OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); + LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get()); + if (immediately) + m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); + else + m_scriptExecutionContext->databaseThread()->scheduleTask(task.release()); } +class DeliverPendingCallbackTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction) + { + return new DeliverPendingCallbackTask(transaction); + } + + virtual void performTask(ScriptExecutionContext*) + { + m_transaction->performPendingCallback(); + } + +private: + DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction) + : m_transaction(transaction) + { + } + + RefPtr<SQLTransaction> m_transaction; +}; + void Database::scheduleTransactionCallback(SQLTransaction* transaction) { - transaction->ref(); - callOnMainThread(deliverPendingCallback, transaction); + m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction)); } Vector<String> Database::performGetTableNames() @@ -588,50 +685,81 @@ Vector<String> Database::performGetTableNames() return tableNames; } +SQLTransactionClient* Database::transactionClient() const +{ + return m_scriptExecutionContext->databaseThread()->transactionClient(); +} + +SQLTransactionCoordinator* Database::transactionCoordinator() const +{ + return m_scriptExecutionContext->databaseThread()->transactionCoordinator(); +} + String Database::version() const { if (m_deleted) return String(); MutexLocker locker(guidMutex()); - return guidToVersionMap().get(m_guid).copy(); -} - -void Database::deliverPendingCallback(void* context) -{ - SQLTransaction* transaction = static_cast<SQLTransaction*>(context); - transaction->performPendingCallback(); - transaction->deref(); // Was ref'd in scheduleTransactionCallback(). + return guidToVersionMap().get(m_guid).threadsafeCopy(); } Vector<String> Database::tableNames() { - if (!m_document->databaseThread()) - return Vector<String>(); - RefPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this); + // 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_scriptExecutionContext->databaseThread()) + return result; - task->lockForSynchronousScheduling(); - m_document->databaseThread()->scheduleImmediateTask(task); - task->waitForSynchronousCompletion(); + DatabaseTaskSynchronizer synchronizer; + OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result); - return task->tableNames(); + m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release()); + synchronizer.waitForTaskCompletion(); + + return result; } void Database::setExpectedVersion(const String& version) { - m_expectedVersion = version.copy(); + m_expectedVersion = version.threadsafeCopy(); + // Update the in memory database version map. + MutexLocker locker(guidMutex()); + updateGuidVersionMap(m_guid, version); } -PassRefPtr<SecurityOrigin> Database::securityOriginCopy() const +SecurityOrigin* Database::securityOrigin() const { - return m_securityOrigin->copy(); + if (scriptExecutionContext()->isContextThread()) + return m_mainThreadSecurityOrigin.get(); + if (currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID()) + return m_databaseThreadSecurityOrigin.get(); + return 0; } String Database::stringIdentifier() const { // Return a deep copy for ref counting thread safety - return m_name.copy(); + return m_name.threadsafeCopy(); } -#endif +String Database::displayName() const +{ + // Return a deep copy for ref counting thread safety + return m_displayName.threadsafeCopy(); +} + +unsigned long Database::estimatedSize() const +{ + return m_estimatedSize; +} +String Database::fileName() const +{ + // Return a deep copy for ref counting thread safety + return m_filename.threadsafeCopy(); } + +#endif // ENABLE(DATABASE) + +} // namespace WebCore diff --git a/WebCore/storage/Database.h b/WebCore/storage/Database.h index 0bdb37b..0d7f33c 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" @@ -41,7 +40,6 @@ #include <wtf/Forward.h> #include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Deque.h> @@ -54,12 +52,14 @@ namespace WebCore { class DatabaseAuthorizer; class DatabaseThread; -class Document; +class ScriptExecutionContext; class SQLResultSet; class SQLTransactionCallback; +class SQLTransactionClient; +class SQLTransactionCoordinator; class SQLTransactionErrorCallback; class SQLValue; - + typedef int ExceptionCode; class Database : public ThreadSafeShared<Database> { @@ -67,17 +67,20 @@ class Database : public ThreadSafeShared<Database> { friend class SQLStatement; friend class SQLTransaction; public: + static void setIsAvailable(bool); + static bool isAvailable(); + ~Database(); // Direct support for the DOM API - static PassRefPtr<Database> openDatabase(Document* document, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, ExceptionCode&); + static PassRefPtr<Database> openDatabase(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, ExceptionCode&); String version() const; - void changeVersion(const String& oldVersion, const String& newVersion, + void changeVersion(const String& oldVersion, const String& newVersion, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback); void transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, - PassRefPtr<VoidCallback> successCallback); - + PassRefPtr<VoidCallback> successCallback, bool readOnly); + // Internal engine support static const String& databaseInfoTableName(); @@ -87,10 +90,13 @@ public: Vector<String> tableNames(); - Document* document() const { return m_document.get(); } - PassRefPtr<SecurityOrigin> securityOriginCopy() const; + ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext.get(); } + SecurityOrigin* securityOrigin() const; String stringIdentifier() const; - + String displayName() const; + unsigned long estimatedSize() const; + String fileName() const; + bool getVersionFromDatabase(String&); bool setVersionInDatabase(const String&); void setExpectedVersion(const String&); @@ -101,7 +107,7 @@ public: void close(); bool opened() const { return m_opened; } - + void stop(); bool stopped() const { return m_stopped; } @@ -116,30 +122,39 @@ public: Vector<String> performGetTableNames(); + SQLTransactionClient* transactionClient() const; + SQLTransactionCoordinator* transactionCoordinator() const; + private: - Database(Document* document, const String& name, const String& expectedVersion); + Database(ScriptExecutionContext* context, const String& name, + const String& expectedVersion, const String& displayName, + unsigned long estimatedSize); bool openAndVerifyVersion(ExceptionCode&); void scheduleTransaction(); void scheduleTransactionCallback(SQLTransaction*); - void scheduleTransactionStep(SQLTransaction* transaction); - - MessageQueue<RefPtr<SQLTransaction> > m_transactionQueue; + void scheduleTransactionStep(SQLTransaction* transaction, bool immediately = false); + + Deque<RefPtr<SQLTransaction> > m_transactionQueue; Mutex m_transactionInProgressMutex; bool m_transactionInProgress; + bool m_isTransactionQueueEnabled; static void deliverPendingCallback(void*); - RefPtr<Document> m_document; - RefPtr<SecurityOrigin> m_securityOrigin; + RefPtr<ScriptExecutionContext> m_scriptExecutionContext; + RefPtr<SecurityOrigin> m_mainThreadSecurityOrigin; + RefPtr<SecurityOrigin> m_databaseThreadSecurityOrigin; String m_name; int m_guid; String m_expectedVersion; + String m_displayName; + unsigned long m_estimatedSize; String m_filename; bool m_deleted; - + bool m_stopped; bool m_opened; @@ -148,7 +163,7 @@ private: RefPtr<DatabaseAuthorizer> m_databaseAuthorizer; #ifndef NDEBUG - String databaseDebugName() const { return m_securityOrigin->toString() + "::" + m_name; } + String databaseDebugName() const { return m_mainThreadSecurityOrigin->toString() + "::" + m_name; } #endif }; diff --git a/WebCore/storage/Database.idl b/WebCore/storage/Database.idl index 1e4b316..c8a537c 100644 --- a/WebCore/storage/Database.idl +++ b/WebCore/storage/Database.idl @@ -29,11 +29,13 @@ module storage { interface [ - Conditional=DATABASE + Conditional=DATABASE, + OmitConstructor ] Database { readonly attribute DOMString version; [Custom] void changeVersion(in DOMString oldVersion, in DOMString newVersion, in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback, in VoidCallback successCallback); [Custom] void transaction(in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback, in VoidCallback successCallback); + [Custom] void readTransaction(in SQLTransactionCallback callback, in SQLTransactionErrorCallback errorCallback, in VoidCallback successCallback); }; } diff --git a/WebCore/storage/DatabaseAuthorizer.cpp b/WebCore/storage/DatabaseAuthorizer.cpp index 2d182ce..d87d4d9 100644 --- a/WebCore/storage/DatabaseAuthorizer.cpp +++ b/WebCore/storage/DatabaseAuthorizer.cpp @@ -38,6 +38,7 @@ DatabaseAuthorizer::DatabaseAuthorizer() : m_securityEnabled(false) { reset(); + addWhitelistedFunctions(); } void DatabaseAuthorizer::reset() @@ -47,6 +48,69 @@ void DatabaseAuthorizer::reset() m_readOnly = false; } +void DatabaseAuthorizer::addWhitelistedFunctions() +{ + // SQLite functions used to help implement some operations + // ALTER TABLE helpers + m_whitelistedFunctions.add("sqlite_rename_table"); + m_whitelistedFunctions.add("sqlite_rename_trigger"); + // GLOB helpers + m_whitelistedFunctions.add("glob"); + + // SQLite core functions + m_whitelistedFunctions.add("abs"); + m_whitelistedFunctions.add("changes"); + m_whitelistedFunctions.add("coalesce"); + m_whitelistedFunctions.add("glob"); + m_whitelistedFunctions.add("ifnull"); + m_whitelistedFunctions.add("hex"); + m_whitelistedFunctions.add("last_insert_rowid"); + m_whitelistedFunctions.add("length"); + m_whitelistedFunctions.add("like"); + m_whitelistedFunctions.add("lower"); + m_whitelistedFunctions.add("ltrim"); + m_whitelistedFunctions.add("max"); + m_whitelistedFunctions.add("min"); + m_whitelistedFunctions.add("nullif"); + m_whitelistedFunctions.add("quote"); + m_whitelistedFunctions.add("replace"); + m_whitelistedFunctions.add("round"); + m_whitelistedFunctions.add("rtrim"); + m_whitelistedFunctions.add("soundex"); + m_whitelistedFunctions.add("sqlite_source_id"); + m_whitelistedFunctions.add("sqlite_version"); + m_whitelistedFunctions.add("substr"); + m_whitelistedFunctions.add("total_changes"); + m_whitelistedFunctions.add("trim"); + m_whitelistedFunctions.add("typeof"); + m_whitelistedFunctions.add("upper"); + m_whitelistedFunctions.add("zeroblob"); + + // SQLite date and time functions + m_whitelistedFunctions.add("date"); + m_whitelistedFunctions.add("time"); + m_whitelistedFunctions.add("datetime"); + m_whitelistedFunctions.add("julianday"); + m_whitelistedFunctions.add("strftime"); + + // SQLite aggregate functions + // max() and min() are already in the list + m_whitelistedFunctions.add("avg"); + m_whitelistedFunctions.add("count"); + m_whitelistedFunctions.add("group_concat"); + m_whitelistedFunctions.add("sum"); + m_whitelistedFunctions.add("total"); + + // SQLite FTS functions + m_whitelistedFunctions.add("snippet"); + m_whitelistedFunctions.add("offsets"); + m_whitelistedFunctions.add("optimize"); + + // SQLite ICU functions + // like(), lower() and upper() are already in the list + m_whitelistedFunctions.add("regexp"); +} + int DatabaseAuthorizer::createTable(const String& tableName) { if (m_readOnly && m_securityEnabled) @@ -58,6 +122,12 @@ int DatabaseAuthorizer::createTable(const String& tableName) int DatabaseAuthorizer::createTempTable(const String& tableName) { + // SQLITE_CREATE_TEMP_TABLE results in a UPDATE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_CREATE_TEMP_TABLE in these cases + if (m_readOnly && m_securityEnabled) + return SQLAuthDeny; + return denyBasedOnTableName(tableName); } @@ -71,6 +141,12 @@ int DatabaseAuthorizer::dropTable(const String& tableName) int DatabaseAuthorizer::dropTempTable(const String& tableName) { + // SQLITE_DROP_TEMP_TABLE results in a DELETE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_DROP_TEMP_TABLE in these cases + if (m_readOnly && m_securityEnabled) + return SQLAuthDeny; + return denyBasedOnTableName(tableName); } @@ -94,6 +170,12 @@ int DatabaseAuthorizer::createIndex(const String&, const String& tableName) int DatabaseAuthorizer::createTempIndex(const String&, const String& tableName) { + // SQLITE_CREATE_TEMP_INDEX should result in a UPDATE or INSERT operation, + // which is not allowed in read-only transactions or private browsing, + // so we might as well disallow SQLITE_CREATE_TEMP_INDEX in these cases + if (m_readOnly && m_securityEnabled) + return SQLAuthDeny; + return denyBasedOnTableName(tableName); } @@ -107,6 +189,12 @@ int DatabaseAuthorizer::dropIndex(const String&, const String& tableName) int DatabaseAuthorizer::dropTempIndex(const String&, const String& tableName) { + // SQLITE_DROP_TEMP_INDEX should result in a DELETE operation, which is + // not allowed in read-only transactions or private browsing, so we might + // as well disallow SQLITE_DROP_TEMP_INDEX in these cases + if (m_readOnly && m_securityEnabled) + return SQLAuthDeny; + return denyBasedOnTableName(tableName); } @@ -121,6 +209,12 @@ int DatabaseAuthorizer::createTrigger(const String&, const String& tableName) int DatabaseAuthorizer::createTempTrigger(const String&, const String& tableName) { + // SQLITE_CREATE_TEMP_TRIGGER results in a INSERT operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_CREATE_TEMP_TRIGGER in these cases + if (m_readOnly && m_securityEnabled) + return SQLAuthDeny; + return denyBasedOnTableName(tableName); } @@ -134,9 +228,41 @@ int DatabaseAuthorizer::dropTrigger(const String&, const String& tableName) int DatabaseAuthorizer::dropTempTrigger(const String&, const String& tableName) { + // SQLITE_DROP_TEMP_TRIGGER results in a DELETE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_DROP_TEMP_TRIGGER in these cases + if (m_readOnly && m_securityEnabled) + return SQLAuthDeny; + return denyBasedOnTableName(tableName); } +int DatabaseAuthorizer::createView(const String&) +{ + return (m_readOnly && m_securityEnabled ? SQLAuthDeny : SQLAuthAllow); +} + +int DatabaseAuthorizer::createTempView(const String&) +{ + // SQLITE_CREATE_TEMP_VIEW results in a UPDATE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_CREATE_TEMP_VIEW in these cases + return (m_readOnly && m_securityEnabled ? SQLAuthDeny : SQLAuthAllow); +} + +int DatabaseAuthorizer::dropView(const String&) +{ + return (m_readOnly && m_securityEnabled ? SQLAuthDeny : SQLAuthAllow); +} + +int DatabaseAuthorizer::dropTempView(const String&) +{ + // SQLITE_DROP_TEMP_VIEW results in a DELETE operation, which is not + // allowed in read-only transactions or private browsing, so we might as + // well disallow SQLITE_DROP_TEMP_VIEW in these cases + return (m_readOnly && m_securityEnabled ? SQLAuthDeny : SQLAuthAllow); +} + int DatabaseAuthorizer::createVTable(const String&, const String&) { if (m_readOnly && m_securityEnabled) @@ -191,6 +317,11 @@ int DatabaseAuthorizer::allowRead(const String& tableName, const String&) return denyBasedOnTableName(tableName); } +int DatabaseAuthorizer::allowReindex(const String&) +{ + return (m_readOnly && m_securityEnabled ? SQLAuthDeny : SQLAuthAllow); +} + int DatabaseAuthorizer::allowAnalyze(const String& tableName) { return denyBasedOnTableName(tableName); @@ -211,11 +342,11 @@ int DatabaseAuthorizer::allowDetach(const String&) return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow; } -int DatabaseAuthorizer::allowFunction(const String&) +int DatabaseAuthorizer::allowFunction(const String& functionName) { - // FIXME: Are there any of these we need to prevent? One might guess current_date, current_time, current_timestamp because - // they would violate the "sandbox environment" part of 4.11.3, but scripts can generate the local client side information via - // javascript directly, anyways. Are there any other built-ins we need to be worried about? + if (m_securityEnabled && !m_whitelistedFunctions.contains(functionName)) + return SQLAuthDeny; + return SQLAuthAllow; } diff --git a/WebCore/storage/DatabaseAuthorizer.h b/WebCore/storage/DatabaseAuthorizer.h index e53ea50..037409e 100644 --- a/WebCore/storage/DatabaseAuthorizer.h +++ b/WebCore/storage/DatabaseAuthorizer.h @@ -28,6 +28,8 @@ #ifndef DatabaseAuthorizer_h #define DatabaseAuthorizer_h +#include "StringHash.h" +#include <wtf/HashSet.h> #include <wtf/PassRefPtr.h> #include <wtf/Threading.h> @@ -59,10 +61,10 @@ public: int dropTrigger(const String& triggerName, const String& tableName); int dropTempTrigger(const String& triggerName, const String& tableName); - int createView(const String& /*viewName*/) { return SQLAuthAllow; } - int createTempView(const String& /*viewName*/) { return SQLAuthAllow; } - int dropView(const String& /*viewName*/) { return SQLAuthAllow; } - int dropTempView(const String& /*viewName*/) { return SQLAuthAllow; } + int createView(const String& viewName); + int createTempView(const String& viewName); + int dropView(const String& viewName); + int dropTempView(const String& viewName); int createVTable(const String& tableName, const String& moduleName); int dropVTable(const String& tableName, const String& moduleName); @@ -75,7 +77,7 @@ public: int allowSelect() { return SQLAuthAllow; } int allowRead(const String& tableName, const String& columnName); - int allowReindex(const String& /*indexName*/) { return SQLAuthAllow; } + int allowReindex(const String& indexName); int allowAnalyze(const String& tableName); int allowFunction(const String& functionName); int allowPragma(const String& pragmaName, const String& firstArgument); @@ -94,12 +96,15 @@ public: private: DatabaseAuthorizer(); + void addWhitelistedFunctions(); int denyBasedOnTableName(const String&); bool m_securityEnabled : 1; bool m_lastActionWasInsert : 1; bool m_lastActionChangedDatabase : 1; bool m_readOnly : 1; + + HashSet<String, CaseFoldingHash> m_whitelistedFunctions; }; } // namespace WebCore diff --git a/WebCore/storage/DatabaseDetails.h b/WebCore/storage/DatabaseDetails.h index a4d85fd..9b0f506 100644 --- a/WebCore/storage/DatabaseDetails.h +++ b/WebCore/storage/DatabaseDetails.h @@ -60,7 +60,7 @@ private: String m_name; String m_displayName; unsigned long long m_expectedUsage; - unsigned long long m_currentUsage; + unsigned long long m_currentUsage; }; 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..998e373 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,32 @@ 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 { +public: + DatabaseTaskSynchronizer(); + + // Called from main thread to wait until task is completed. + void waitForTaskCompletion(); + + // Called by the task. + void taskCompleted(); +private: + + bool m_taskCompleted; + Mutex m_synchronousMutex; + ThreadCondition m_synchronousCondition; +}; + +class DatabaseTask : public Noncopyable { friend class Database; public: virtual ~DatabaseTask(); @@ -53,53 +73,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 +126,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 +148,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 b6c9b5d..ec4c6d1 100644 --- a/WebCore/storage/DatabaseThread.cpp +++ b/WebCore/storage/DatabaseThread.cpp @@ -35,11 +35,16 @@ #include "Database.h" #include "DatabaseTask.h" #include "Logging.h" +#include "SQLTransactionClient.h" +#include "SQLTransactionCoordinator.h" namespace WebCore { DatabaseThread::DatabaseThread() : m_threadID(0) + , m_transactionClient(new SQLTransactionClient()) + , m_transactionCoordinator(new SQLTransactionCoordinator()) + , m_cleanupSync(0) { m_selfRef = this; } @@ -47,6 +52,7 @@ DatabaseThread::DatabaseThread() DatabaseThread::~DatabaseThread() { // FIXME: Any cleanup required here? Since the thread deletes itself after running its detached course, I don't think so. Lets be sure. + ASSERT(terminationRequested()); } bool DatabaseThread::start() @@ -61,8 +67,10 @@ bool DatabaseThread::start() return m_threadID; } -void DatabaseThread::requestTermination() +void DatabaseThread::requestTermination(DatabaseTaskSynchronizer *cleanupSync) { + ASSERT(!m_cleanupSync); + m_cleanupSync = cleanupSync; LOG(StorageAPI, "DatabaseThread %p was asked to terminate\n", this); m_queue.kill(); } @@ -87,16 +95,14 @@ 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(); } + // Clean up the list of all pending transactions on this database thread + m_transactionCoordinator->shutdown(); + LOG(StorageAPI, "About to detach thread %i and clear the ref to DatabaseThread %p, which currently has %i ref(s)", m_threadID, this, refCount()); // Close the databases that we ran transactions on. This ensures that if any transactions are still open, they are rolled back and we don't leave the database in an @@ -113,13 +119,18 @@ void* DatabaseThread::databaseThread() // Detach the thread so its resources are no longer of any concern to anyone else detachThread(m_threadID); + DatabaseTaskSynchronizer* cleanupSync = m_cleanupSync; + // Clear the self refptr, possibly resulting in deletion m_selfRef = 0; + if (cleanupSync) // Someone wanted to know when we were done cleaning up. + cleanupSync->taskCompleted(); + return 0; } -void DatabaseThread::recordDatabaseOpen(Database* database) +void DatabaseThread::recordDatabaseOpen(Database* database) { ASSERT(currentThread() == m_threadID); ASSERT(database); @@ -127,7 +138,7 @@ void DatabaseThread::recordDatabaseOpen(Database* database) m_openDatabaseSet.add(database); } -void DatabaseThread::recordDatabaseClosed(Database* database) +void DatabaseThread::recordDatabaseClosed(Database* database) { ASSERT(currentThread() == m_threadID); ASSERT(database); @@ -135,33 +146,30 @@ 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); } +class SameDatabasePredicate { +public: + SameDatabasePredicate(const Database* database) : m_database(database) { } + bool operator()(DatabaseTask* task) const { return task->database() == m_database; } +private: + const Database* m_database; +}; + void DatabaseThread::unscheduleDatabaseTasks(Database* database) { // Note that the thread loop is running, so some tasks for the database // may still be executed. This is unavoidable. - - Deque<RefPtr<DatabaseTask> > filteredReverseQueue; - RefPtr<DatabaseTask> task; - while (m_queue.tryGetMessage(task)) { - if (task->database() != database) - filteredReverseQueue.append(task); - } - - while (!filteredReverseQueue.isEmpty()) { - m_queue.append(filteredReverseQueue.first()); - filteredReverseQueue.removeFirst(); - } + SameDatabasePredicate predicate(database); + m_queue.removeIf(predicate); } - } // namespace WebCore #endif diff --git a/WebCore/storage/DatabaseThread.h b/WebCore/storage/DatabaseThread.h index 5aab5fd..3702619 100644 --- a/WebCore/storage/DatabaseThread.h +++ b/WebCore/storage/DatabaseThread.h @@ -33,6 +33,8 @@ #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/MessageQueue.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Threading.h> @@ -41,7 +43,10 @@ namespace WebCore { class Database; class DatabaseTask; +class DatabaseTaskSynchronizer; class Document; +class SQLTransactionClient; +class SQLTransactionCoordinator; class DatabaseThread : public ThreadSafeShared<DatabaseThread> { public: @@ -49,17 +54,20 @@ public: ~DatabaseThread(); bool start(); - void requestTermination(); + void requestTermination(DatabaseTaskSynchronizer* cleanupSync); 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*); void recordDatabaseClosed(Database*); ThreadIdentifier getThreadID() { return m_threadID; } + SQLTransactionClient* transactionClient() { return m_transactionClient.get(); } + SQLTransactionCoordinator* transactionCoordinator() { return m_transactionCoordinator.get(); } + private: DatabaseThread(); @@ -70,11 +78,15 @@ 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; DatabaseSet m_openDatabaseSet; + + OwnPtr<SQLTransactionClient> m_transactionClient; + OwnPtr<SQLTransactionCoordinator> m_transactionCoordinator; + DatabaseTaskSynchronizer* m_cleanupSync; }; } // namespace WebCore diff --git a/WebCore/storage/DatabaseTracker.cpp b/WebCore/storage/DatabaseTracker.cpp index e7c9485..76492c9 100644 --- a/WebCore/storage/DatabaseTracker.cpp +++ b/WebCore/storage/DatabaseTracker.cpp @@ -31,13 +31,15 @@ #if ENABLE(DATABASE) +#include "Chrome.h" #include "ChromeClient.h" #include "Database.h" +#include "DatabaseThread.h" #include "DatabaseTrackerClient.h" -#include "Document.h" #include "Logging.h" #include "OriginQuotaManager.h" #include "Page.h" +#include "ScriptExecutionContext.h" #include "SecurityOrigin.h" #include "SecurityOriginHash.h" #include "SQLiteFileSystem.h" @@ -118,7 +120,7 @@ void DatabaseTracker::openTrackerDatabase(bool createIfDoesNotExist) } } -bool DatabaseTracker::canEstablishDatabase(Document* document, const String& name, const String& displayName, unsigned long estimatedSize) +bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext* context, const String& name, const String& displayName, unsigned long estimatedSize) { ASSERT(currentThread() == m_thread); @@ -126,12 +128,12 @@ bool DatabaseTracker::canEstablishDatabase(Document* document, const String& nam // can run on the database thread later. populateOrigins(); - SecurityOrigin* origin = document->securityOrigin(); + SecurityOrigin* origin = context->securityOrigin(); - // Since we're imminently opening a database within this Document's origin, make sure this origin is being tracked by the QuotaTracker + // Since we're imminently opening a database within this context's origin, make sure this origin is being tracked by the QuotaTracker // by fetching it's current usage now unsigned long long usage = usageForOrigin(origin); - + // If a database already exists, ignore the passed-in estimated size and say it's OK. if (hasEntryForDatabase(origin, name)) return true; @@ -145,12 +147,9 @@ bool DatabaseTracker::canEstablishDatabase(Document* document, const String& nam // Give the chrome client a chance to increase the quota. // Temporarily make the details of the proposed database available, so the client can get at them. - Page* page = document->page(); - if (!page) - return false; pair<SecurityOrigin*, DatabaseDetails> details(origin, DatabaseDetails(name, displayName, estimatedSize, 0)); m_proposedDatabase = &details; - page->chrome()->client()->exceededDatabaseQuota(document->frame(), name); + context->databaseExceededQuota(name); m_proposedDatabase = 0; // If the database will fit now, allow its creation. @@ -182,6 +181,16 @@ bool DatabaseTracker::hasEntryForDatabase(SecurityOrigin* origin, const String& return statement.step() == SQLResultRow; } +unsigned long long DatabaseTracker::getMaxSizeForDatabase(const Database* database) +{ + ASSERT(currentThread() == database->scriptExecutionContext()->databaseThread()->getThreadID()); + // The maximum size for a database is the full quota for its origin, minus the current usage within the origin, + // plus the current usage of the given database + Locker<OriginQuotaManager> locker(originQuotaManager()); + SecurityOrigin* origin = database->securityOrigin(); + return quotaForOrigin(origin) - originQuotaManager().diskUsage(origin) + SQLiteFileSystem::getDatabaseFileSize(database->fileName()); +} + String DatabaseTracker::originPath(SecurityOrigin* origin) const { ASSERT(currentThread() == m_thread); @@ -197,11 +206,11 @@ String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String String originIdentifier = origin->databaseIdentifier(); String originPath = this->originPath(origin); - + // Make sure the path for this SecurityOrigin exists if (createIfNotExists && !SQLiteFileSystem::ensureDatabaseDirectoryExists(originPath)) return String(); - + // See if we have a path for this database yet openTrackerDatabase(false); if (!m_database.isOpen()) @@ -220,14 +229,14 @@ String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String return SQLiteFileSystem::appendDatabaseFileNameToPath(originPath, statement.getColumnText(0)); if (!createIfNotExists) return String(); - + if (result != SQLResultDone) { LOG_ERROR("Failed to retrieve filename from Database Tracker for origin %s, name %s", origin->databaseIdentifier().ascii().data(), name.ascii().data()); return String(); } statement.finalize(); - - String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, origin->databaseIdentifier(), name, &m_database); + + String fileName = SQLiteFileSystem::getFileNameForNewDatabase(originPath, name, origin->databaseIdentifier(), &m_database); if (!addDatabase(origin, name, fileName)) return String(); @@ -239,7 +248,7 @@ String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String if (originQuotaManager().tracksOrigin(origin)) originQuotaManager().addDatabase(origin, name, fullFilePath); } - + return fullFilePath; } @@ -321,19 +330,19 @@ DatabaseDetails DatabaseTracker::detailsForNameAndOrigin(const String& name, Sec SQLiteStatement statement(m_database, "SELECT displayName, estimatedSize FROM Databases WHERE origin=? AND name=?"); if (statement.prepare() != SQLResultOk) return DatabaseDetails(); - + statement.bindText(1, originIdentifier); statement.bindText(2, name); - + int result = statement.step(); if (result == SQLResultDone) return DatabaseDetails(); - + if (result != SQLResultRow) { LOG_ERROR("Error retrieving details for database %s in origin %s from tracker database", name.ascii().data(), originIdentifier.ascii().data()); return DatabaseDetails(); } - + return DatabaseDetails(name, statement.getColumnText(0), statement.getColumnInt64(1), usageForDatabase(name, origin)); } @@ -343,17 +352,17 @@ void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& n String originIdentifier = origin->databaseIdentifier(); int64_t guid = 0; - + openTrackerDatabase(true); if (!m_database.isOpen()) return; SQLiteStatement statement(m_database, "SELECT guid FROM Databases WHERE origin=? AND name=?"); if (statement.prepare() != SQLResultOk) return; - + statement.bindText(1, originIdentifier); statement.bindText(2, name); - + int result = statement.step(); if (result == SQLResultRow) guid = statement.getColumnInt64(0); @@ -371,20 +380,20 @@ void DatabaseTracker::setDatabaseDetails(SecurityOrigin* origin, const String& n } return; } - + SQLiteStatement updateStatement(m_database, "UPDATE Databases SET displayName=?, estimatedSize=? WHERE guid=?"); if (updateStatement.prepare() != SQLResultOk) return; - + updateStatement.bindText(1, displayName); updateStatement.bindInt64(2, estimatedSize); updateStatement.bindInt64(3, guid); - + if (updateStatement.step() != SQLResultDone) { LOG_ERROR("Failed to update details for database %s in origin %s", name.ascii().data(), originIdentifier.ascii().data()); - return; + return; } - + if (m_client) m_client->dispatchDidModifyDatabase(origin, name); } @@ -395,7 +404,7 @@ unsigned long long DatabaseTracker::usageForDatabase(const String& name, Securit String path = fullPathForDatabase(origin, name, false); if (path.isEmpty()) return 0; - + return SQLiteFileSystem::getDatabaseFileSize(path); } @@ -409,13 +418,11 @@ void DatabaseTracker::addOpenDatabase(Database* database) if (!m_openDatabaseMap) m_openDatabaseMap.set(new DatabaseOriginMap); - RefPtr<SecurityOrigin> origin(database->securityOriginCopy()); String name(database->stringIdentifier()); - - DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); if (!nameMap) { nameMap = new DatabaseNameMap; - m_openDatabaseMap->set(origin, nameMap); + m_openDatabaseMap->set(database->securityOrigin(), nameMap); } DatabaseSet* databaseSet = nameMap->get(name); @@ -441,10 +448,8 @@ void DatabaseTracker::removeOpenDatabase(Database* database) return; } - RefPtr<SecurityOrigin> origin(database->securityOriginCopy()); String name(database->stringIdentifier()); - - DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); if (!nameMap) { ASSERT_NOT_REACHED(); return; @@ -469,10 +474,28 @@ void DatabaseTracker::removeOpenDatabase(Database* database) if (!nameMap->isEmpty()) return; - m_openDatabaseMap->remove(origin); + m_openDatabaseMap->remove(database->securityOrigin()); delete nameMap; } +void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<Database> >* databases) +{ + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + if (!m_openDatabaseMap) + return; + + DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); + if (!nameMap) + return; + + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) + return; + + for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) + databases->add(*it); +} + unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin) { ASSERT(currentThread() == m_thread); @@ -481,16 +504,16 @@ unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin) // Use the OriginQuotaManager mechanism to calculate the usage if (originQuotaManager().tracksOrigin(origin)) return originQuotaManager().diskUsage(origin); - + // If the OriginQuotaManager doesn't track this origin already, prime it to do so originQuotaManager().trackOrigin(origin); - + Vector<String> names; databaseNamesForOrigin(origin, names); for (unsigned i = 0; i < names.size(); ++i) originQuotaManager().addDatabase(origin, names[i], fullPathForDatabase(origin, names[i], false)); - + if (!originQuotaManager().tracksOrigin(origin)) return 0; return originQuotaManager().diskUsage(origin); @@ -529,7 +552,7 @@ void DatabaseTracker::setQuota(SecurityOrigin* origin, unsigned long long quota) LOG_ERROR("Unable to establish origin %s in the tracker", origin->databaseIdentifier().ascii().data()); } } else { - SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); + SQLiteStatement statement(m_database, "UPDATE Origins SET quota=? WHERE origin=?"); bool error = statement.prepare() != SQLResultOk; if (!error) { statement.bindInt64(1, quota); @@ -556,7 +579,7 @@ bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, co openTrackerDatabase(true); if (!m_database.isOpen()) return false; - + // New database should never be added until the origin has been established ASSERT(hasEntryForOrigin(origin)); @@ -573,10 +596,10 @@ bool DatabaseTracker::addDatabase(SecurityOrigin* origin, const String& name, co LOG_ERROR("Failed to add database %s to origin %s: %s\n", name.ascii().data(), origin->databaseIdentifier().ascii().data(), m_database.lastErrorMsg()); return false; } - + if (m_client) m_client->dispatchDidModifyOrigin(origin); - + return true; } @@ -603,27 +626,27 @@ void DatabaseTracker::deleteOrigin(SecurityOrigin* origin) LOG_ERROR("Unable to retrieve list of database names for origin %s", origin->databaseIdentifier().ascii().data()); return; } - + for (unsigned i = 0; i < databaseNames.size(); ++i) { if (!deleteDatabaseFile(origin, databaseNames[i])) { + // Even if the file can't be deleted, we want to try and delete the rest, don't return early here. LOG_ERROR("Unable to delete file for database %s in origin %s", databaseNames[i].ascii().data(), origin->databaseIdentifier().ascii().data()); - return; } } - + SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?"); if (statement.prepare() != SQLResultOk) { LOG_ERROR("Unable to prepare deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); return; } - + statement.bindText(1, origin->databaseIdentifier()); - + if (!statement.executeCommand()) { LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); return; } - + SQLiteStatement originStatement(m_database, "DELETE FROM Origins WHERE origin=?"); if (originStatement.prepare() != SQLResultOk) { LOG_ERROR("Unable to prepare deletion of origin %s from tracker", origin->databaseIdentifier().ascii().data()); @@ -631,7 +654,7 @@ void DatabaseTracker::deleteOrigin(SecurityOrigin* origin) } originStatement.bindText(1, origin->databaseIdentifier()); - + if (!originStatement.executeCommand()) { LOG_ERROR("Unable to execute deletion of databases from origin %s from tracker", origin->databaseIdentifier().ascii().data()); return; @@ -674,26 +697,26 @@ void DatabaseTracker::deleteDatabase(SecurityOrigin* origin, const String& name) LOG_ERROR("Unable to delete file for database %s in origin %s", name.ascii().data(), origin->databaseIdentifier().ascii().data()); return; } - + SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=? AND name=?"); if (statement.prepare() != SQLResultOk) { LOG_ERROR("Unable to prepare deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); return; } - + statement.bindText(1, origin->databaseIdentifier()); statement.bindText(2, name); - + if (!statement.executeCommand()) { LOG_ERROR("Unable to execute deletion of database %s from origin %s from tracker", name.ascii().data(), origin->databaseIdentifier().ascii().data()); return; } - + { Locker<OriginQuotaManager> quotaManagerLocker(originQuotaManager()); originQuotaManager().removeDatabase(origin, name); } - + if (m_client) { m_client->dispatchDidModifyOrigin(origin); m_client->dispatchDidModifyDatabase(origin, name); @@ -762,7 +785,7 @@ void DatabaseTracker::scheduleNotifyDatabaseChanged(SecurityOrigin* origin, cons { MutexLocker locker(notificationMutex()); - notificationQueue().append(pair<SecurityOrigin*, String>(origin, name.copy())); + notificationQueue().append(pair<SecurityOrigin*, String>(origin, name.crossThreadString())); scheduleForNotification(); } diff --git a/WebCore/storage/DatabaseTracker.h b/WebCore/storage/DatabaseTracker.h index dc50965..4640b18 100644 --- a/WebCore/storage/DatabaseTracker.h +++ b/WebCore/storage/DatabaseTracker.h @@ -31,74 +31,99 @@ #if ENABLE(DATABASE) -#include "DatabaseDetails.h" #include "PlatformString.h" -#include "SQLiteDatabase.h" #include "StringHash.h" +#include <wtf/HashMap.h> #include <wtf/HashSet.h> + +#if !PLATFORM(CHROMIUM) +#include "DatabaseDetails.h" +#include "SQLiteDatabase.h" #include <wtf/OwnPtr.h> +#endif // !PLATFORM(CHROMIUM) namespace WebCore { class Database; -class DatabaseTrackerClient; -class Document; -class OriginQuotaManager; +class ScriptExecutionContext; class SecurityOrigin; struct SecurityOriginHash; + +#if !PLATFORM(CHROMIUM) +class DatabaseTrackerClient; +class OriginQuotaManager; + struct SecurityOriginTraits; +#endif // !PLATFORM(CHROMIUM) -class DatabaseTracker { +class DatabaseTracker : public Noncopyable { public: - void setDatabaseDirectoryPath(const String&); - const String& databaseDirectoryPath() const; + static DatabaseTracker& tracker(); + // FIXME: Due to workers having multiple threads in a single process sharing + // a DatabaseTracker, this singleton will have to be synchronized or moved + // to TLS. - bool canEstablishDatabase(Document*, const String& name, const String& displayName, unsigned long estimatedSize); + bool canEstablishDatabase(ScriptExecutionContext*, const String& name, const String& displayName, unsigned long estimatedSize); void setDatabaseDetails(SecurityOrigin*, const String& name, const String& displayName, unsigned long estimatedSize); String fullPathForDatabase(SecurityOrigin*, const String& name, bool createIfDoesNotExist = true); + void addOpenDatabase(Database*); + void removeOpenDatabase(Database*); + void getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<Database> >* databases); + + unsigned long long getMaxSizeForDatabase(const Database*); + +private: + DatabaseTracker(); + + typedef HashSet<Database*> DatabaseSet; + typedef HashMap<String, DatabaseSet*> DatabaseNameMap; + typedef HashMap<RefPtr<SecurityOrigin>, DatabaseNameMap*, SecurityOriginHash> DatabaseOriginMap; + + Mutex m_openDatabaseMapGuard; + mutable OwnPtr<DatabaseOriginMap> m_openDatabaseMap; + +#if !PLATFORM(CHROMIUM) +public: + void setDatabaseDirectoryPath(const String&); + 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*); void setQuota(SecurityOrigin*, unsigned long long); - + void deleteAllDatabases(); void deleteOrigin(SecurityOrigin*); void deleteDatabase(SecurityOrigin*, const String& name); void setClient(DatabaseTrackerClient*); - + // From a secondary thread, must be thread safe with its data void scheduleNotifyDatabaseChanged(SecurityOrigin*, const String& name); - + OriginQuotaManager& originQuotaManager(); - - static DatabaseTracker& tracker(); + bool hasEntryForOrigin(SecurityOrigin*); private: - DatabaseTracker(); - String trackerDatabasePath() const; void openTrackerDatabase(bool createIfDoesNotExist); String originPath(SecurityOrigin*) const; - + bool hasEntryForDatabase(SecurityOrigin*, const String& databaseIdentifier); - + bool addDatabase(SecurityOrigin*, const String& name, const String& path); void populateOrigins(); - + bool deleteDatabaseFile(SecurityOrigin*, const String& name); SQLiteDatabase m_database; @@ -107,17 +132,10 @@ private: Mutex m_quotaMapGuard; mutable OwnPtr<QuotaMap> m_quotaMap; - typedef HashSet<Database*> DatabaseSet; - typedef HashMap<String, DatabaseSet*> DatabaseNameMap; - typedef HashMap<RefPtr<SecurityOrigin>, DatabaseNameMap*, SecurityOriginHash> DatabaseOriginMap; - - Mutex m_openDatabaseMapGuard; - mutable OwnPtr<DatabaseOriginMap> m_openDatabaseMap; - OwnPtr<OriginQuotaManager> m_quotaManager; String m_databaseDirectoryPath; - + DatabaseTrackerClient* m_client; std::pair<SecurityOrigin*, DatabaseDetails>* m_proposedDatabase; @@ -128,6 +146,7 @@ private: static void scheduleForNotification(); static void notifyDatabasesChanged(void*); +#endif // !PLATFORM(CHROMIUM) }; } // namespace WebCore diff --git a/WebCore/storage/IDBDatabaseError.h b/WebCore/storage/IDBDatabaseError.h new file mode 100644 index 0000000..e8fd2dd --- /dev/null +++ b/WebCore/storage/IDBDatabaseError.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef IDBDatabaseError_h +#define IDBDatabaseError_h + +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBDatabaseError : public RefCounted<IDBDatabaseError> { +public: + static PassRefPtr<IDBDatabaseError> create() + { + return adoptRef(new IDBDatabaseError()); + } + ~IDBDatabaseError() { } + + unsigned short code() const { return m_code; } + void setCode(unsigned short value) { m_code = value; } + String message() const { return m_message; } + void setMessage(const String& value) { m_message = value; } + +private: + IDBDatabaseError() { } + + unsigned short m_code; + String m_message; +}; + +} // namespace WebCore + +#endif + +#endif // IDBDatabaseError_h + diff --git a/WebCore/storage/IDBDatabaseError.idl b/WebCore/storage/IDBDatabaseError.idl new file mode 100644 index 0000000..6c6019c --- /dev/null +++ b/WebCore/storage/IDBDatabaseError.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBDatabaseError { + attribute unsigned short code; + attribute DOMString message; + }; + +} diff --git a/WebCore/storage/IDBDatabaseException.h b/WebCore/storage/IDBDatabaseException.h new file mode 100644 index 0000000..d94a7f9 --- /dev/null +++ b/WebCore/storage/IDBDatabaseException.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef IDBDatabaseException_h +#define IDBDatabaseException_h + +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBDatabaseException : public RefCounted<IDBDatabaseException> { +public: + static PassRefPtr<IDBDatabaseException> create() + { + return adoptRef(new IDBDatabaseException()); + } + ~IDBDatabaseException() { } + + unsigned short code() const { return m_code; } + void setCode(unsigned short value) { m_code = value; } + String message() const { return m_message; } + void setMessage(const String& value) { m_message = value; } + +private: + IDBDatabaseException() { } + + unsigned short m_code; + String m_message; +}; + +} // namespace WebCore + +#endif + +#endif // IDBDatabaseException_h + diff --git a/WebCore/storage/IDBDatabaseException.idl b/WebCore/storage/IDBDatabaseException.idl new file mode 100644 index 0000000..898e5f9 --- /dev/null +++ b/WebCore/storage/IDBDatabaseException.idl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IDBDatabaseException { + const unsigned short UNKNOWN_ERR = 0; + const unsigned short NON_TRANSIENT_ERR = 1; + const unsigned short NOT_FOUND_ERR = 2; + const unsigned short CONSTRAINT_ERR = 3; + const unsigned short DATA_ERR = 4; + const unsigned short NOT_ALLOWED_ERR = 5; + const unsigned short SERIAL_ERR = 11; + const unsigned short RECOVERABLE_ERR = 21; + const unsigned short TRANSIENT_ERR = 31; + const unsigned short TIMEOUT_ERR = 32; + const unsigned short DEADLOCK_ERR = 33; + attribute unsigned short code; + attribute DOMString message; + }; + +} diff --git a/WebCore/storage/IDBRequest.cpp b/WebCore/storage/IDBRequest.cpp new file mode 100644 index 0000000..1a20499 --- /dev/null +++ b/WebCore/storage/IDBRequest.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "IDBRequest.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBDatabaseError.h" +#include "SerializedScriptValue.h" + +namespace WebCore { + +IDBRequest::IDBRequest(ScriptExecutionContext* context) + : ActiveDOMObject(context, this) +{ +} + +IDBRequest::~IDBRequest() +{ +} + +void IDBRequest::abort() +{ +} + +EventTargetData* IDBRequest::eventTargetData() +{ + return 0; +} + +EventTargetData* IDBRequest::ensureEventTargetData() +{ + return 0; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + diff --git a/WebCore/storage/IDBRequest.h b/WebCore/storage/IDBRequest.h new file mode 100644 index 0000000..5f00aa8 --- /dev/null +++ b/WebCore/storage/IDBRequest.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef IDBRequest_h +#define IDBRequest_h + +#include "ActiveDOMObject.h" +#include "EventTarget.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBDatabaseError; +class SerializedScriptValue; + +class IDBRequest : public RefCounted<IDBRequest>, public ActiveDOMObject, public EventTarget { +public: + static PassRefPtr<IDBRequest> create(ScriptExecutionContext* context) + { + return adoptRef(new IDBRequest(context)); + } + ~IDBRequest(); + + void abort(); + unsigned short readyState() const { return m_readyState; } + IDBDatabaseError* error() const { return m_error.get(); } + SerializedScriptValue* result() const { return m_result.get(); } + + DEFINE_ATTRIBUTE_EVENT_LISTENER(success); + DEFINE_ATTRIBUTE_EVENT_LISTENER(error); + + using RefCounted<IDBRequest>::ref; + using RefCounted<IDBRequest>::deref; + + // EventTarget interface + virtual ScriptExecutionContext* scriptExecutionContext() const { return ActiveDOMObject::scriptExecutionContext(); } + virtual IDBRequest* toIDBRequest() { return this; } + +private: + explicit IDBRequest(ScriptExecutionContext* context); + + // EventTarget interface + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + + unsigned short m_readyState; + RefPtr<IDBDatabaseError> m_error; + RefPtr<SerializedScriptValue> m_result; + + EventTargetData m_eventTargetData; +}; + +} // namespace WebCore + +#endif + +#endif // IDBRequest_h + diff --git a/WebCore/storage/IDBRequest.idl b/WebCore/storage/IDBRequest.idl new file mode 100644 index 0000000..b34184c --- /dev/null +++ b/WebCore/storage/IDBRequest.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module storage { + + interface [ + Conditional=INDEXED_DATABASE, + EventTarget + ] IDBRequest { + void abort(); + const unsigned short INITIAL = 0; + const unsigned short LOADING = 1; + const unsigned short DONE = 2; + readonly attribute unsigned short readyState; + readonly attribute IDBDatabaseError error; + readonly attribute [CustomGetter] any result; + attribute EventListener onsuccess; + attribute EventListener onerror; + }; + +} diff --git a/WebCore/storage/IndexedDatabaseRequest.cpp b/WebCore/storage/IndexedDatabaseRequest.cpp new file mode 100644 index 0000000..827493b --- /dev/null +++ b/WebCore/storage/IndexedDatabaseRequest.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "IndexedDatabaseRequest.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "ExceptionCode.h" +#include "IDBRequest.h" + +namespace WebCore { + +IndexedDatabaseRequest::IndexedDatabaseRequest() +{ +} + +IndexedDatabaseRequest::~IndexedDatabaseRequest() +{ +} + +void IndexedDatabaseRequest::open(const String& name, const String& description, bool modifyDatabase, ExceptionCode& exception) +{ +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) + diff --git a/WebCore/storage/IndexedDatabaseRequest.h b/WebCore/storage/IndexedDatabaseRequest.h new file mode 100644 index 0000000..74aada3 --- /dev/null +++ b/WebCore/storage/IndexedDatabaseRequest.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef IndexedDatabaseRequest_h +#define IndexedDatabaseRequest_h + +#include "ExceptionCode.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +#if ENABLE(INDEXED_DATABASE) + +namespace WebCore { + +class IDBRequest; + +class IndexedDatabaseRequest : public RefCounted<IndexedDatabaseRequest> { +public: + static PassRefPtr<IndexedDatabaseRequest> create() + { + return adoptRef(new IndexedDatabaseRequest()); + } + ~IndexedDatabaseRequest(); + + IDBRequest* request() const { return m_request.get(); } + void open(const String& name, const String& description, bool modifyDatabase, ExceptionCode&); + +private: + IndexedDatabaseRequest(); + + PassRefPtr<IDBRequest> m_request; +}; + +} // namespace WebCore + +#endif + +#endif // IndexedDatabaseRequest_h + diff --git a/WebCore/storage/IndexedDatabaseRequest.idl b/WebCore/storage/IndexedDatabaseRequest.idl new file mode 100644 index 0000000..b1fc7da --- /dev/null +++ b/WebCore/storage/IndexedDatabaseRequest.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +module storage { + + interface [ + Conditional=INDEXED_DATABASE + ] IndexedDatabaseRequest { + readonly attribute IDBRequest request; + [Custom] void open(in DOMString name, in DOMString description, in optional boolean modifyDatabase) + raises(IDBDatabaseException); + }; + +} diff --git a/WebCore/storage/LocalStorageTask.cpp b/WebCore/storage/LocalStorageTask.cpp index f5d4890..12cc083 100644 --- a/WebCore/storage/LocalStorageTask.cpp +++ b/WebCore/storage/LocalStorageTask.cpp @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -33,16 +33,18 @@ namespace WebCore { -LocalStorageTask::LocalStorageTask(Type type, PassRefPtr<StorageAreaSync> area) +LocalStorageTask::LocalStorageTask(Type type, StorageAreaSync* area) : m_type(type) , m_area(area) + , m_thread(0) { ASSERT(m_area); ASSERT(m_type == AreaImport || m_type == AreaSync); } -LocalStorageTask::LocalStorageTask(Type type, PassRefPtr<LocalStorageThread> thread) +LocalStorageTask::LocalStorageTask(Type type, LocalStorageThread* thread) : m_type(type) + , m_area(0) , m_thread(thread) { ASSERT(m_thread); @@ -57,11 +59,9 @@ void LocalStorageTask::performTask() { switch (m_type) { case AreaImport: - ASSERT(m_area); m_area->performImport(); break; case AreaSync: - ASSERT(m_area); m_area->performSync(); break; case TerminateThread: @@ -73,4 +73,3 @@ void LocalStorageTask::performTask() } #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/LocalStorageTask.h b/WebCore/storage/LocalStorageTask.h index b12a26b..dc3e7e2 100644 --- a/WebCore/storage/LocalStorageTask.h +++ b/WebCore/storage/LocalStorageTask.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef 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,25 +37,25 @@ 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(PassRefPtr<StorageAreaSync> area) { return adoptRef(new LocalStorageTask(AreaImport, area)); } - static PassRefPtr<LocalStorageTask> createSync(PassRefPtr<StorageAreaSync> area) { return adoptRef(new LocalStorageTask(AreaSync, area)); } - static PassRefPtr<LocalStorageTask> createTerminate(PassRefPtr<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(); private: - LocalStorageTask(Type, PassRefPtr<StorageAreaSync>); - LocalStorageTask(Type, PassRefPtr<LocalStorageThread>); + LocalStorageTask(Type, StorageAreaSync*); + LocalStorageTask(Type, LocalStorageThread*); Type m_type; - RefPtr<StorageAreaSync> m_area; - RefPtr<LocalStorageThread> m_thread; + StorageAreaSync* m_area; + LocalStorageThread* m_thread; }; } // namespace WebCore diff --git a/WebCore/storage/LocalStorageThread.cpp b/WebCore/storage/LocalStorageThread.cpp index 2da5934..d4a7b4c 100644 --- a/WebCore/storage/LocalStorageThread.cpp +++ b/WebCore/storage/LocalStorageThread.cpp @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -33,99 +33,72 @@ 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(PassRefPtr<StorageAreaSync> area) -{ - ASSERT(!m_queue.killed() && m_threadID); - m_queue.append(LocalStorageTask::createImport(area)); -} - -void LocalStorageThread::scheduleSync(PassRefPtr<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(); } } #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/LocalStorageThread.h b/WebCore/storage/LocalStorageThread.h index 3d58427..6f05911 100644 --- a/WebCore/storage/LocalStorageThread.h +++ b/WebCore/storage/LocalStorageThread.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef 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(PassRefPtr<StorageAreaSync>); - void scheduleSync(PassRefPtr<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/OriginQuotaManager.cpp b/WebCore/storage/OriginQuotaManager.cpp index 2b98ab7..30b3271 100644 --- a/WebCore/storage/OriginQuotaManager.cpp +++ b/WebCore/storage/OriginQuotaManager.cpp @@ -75,25 +75,25 @@ bool OriginQuotaManager::tracksOrigin(SecurityOrigin* origin) const void OriginQuotaManager::addDatabase(SecurityOrigin* origin, const String& databaseIdentifier, const String& fullPath) { ASSERT(m_usageRecordGuardLocked); - + OriginUsageRecord* usageRecord = m_usageMap.get(origin); ASSERT(usageRecord); - - usageRecord->addDatabase(databaseIdentifier.copy(), fullPath.copy()); + + usageRecord->addDatabase(databaseIdentifier.threadsafeCopy(), fullPath.threadsafeCopy()); } void OriginQuotaManager::removeDatabase(SecurityOrigin* origin, const String& databaseIdentifier) { ASSERT(m_usageRecordGuardLocked); - - if (OriginUsageRecord* usageRecord = m_usageMap.get(origin)) + + if (OriginUsageRecord* usageRecord = m_usageMap.get(origin)) usageRecord->removeDatabase(databaseIdentifier); } void OriginQuotaManager::removeOrigin(SecurityOrigin* origin) { ASSERT(m_usageRecordGuardLocked); - + if (OriginUsageRecord* usageRecord = m_usageMap.get(origin)) { m_usageMap.remove(origin); delete usageRecord; @@ -104,24 +104,22 @@ void OriginQuotaManager::markDatabase(Database* database) { ASSERT(database); ASSERT(m_usageRecordGuardLocked); - RefPtr<SecurityOrigin> origin = database->securityOriginCopy(); - OriginUsageRecord* usageRecord = m_usageMap.get(origin); + OriginUsageRecord* usageRecord = m_usageMap.get(database->securityOrigin()); ASSERT(usageRecord); - + usageRecord->markDatabase(database->stringIdentifier()); } unsigned long long OriginQuotaManager::diskUsage(SecurityOrigin* origin) const { ASSERT(m_usageRecordGuardLocked); - + OriginUsageRecord* usageRecord = m_usageMap.get(origin); ASSERT(usageRecord); - + return usageRecord->diskUsage(); } - } #endif // ENABLE(DATABASE) diff --git a/WebCore/storage/OriginUsageRecord.cpp b/WebCore/storage/OriginUsageRecord.cpp index 5f4957f..684df53 100644 --- a/WebCore/storage/OriginUsageRecord.cpp +++ b/WebCore/storage/OriginUsageRecord.cpp @@ -44,10 +44,10 @@ void OriginUsageRecord::addDatabase(const String& identifier, const String& full ASSERT(!m_databaseMap.contains(identifier)); ASSERT_ARG(identifier, identifier.impl()->refCount() == 1); ASSERT_ARG(fullPath, fullPath.impl()->refCount() == 1); - + m_databaseMap.set(identifier, DatabaseEntry(fullPath)); m_unknownSet.add(identifier); - + m_cachedDiskUsageIsValid = false; } @@ -81,13 +81,13 @@ unsigned long long OriginUsageRecord::diskUsage() for (; iUnknown != endUnknown; ++iUnknown) { const String& path = m_databaseMap.get(*iUnknown).filename; ASSERT(!path.isEmpty()); - + // When we can't determine the file size, we'll just have to assume the file is missing/inaccessible. long long size = SQLiteFileSystem::getDatabaseFileSize(path); m_databaseMap.set(*iUnknown, DatabaseEntry(path, size)); } m_unknownSet.clear(); - + // Recalculate the cached usage value. m_cachedDiskUsage = 0; HashMap<String, DatabaseEntry>::iterator iDatabase = m_databaseMap.begin(); @@ -98,7 +98,7 @@ unsigned long long OriginUsageRecord::diskUsage() m_cachedDiskUsageIsValid = true; return m_cachedDiskUsage; } - + } #endif diff --git a/WebCore/storage/OriginUsageRecord.h b/WebCore/storage/OriginUsageRecord.h index 3442ae1..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(); @@ -68,4 +68,4 @@ private: #endif -#endif +#endif diff --git a/WebCore/storage/SQLError.h b/WebCore/storage/SQLError.h index b6ebb5c..4414e6b 100644 --- a/WebCore/storage/SQLError.h +++ b/WebCore/storage/SQLError.h @@ -41,10 +41,10 @@ public: static PassRefPtr<SQLError> create(unsigned code, const String& message) { return adoptRef(new SQLError(code, message)); } unsigned code() const { return m_code; } - String message() const { return m_message.copy(); } - + String message() const { return m_message.threadsafeCopy(); } + private: - SQLError(unsigned code, const String& message) : m_code(code), m_message(message.copy()) { } + SQLError(unsigned code, const String& message) : m_code(code), m_message(message.threadsafeCopy()) { } unsigned m_code; String m_message; }; diff --git a/WebCore/storage/SQLError.idl b/WebCore/storage/SQLError.idl index d889c5b..503fe6f 100644 --- a/WebCore/storage/SQLError.idl +++ b/WebCore/storage/SQLError.idl @@ -29,7 +29,8 @@ module storage { interface [ - Conditional=DATABASE + Conditional=DATABASE, + OmitConstructor ] SQLError { readonly attribute unsigned long code; readonly attribute DOMString message; diff --git a/WebCore/storage/SQLResultSet.idl b/WebCore/storage/SQLResultSet.idl index 1db07cd..c98fff6 100644 --- a/WebCore/storage/SQLResultSet.idl +++ b/WebCore/storage/SQLResultSet.idl @@ -29,7 +29,8 @@ module storage { interface [ - Conditional=DATABASE + Conditional=DATABASE, + OmitConstructor ] SQLResultSet { readonly attribute SQLResultSetRowList rows; diff --git a/WebCore/storage/SQLResultSetRowList.h b/WebCore/storage/SQLResultSetRowList.h index 96a6aa1..92b5ec0 100644 --- a/WebCore/storage/SQLResultSetRowList.h +++ b/WebCore/storage/SQLResultSetRowList.h @@ -39,7 +39,7 @@ namespace WebCore { class SQLResultSetRowList : public RefCounted<SQLResultSetRowList> { public: static PassRefPtr<SQLResultSetRowList> create() { return adoptRef(new SQLResultSetRowList); } - + const Vector<String>& columnNames() const { return m_columns; } const Vector<SQLValue>& values() const { return m_result; } @@ -50,7 +50,7 @@ public: private: SQLResultSetRowList() { } - + Vector<String> m_columns; Vector<SQLValue> m_result; }; diff --git a/WebCore/storage/SQLResultSetRowList.idl b/WebCore/storage/SQLResultSetRowList.idl index 6a477e9..7ae7a9c 100644 --- a/WebCore/storage/SQLResultSetRowList.idl +++ b/WebCore/storage/SQLResultSetRowList.idl @@ -29,7 +29,8 @@ module storage { interface [ - Conditional=DATABASE + Conditional=DATABASE, + OmitConstructor ] SQLResultSetRowList { readonly attribute unsigned long length; [Custom] DOMObject item(in unsigned long index); diff --git a/WebCore/storage/SQLStatement.cpp b/WebCore/storage/SQLStatement.cpp index 38ca75d..b4eb9ef 100644 --- a/WebCore/storage/SQLStatement.cpp +++ b/WebCore/storage/SQLStatement.cpp @@ -50,31 +50,31 @@ PassRefPtr<SQLStatement> SQLStatement::create(const String& statement, const Vec } SQLStatement::SQLStatement(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback, bool readOnly) - : m_statement(statement.copy()) + : m_statement(statement.crossThreadString()) , m_arguments(arguments) , m_statementCallback(callback) , m_statementErrorCallback(errorCallback) , m_readOnly(readOnly) { } - + bool SQLStatement::execute(Database* db) { ASSERT(!m_resultSet); - + // If we're re-running this statement after a quota violation, we need to clear that error now clearFailureDueToQuota(); - // This transaction might have been marked bad while it was being set up on the main thread, + // This transaction might have been marked bad while it was being set up on the main thread, // so if there is still an error, return false. if (m_error) return false; - + if (m_readOnly) db->setAuthorizerReadOnly(); - + SQLiteDatabase* database = &db->m_sqliteDatabase; - + SQLiteStatement statement(*database, m_statement); int result = statement.prepare(); @@ -98,7 +98,7 @@ bool SQLStatement::execute(Database* db) setFailureDueToQuota(); return false; } - + if (result != SQLResultOk) { LOG(StorageAPI, "Failed to bind value index %i to statement for query '%s'", i + 1, m_statement.ascii().data()); m_error = SQLError::create(1, database->lastErrorMsg()); @@ -165,9 +165,9 @@ void SQLStatement::setVersionMismatchedError() bool SQLStatement::performCallback(SQLTransaction* transaction) { ASSERT(transaction); - + bool callbackError = false; - + // Call the appropriate statement callback and track if it resulted in an error, // because then we need to jump to the transaction error callback. if (m_error) { @@ -195,9 +195,9 @@ void SQLStatement::clearFailureDueToQuota() m_error = 0; } -bool SQLStatement::lastExecutionFailedDueToQuota() const -{ - return m_error && m_error->code() == 4; +bool SQLStatement::lastExecutionFailedDueToQuota() const +{ + return m_error && m_error->code() == 4; } } // namespace WebCore diff --git a/WebCore/storage/SQLStatement.h b/WebCore/storage/SQLStatement.h index 831aecc..f01f7bf 100644 --- a/WebCore/storage/SQLStatement.h +++ b/WebCore/storage/SQLStatement.h @@ -52,10 +52,10 @@ class String; class SQLStatement : public ThreadSafeShared<SQLStatement> { public: static PassRefPtr<SQLStatement> create(const String&, const Vector<SQLValue>&, PassRefPtr<SQLStatementCallback>, PassRefPtr<SQLStatementErrorCallback>, bool readOnly); - + bool execute(Database*); bool lastExecutionFailedDueToQuota() const; - + bool hasStatementCallback() const { return m_statementCallback; } bool hasStatementErrorCallback() const { return m_statementErrorCallback; } @@ -63,22 +63,22 @@ public: void setVersionMismatchedError(); bool performCallback(SQLTransaction*); - + SQLError* sqlError() const { return m_error.get(); } private: SQLStatement(const String& statement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> errorCallback, bool readOnly); void setFailureDueToQuota(); void clearFailureDueToQuota(); - + String m_statement; Vector<SQLValue> m_arguments; RefPtr<SQLStatementCallback> m_statementCallback; RefPtr<SQLStatementErrorCallback> m_statementErrorCallback; - + RefPtr<SQLError> m_error; RefPtr<SQLResultSet> m_resultSet; - + bool m_readOnly; }; diff --git a/WebCore/storage/SQLStatementCallback.h b/WebCore/storage/SQLStatementCallback.h index 14d19bb..31f5c0c 100644 --- a/WebCore/storage/SQLStatementCallback.h +++ b/WebCore/storage/SQLStatementCallback.h @@ -36,7 +36,7 @@ namespace WebCore { class SQLTransaction; class SQLResultSet; - + class SQLStatementCallback : public ThreadSafeShared<SQLStatementCallback> { public: virtual ~SQLStatementCallback() { } diff --git a/WebCore/storage/SQLStatementErrorCallback.h b/WebCore/storage/SQLStatementErrorCallback.h index ef0c328..29127ce 100644 --- a/WebCore/storage/SQLStatementErrorCallback.h +++ b/WebCore/storage/SQLStatementErrorCallback.h @@ -37,7 +37,7 @@ namespace WebCore { class SQLTransaction; class SQLError; - + class SQLStatementErrorCallback : public ThreadSafeShared<SQLStatementErrorCallback> { public: virtual ~SQLStatementErrorCallback() { } diff --git a/WebCore/storage/SQLTransaction.cpp b/WebCore/storage/SQLTransaction.cpp index 3331e6e..754cebc 100644 --- a/WebCore/storage/SQLTransaction.cpp +++ b/WebCore/storage/SQLTransaction.cpp @@ -35,14 +35,12 @@ #include "Database.h" #include "DatabaseAuthorizer.h" #include "DatabaseDetails.h" -#include "DatabaseTracker.h" -#include "Document.h" +#include "DatabaseThread.h" #include "ExceptionCode.h" #include "Logging.h" -#include "OriginQuotaManager.h" #include "Page.h" #include "PlatformString.h" -#include "SecurityOrigin.h" +#include "ScriptExecutionContext.h" #include "Settings.h" #include "SQLError.h" #include "SQLiteTransaction.h" @@ -50,24 +48,26 @@ #include "SQLStatement.h" #include "SQLStatementCallback.h" #include "SQLStatementErrorCallback.h" +#include "SQLTransactionClient.h" +#include "SQLTransactionCoordinator.h" #include "SQLValue.h" -// There's no way of knowing exactly how much more space will be required when a statement hits the quota limit. +// There's no way of knowing exactly how much more space will be required when a statement hits the quota limit. // For now, we'll arbitrarily choose currentQuota + 1mb. // In the future we decide to track if a size increase wasn't enough, and ask for larger-and-larger increases until its enough. static const int DefaultQuotaSizeIncrease = 1048576; namespace WebCore { -PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, - PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper) +PassRefPtr<SQLTransaction> SQLTransaction::create(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, + PassRefPtr<VoidCallback> successCallback, PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) { - return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper)); + return adoptRef(new SQLTransaction(db, callback, errorCallback, successCallback, wrapper, readOnly)); } -SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback, - PassRefPtr<SQLTransactionWrapper> wrapper) - : m_nextStep(&SQLTransaction::openTransactionAndPreflight) +SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback, + PassRefPtr<SQLTransactionWrapper> wrapper, bool readOnly) + : m_nextStep(&SQLTransaction::acquireLock) , m_executeSqlAllowed(false) , m_database(db) , m_wrapper(wrapper) @@ -76,12 +76,15 @@ SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> , m_errorCallback(errorCallback) , m_shouldRetryCurrentStatement(false) , m_modifiedDatabase(false) + , m_lockAcquired(false) + , m_readOnly(readOnly) { ASSERT(m_database); } SQLTransaction::~SQLTransaction() { + ASSERT(!m_sqliteTransaction); } void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e) @@ -91,11 +94,12 @@ void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValu return; } - bool readOnlyMode = false; - Page* page = m_database->document()->page(); - if (!page || page->settings()->privateBrowsingEnabled()) - readOnlyMode = true; - + bool readOnlyMode = m_readOnly; + if (!readOnlyMode) { + if (m_database->scriptExecutionContext()->isDatabaseReadOnly()) + readOnlyMode = true; + } + RefPtr<SQLStatement> statement = SQLStatement::create(sqlStatement, arguments, callback, callbackError, readOnlyMode); if (m_database->deleted()) @@ -116,7 +120,9 @@ void SQLTransaction::enqueueStatement(PassRefPtr<SQLStatement> statement) #ifndef NDEBUG const char* SQLTransaction::debugStepName(SQLTransaction::TransactionStepMethod step) { - if (step == &SQLTransaction::openTransactionAndPreflight) + if (step == &SQLTransaction::acquireLock) + return "acquireLock"; + else if (step == &SQLTransaction::openTransactionAndPreflight) return "openTransactionAndPreflight"; else if (step == &SQLTransaction::runStatements) return "runStatements"; @@ -145,18 +151,21 @@ void SQLTransaction::checkAndHandleClosedDatabase() { if (!m_database->stopped()) return; - + // If the database was stopped, don't do anything and cancel queued work LOG(StorageAPI, "Database was stopped - cancelling work for this transaction"); MutexLocker locker(m_statementMutex); m_statementQueue.clear(); m_nextStep = 0; - + // The current SQLite transaction should be stopped, as well if (m_sqliteTransaction) { m_sqliteTransaction->stop(); m_sqliteTransaction.clear(); } + + if (m_lockAcquired) + m_database->transactionCoordinator()->releaseLock(this); } @@ -164,14 +173,15 @@ bool SQLTransaction::performNextStep() { LOG(StorageAPI, "Step %s\n", debugStepName(m_nextStep)); - ASSERT(m_nextStep == &SQLTransaction::openTransactionAndPreflight || + ASSERT(m_nextStep == &SQLTransaction::acquireLock || + m_nextStep == &SQLTransaction::openTransactionAndPreflight || m_nextStep == &SQLTransaction::runStatements || m_nextStep == &SQLTransaction::postflightAndCommit || m_nextStep == &SQLTransaction::cleanupAfterSuccessCallback || m_nextStep == &SQLTransaction::cleanupAfterTransactionErrorCallback); - + checkAndHandleClosedDatabase(); - + if (m_nextStep) (this->*m_nextStep)(); @@ -190,14 +200,38 @@ void SQLTransaction::performPendingCallback() m_nextStep == &SQLTransaction::deliverSuccessCallback); checkAndHandleClosedDatabase(); - + if (m_nextStep) (this->*m_nextStep)(); } +void SQLTransaction::notifyDatabaseThreadIsShuttingDown() +{ + ASSERT(currentThread() == database()->scriptExecutionContext()->databaseThread()->getThreadID()); + + // If the transaction is in progress, we should roll it back here, since this is our last + // oportunity to do something related to this transaction on the DB thread. + // Clearing m_sqliteTransaction invokes SQLiteTransaction's destructor which does just that. + m_sqliteTransaction.clear(); +} + +void SQLTransaction::acquireLock() +{ + m_database->transactionCoordinator()->acquireLock(this); +} + +void SQLTransaction::lockAcquired() +{ + m_lockAcquired = true; + m_nextStep = &SQLTransaction::openTransactionAndPreflight; + LOG(StorageAPI, "Scheduling openTransactionAndPreflight immediately for transaction %p\n", this); + m_database->scheduleTransactionStep(this, true); +} + void SQLTransaction::openTransactionAndPreflight() { ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); + ASSERT(m_lockAcquired); LOG(StorageAPI, "Opening and preflighting transaction %p", this); @@ -208,16 +242,17 @@ void SQLTransaction::openTransactionAndPreflight() return; } - // Set the maximum usage for this transaction - m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize()); - + // Set the maximum usage for this transaction if this transactions is not read-only + if (!m_readOnly) + m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize()); + ASSERT(!m_sqliteTransaction); - m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase)); - + m_sqliteTransaction.set(new SQLiteTransaction(m_database->m_sqliteDatabase, m_readOnly)); + m_database->m_databaseAuthorizer->disable(); m_sqliteTransaction->begin(); - m_database->m_databaseAuthorizer->enable(); - + m_database->m_databaseAuthorizer->enable(); + // Transaction Steps 1+2 - Open a transaction to the database, jumping to the error callback if that fails if (!m_sqliteTransaction->inProgress()) { ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); @@ -226,7 +261,7 @@ void SQLTransaction::openTransactionAndPreflight() handleTransactionError(false); return; } - + // Transaction Steps 3 - Peform preflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPreflight(this)) { ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); @@ -238,7 +273,7 @@ void SQLTransaction::openTransactionAndPreflight() handleTransactionError(false); return; } - + // Transaction Step 4 - Invoke the transaction callback with the new SQLTransaction object m_nextStep = &SQLTransaction::deliverTransactionCallback; LOG(StorageAPI, "Scheduling deliverTransactionCallback for transaction %p\n", this); @@ -273,15 +308,20 @@ void SQLTransaction::scheduleToRunStatements() void SQLTransaction::runStatements() { + ASSERT(m_lockAcquired); + // If there is a series of statements queued up that are all successful and have no associated // SQLStatementCallback objects, then we can burn through the queue do { - if (m_shouldRetryCurrentStatement) { + if (m_shouldRetryCurrentStatement && !m_sqliteTransaction->wasRolledBackBySqlite()) { m_shouldRetryCurrentStatement = false; // FIXME - Another place that needs fixing up after <rdar://problem/5628468> is addressed. // See ::openTransactionAndPreflight() for discussion - - // Reset the maximum size here, as it was increased to allow us to retry this statement + + // Reset the maximum size here, as it was increased to allow us to retry this statement. + // m_shouldRetryCurrentStatement is set to true only when a statement exceeds + // the quota, which can happen only in a read-write transaction. Therefore, there + // is no need to check here if the transaction is read-write. m_database->m_sqliteDatabase.setMaximumSize(m_database->maximumSize()); } else { // If the current statement has already been run, failed due to quota constraints, and we're not retrying it, @@ -290,14 +330,14 @@ void SQLTransaction::runStatements() handleCurrentStatementError(); break; } - + // Otherwise, advance to the next statement getNextStatement(); } } while (runCurrentStatement()); - + // If runCurrentStatement() returned false, that means either there was no current statement to run, - // or the current statement requires a callback to complete. In the later case, it also scheduled + // or the current statement requires a callback to complete. In the later case, it also scheduled // the callback or performed any other additional work so we can return if (!m_currentStatement) postflightAndCommit(); @@ -306,7 +346,7 @@ void SQLTransaction::runStatements() void SQLTransaction::getNextStatement() { m_currentStatement = 0; - + MutexLocker locker(m_statementMutex); if (!m_statementQueue.isEmpty()) { m_currentStatement = m_statementQueue.first(); @@ -318,20 +358,17 @@ bool SQLTransaction::runCurrentStatement() { if (!m_currentStatement) return false; - + m_database->m_databaseAuthorizer->reset(); - + if (m_currentStatement->execute(m_database.get())) { if (m_database->m_databaseAuthorizer->lastActionChangedDatabase()) { // Flag this transaction as having changed the database for later delegate notification m_modifiedDatabase = true; // Also dirty the size of this database file for calculating quota usage - OriginQuotaManager& manager(DatabaseTracker::tracker().originQuotaManager()); - Locker<OriginQuotaManager> locker(manager); - - manager.markDatabase(m_database.get()); + m_database->transactionClient()->didExecuteStatement(this); } - + if (m_currentStatement->hasStatementCallback()) { m_nextStep = &SQLTransaction::deliverStatementCallback; LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); @@ -340,24 +377,24 @@ bool SQLTransaction::runCurrentStatement() } return true; } - + if (m_currentStatement->lastExecutionFailedDueToQuota()) { m_nextStep = &SQLTransaction::deliverQuotaIncreaseCallback; LOG(StorageAPI, "Scheduling deliverQuotaIncreaseCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); return false; } - + handleCurrentStatementError(); - + return false; } void SQLTransaction::handleCurrentStatementError() { // Transaction Steps 6.error - Call the statement's error callback, but if there was no error callback, - // jump to the transaction error callback - if (m_currentStatement->hasStatementErrorCallback()) { + // or the transaction was rolled back, jump to the transaction error callback + if (m_currentStatement->hasStatementErrorCallback() && !m_sqliteTransaction->wasRolledBackBySqlite()) { m_nextStep = &SQLTransaction::deliverStatementCallback; LOG(StorageAPI, "Scheduling deliverStatementCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); @@ -372,7 +409,7 @@ void SQLTransaction::handleCurrentStatementError() void SQLTransaction::deliverStatementCallback() { ASSERT(m_currentStatement); - + // Transaction Step 6.6 and 6.3(error) - If the statement callback went wrong, jump to the transaction error callback // Otherwise, continue to loop through the statement queue m_executeSqlAllowed = true; @@ -390,27 +427,18 @@ void SQLTransaction::deliverQuotaIncreaseCallback() { ASSERT(m_currentStatement); ASSERT(!m_shouldRetryCurrentStatement); - - Page* page = m_database->document()->page(); - ASSERT(page); - - RefPtr<SecurityOrigin> origin = m_database->securityOriginCopy(); - - unsigned long long currentQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get()); - page->chrome()->client()->exceededDatabaseQuota(m_database->document()->frame(), m_database->stringIdentifier()); - unsigned long long newQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get()); - - // If the new quota ended up being larger than the old quota, we will retry the statement. - if (newQuota > currentQuota) - m_shouldRetryCurrentStatement = true; - + + m_shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(this); + m_nextStep = &SQLTransaction::runStatements; LOG(StorageAPI, "Scheduling runStatements for transaction %p\n", this); m_database->scheduleTransactionStep(this); } void SQLTransaction::postflightAndCommit() -{ +{ + ASSERT(m_lockAcquired); + // Transaction Step 7 - Peform postflight steps, jumping to the error callback if they fail if (m_wrapper && !m_wrapper->performPostflight(this)) { m_transactionError = m_wrapper->sqlError(); @@ -419,10 +447,10 @@ void SQLTransaction::postflightAndCommit() handleTransactionError(false); return; } - + // Transacton Step 8+9 - Commit the transaction, jumping to the error callback if that fails ASSERT(m_sqliteTransaction); - + m_database->m_databaseAuthorizer->disable(); m_sqliteTransaction->commit(); m_database->m_databaseAuthorizer->enable(); @@ -433,21 +461,21 @@ void SQLTransaction::postflightAndCommit() handleTransactionError(false); return; } - + // The commit was successful, notify the delegates if the transaction modified this database if (m_modifiedDatabase) - DatabaseTracker::tracker().scheduleNotifyDatabaseChanged(m_database->m_securityOrigin.get(), m_database->m_name); - + m_database->transactionClient()->didCommitTransaction(this); + // Now release our unneeded callbacks, to break reference cycles. m_callback = 0; m_errorCallback = 0; - + // Transaction Step 10 - Deliver success callback, if there is one if (m_successCallback) { m_nextStep = &SQLTransaction::deliverSuccessCallback; LOG(StorageAPI, "Scheduling deliverSuccessCallback for transaction %p\n", this); m_database->scheduleTransactionCallback(this); - } else + } else cleanupAfterSuccessCallback(); } @@ -456,7 +484,7 @@ void SQLTransaction::deliverSuccessCallback() // Transaction Step 10 - Deliver success callback ASSERT(m_successCallback); m_successCallback->handleEvent(); - + // Release the last callback to break reference cycle m_successCallback = 0; @@ -469,11 +497,17 @@ void SQLTransaction::deliverSuccessCallback() void SQLTransaction::cleanupAfterSuccessCallback() { + ASSERT(m_lockAcquired); + // Transaction Step 11 - End transaction steps // There is no next step LOG(StorageAPI, "Transaction %p is complete\n", this); ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); + m_sqliteTransaction.clear(); m_nextStep = 0; + + // Release the lock on this database + m_database->transactionCoordinator()->releaseLock(this); } void SQLTransaction::handleTransactionError(bool inCallback) @@ -488,7 +522,7 @@ void SQLTransaction::handleTransactionError(bool inCallback) } return; } - + // No error callback, so fast-forward to: // Transaction Step 12 - Rollback the transaction. if (inCallback) { @@ -503,7 +537,7 @@ void SQLTransaction::handleTransactionError(bool inCallback) void SQLTransaction::deliverTransactionErrorCallback() { ASSERT(m_transactionError); - + // Transaction Step 12 - If exists, invoke error callback with the last // error to have occurred in this transaction. if (m_errorCallback) @@ -516,22 +550,24 @@ void SQLTransaction::deliverTransactionErrorCallback() void SQLTransaction::cleanupAfterTransactionErrorCallback() { + ASSERT(m_lockAcquired); + m_database->m_databaseAuthorizer->disable(); if (m_sqliteTransaction) { // Transaction Step 12 - Rollback the transaction. m_sqliteTransaction->rollback(); - + ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); m_sqliteTransaction.clear(); } m_database->m_databaseAuthorizer->enable(); - + // Transaction Step 12 - Any still-pending statements in the transaction are discarded. { MutexLocker locker(m_statementMutex); m_statementQueue.clear(); } - + // Transaction is complete! There is no next step LOG(StorageAPI, "Transaction %p is complete with an error\n", this); ASSERT(!m_database->m_sqliteDatabase.transactionInProgress()); @@ -540,6 +576,9 @@ void SQLTransaction::cleanupAfterTransactionErrorCallback() // Now release our callbacks, to break reference cycles. m_callback = 0; m_errorCallback = 0; + + // Now release the lock on this database + m_database->transactionCoordinator()->releaseLock(this); } } // namespace WebCore diff --git a/WebCore/storage/SQLTransaction.h b/WebCore/storage/SQLTransaction.h index e77c183..1b02d01 100644 --- a/WebCore/storage/SQLTransaction.h +++ b/WebCore/storage/SQLTransaction.h @@ -60,34 +60,40 @@ public: virtual ~SQLTransactionWrapper() { } virtual bool performPreflight(SQLTransaction*) = 0; virtual bool performPostflight(SQLTransaction*) = 0; - + virtual SQLError* sqlError() const = 0; }; class SQLTransaction : public ThreadSafeShared<SQLTransaction> { public: - static PassRefPtr<SQLTransaction> create(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback>, PassRefPtr<SQLTransactionWrapper>); + static PassRefPtr<SQLTransaction> create(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, + PassRefPtr<VoidCallback>, PassRefPtr<SQLTransactionWrapper>, bool readOnly = false); ~SQLTransaction(); - - void executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, + + void executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, PassRefPtr<SQLStatementCallback> callback, PassRefPtr<SQLStatementErrorCallback> callbackError, ExceptionCode& e); - + + void lockAcquired(); bool performNextStep(); void performPendingCallback(); - + Database* database() { return m_database.get(); } + bool isReadOnly() { return m_readOnly; } + void notifyDatabaseThreadIsShuttingDown(); private: - SQLTransaction(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, PassRefPtr<VoidCallback>, PassRefPtr<SQLTransactionWrapper>); + SQLTransaction(Database*, PassRefPtr<SQLTransactionCallback>, PassRefPtr<SQLTransactionErrorCallback>, + PassRefPtr<VoidCallback>, PassRefPtr<SQLTransactionWrapper>, bool readOnly); typedef void (SQLTransaction::*TransactionStepMethod)(); TransactionStepMethod m_nextStep; - + void enqueueStatement(PassRefPtr<SQLStatement>); - + void checkAndHandleClosedDatabase(); - + + void acquireLock(); void openTransactionAndPreflight(); void deliverTransactionCallback(); void scheduleToRunStatements(); @@ -109,9 +115,9 @@ private: #endif RefPtr<SQLStatement> m_currentStatement; - + bool m_executeSqlAllowed; - + RefPtr<Database> m_database; RefPtr<SQLTransactionWrapper> m_wrapper; RefPtr<SQLTransactionCallback> m_callback; @@ -120,13 +126,15 @@ private: RefPtr<SQLError> m_transactionError; bool m_shouldRetryCurrentStatement; bool m_modifiedDatabase; - + bool m_lockAcquired; + bool m_readOnly; + Mutex m_statementMutex; Deque<RefPtr<SQLStatement> > m_statementQueue; OwnPtr<SQLiteTransaction> m_sqliteTransaction; }; - + } // namespace WebCore #endif diff --git a/WebCore/storage/SQLTransaction.idl b/WebCore/storage/SQLTransaction.idl index 5d4885c..7d694e8 100644 --- a/WebCore/storage/SQLTransaction.idl +++ b/WebCore/storage/SQLTransaction.idl @@ -29,7 +29,8 @@ module storage { interface [ - Conditional=DATABASE + Conditional=DATABASE, + OmitConstructor ] SQLTransaction { [Custom] void executeSql(in DOMString sqlStatement, in ObjectArray arguments, in SQLStatementCallback callback, in SQLStatementErrorCallback errorCallback); }; diff --git a/WebCore/storage/SQLTransactionCallback.h b/WebCore/storage/SQLTransactionCallback.h index a4cd940..de3e85dc 100644 --- a/WebCore/storage/SQLTransactionCallback.h +++ b/WebCore/storage/SQLTransactionCallback.h @@ -37,7 +37,7 @@ namespace WebCore { class SQLTransaction; class SQLError; - + class SQLTransactionCallback : public ThreadSafeShared<SQLTransactionCallback> { public: virtual ~SQLTransactionCallback() { } diff --git a/WebCore/storage/SQLTransactionClient.cpp b/WebCore/storage/SQLTransactionClient.cpp new file mode 100644 index 0000000..6064c99 --- /dev/null +++ b/WebCore/storage/SQLTransactionClient.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLTransactionClient.h" + +#if ENABLE(DATABASE) + +#include "Chrome.h" +#include "ChromeClient.h" +#include "Database.h" +#include "DatabaseThread.h" +#include "DatabaseTracker.h" +#include "Document.h" +#include "OriginQuotaManager.h" +#include "Page.h" +#include "SQLTransaction.h" + +namespace WebCore { + +void SQLTransactionClient::didCommitTransaction(SQLTransaction* transaction) +{ + ASSERT(currentThread() == transaction->database()->scriptExecutionContext()->databaseThread()->getThreadID()); + Database* database = transaction->database(); + DatabaseTracker::tracker().scheduleNotifyDatabaseChanged( + database->securityOrigin(), database->stringIdentifier()); +} + +void SQLTransactionClient::didExecuteStatement(SQLTransaction* transaction) +{ + ASSERT(currentThread() == transaction->database()->scriptExecutionContext()->databaseThread()->getThreadID()); + OriginQuotaManager& manager(DatabaseTracker::tracker().originQuotaManager()); + Locker<OriginQuotaManager> locker(manager); + manager.markDatabase(transaction->database()); +} + +bool SQLTransactionClient::didExceedQuota(SQLTransaction* transaction) +{ + ASSERT(transaction->database()->scriptExecutionContext()->isContextThread()); + Database* database = transaction->database(); + + unsigned long long currentQuota = DatabaseTracker::tracker().quotaForOrigin(database->securityOrigin()); + database->scriptExecutionContext()->databaseExceededQuota(database->stringIdentifier()); + unsigned long long newQuota = DatabaseTracker::tracker().quotaForOrigin(database->securityOrigin()); + return (newQuota > currentQuota); +} + +} + +#endif // ENABLE(DATABASE) diff --git a/WebCore/storage/SQLTransactionClient.h b/WebCore/storage/SQLTransactionClient.h new file mode 100644 index 0000000..801647b --- /dev/null +++ b/WebCore/storage/SQLTransactionClient.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLTransactionClient_h +#define SQLTransactionClient_h + +#if ENABLE(DATABASE) + +#include <wtf/Noncopyable.h> + +namespace WebCore { + + class SQLTransaction; + + // A client to the SQLTransaction class. Allows SQLTransaction to notify interested + // parties that certain things have happened in a transaction. + class SQLTransactionClient : public Noncopyable { + public: + void didCommitTransaction(SQLTransaction*); + void didExecuteStatement(SQLTransaction*); + bool didExceedQuota(SQLTransaction*); + }; +} + +#endif // ENABLE(DATABASE) + +#endif // SQLTransactionClient_h diff --git a/WebCore/storage/SQLTransactionCoordinator.cpp b/WebCore/storage/SQLTransactionCoordinator.cpp new file mode 100644 index 0000000..0fe5bda --- /dev/null +++ b/WebCore/storage/SQLTransactionCoordinator.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLTransactionCoordinator.h" + +#if ENABLE(DATABASE) + +#include "CString.h" +#include "Database.h" +#include "SQLTransaction.h" +#include <wtf/Deque.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +static String getDatabaseIdentifier(SQLTransaction* transaction) +{ + Database* database = transaction->database(); + ASSERT(database); + return database->stringIdentifier(); +} + +void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& info) +{ + if (info.activeWriteTransaction || info.pendingTransactions.isEmpty()) + return; + + RefPtr<SQLTransaction> firstPendingTransaction = info.pendingTransactions.first(); + if (firstPendingTransaction->isReadOnly()) { + do { + firstPendingTransaction = info.pendingTransactions.first(); + info.pendingTransactions.removeFirst(); + info.activeReadTransactions.add(firstPendingTransaction); + firstPendingTransaction->lockAcquired(); + } while (!info.pendingTransactions.isEmpty() && info.pendingTransactions.first()->isReadOnly()); + } else if (info.activeReadTransactions.isEmpty()) { + info.pendingTransactions.removeFirst(); + info.activeWriteTransaction = firstPendingTransaction; + firstPendingTransaction->lockAcquired(); + } +} + +void SQLTransactionCoordinator::acquireLock(SQLTransaction* transaction) +{ + String dbIdentifier = getDatabaseIdentifier(transaction); + + CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier); + if (coordinationInfoIterator == m_coordinationInfoMap.end()) { + // No pending transactions for this DB + coordinationInfoIterator = m_coordinationInfoMap.add(dbIdentifier, CoordinationInfo()).first; + } + + CoordinationInfo& info = coordinationInfoIterator->second; + info.pendingTransactions.append(transaction); + processPendingTransactions(info); +} + +void SQLTransactionCoordinator::releaseLock(SQLTransaction* transaction) +{ + if (m_coordinationInfoMap.isEmpty()) + return; + + String dbIdentifier = getDatabaseIdentifier(transaction); + + CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier); + ASSERT(coordinationInfoIterator != m_coordinationInfoMap.end()); + CoordinationInfo& info = coordinationInfoIterator->second; + + if (transaction->isReadOnly()) { + ASSERT(info.activeReadTransactions.contains(transaction)); + info.activeReadTransactions.remove(transaction); + } else { + ASSERT(info.activeWriteTransaction == transaction); + info.activeWriteTransaction = 0; + } + + processPendingTransactions(info); +} + +void SQLTransactionCoordinator::shutdown() +{ + // Notify all transactions in progress that the database thread is shutting down + for (CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin(); + coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) { + CoordinationInfo& info = coordinationInfoIterator->second; + if (info.activeWriteTransaction) + info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown(); + for (HashSet<RefPtr<SQLTransaction> >::iterator activeReadTransactionsIterator = + info.activeReadTransactions.begin(); + activeReadTransactionsIterator != info.activeReadTransactions.end(); + ++activeReadTransactionsIterator) { + (*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown(); + } + } + + // Clean up all pending transactions for all databases + m_coordinationInfoMap.clear(); +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/WebCore/storage/SQLTransactionCoordinator.h b/WebCore/storage/SQLTransactionCoordinator.h new file mode 100644 index 0000000..a51084f --- /dev/null +++ b/WebCore/storage/SQLTransactionCoordinator.h @@ -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. + */ + +#ifndef SQLTransactionCoordinator_h +#define SQLTransactionCoordinator_h + +#if ENABLE(DATABASE) + +#include "CString.h" +#include "StringHash.h" +#include <wtf/Deque.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + + class SQLTransaction; + + class SQLTransactionCoordinator : public Noncopyable { + public: + void acquireLock(SQLTransaction*); + void releaseLock(SQLTransaction*); + void shutdown(); + private: + typedef Deque<RefPtr<SQLTransaction> > TransactionsQueue; + struct CoordinationInfo { + TransactionsQueue pendingTransactions; + HashSet<RefPtr<SQLTransaction> > activeReadTransactions; + RefPtr<SQLTransaction> activeWriteTransaction; + }; + // Maps database names to information about pending transactions + typedef HashMap<String, CoordinationInfo> CoordinationInfoMap; + CoordinationInfoMap m_coordinationInfoMap; + + void processPendingTransactions(CoordinationInfo& info); + }; +} + +#endif // ENABLE(DATABASE) + +#endif // SQLTransactionCoordinator_h diff --git a/WebCore/storage/SQLTransactionErrorCallback.h b/WebCore/storage/SQLTransactionErrorCallback.h index 9dd3fd3..de99212 100644 --- a/WebCore/storage/SQLTransactionErrorCallback.h +++ b/WebCore/storage/SQLTransactionErrorCallback.h @@ -34,19 +34,17 @@ #include <wtf/Threading.h> namespace WebCore { - + class SQLError; - + class SQLTransactionErrorCallback : public ThreadSafeShared<SQLTransactionErrorCallback> { public: virtual ~SQLTransactionErrorCallback() { } virtual void handleEvent(SQLError*) = 0; }; - + } #endif #endif // SQLTransactionErrorCallback_h - - diff --git a/WebCore/storage/Storage.cpp b/WebCore/storage/Storage.cpp index d5fc065..0a8eed7 100644 --- a/WebCore/storage/Storage.cpp +++ b/WebCore/storage/Storage.cpp @@ -20,9 +20,9 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + #include "config.h" #include "Storage.h" @@ -111,4 +111,3 @@ bool Storage::contains(const String& key) const } #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/Storage.h b/WebCore/storage/Storage.h index d68e9bc..06cc97b 100644 --- a/WebCore/storage/Storage.h +++ b/WebCore/storage/Storage.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef Storage_h @@ -53,11 +53,12 @@ namespace WebCore { bool contains(const String& key) const; + Frame* frame() { return m_frame; } void disconnectFrame() { m_frame = 0; } private: Storage(Frame*, PassRefPtr<StorageArea>); - + Frame* m_frame; RefPtr<StorageArea> m_storageArea; }; diff --git a/WebCore/storage/Storage.idl b/WebCore/storage/Storage.idl index 7127efd..ffd1af1 100644 --- a/WebCore/storage/Storage.idl +++ b/WebCore/storage/Storage.idl @@ -26,7 +26,6 @@ module storage { interface [ - GenerateConstructor, HasNameGetter, CustomDeleteProperty, CustomGetPropertyNames, diff --git a/WebCore/storage/StorageArea.h b/WebCore/storage/StorageArea.h index f74405a..6081240 100644 --- a/WebCore/storage/StorageArea.h +++ b/WebCore/storage/StorageArea.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef StorageArea_h @@ -31,7 +31,7 @@ #include "PlatformString.h" #include <wtf/PassRefPtr.h> -#include <wtf/Threading.h> +#include <wtf/RefCounted.h> namespace WebCore { @@ -42,7 +42,7 @@ namespace WebCore { enum StorageType { LocalStorage, SessionStorage }; // This interface is required for Chromium since these actions need to be proxied between processes. - class StorageArea : public ThreadSafeShared<StorageArea> { + class StorageArea : public RefCounted<StorageArea> { public: virtual ~StorageArea() { } @@ -50,9 +50,9 @@ namespace WebCore { virtual unsigned length() const = 0; virtual String key(unsigned index) const = 0; virtual String getItem(const String& key) const = 0; - virtual void setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame) = 0; - virtual void removeItem(const String& key, Frame* sourceFrame) = 0; - virtual void clear(Frame* sourceFrame) = 0; + virtual String setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame) = 0; + virtual String removeItem(const String& key, Frame* sourceFrame) = 0; + virtual bool clear(Frame* sourceFrame) = 0; virtual bool contains(const String& key) const = 0; }; diff --git a/WebCore/storage/StorageAreaImpl.cpp b/WebCore/storage/StorageAreaImpl.cpp index f53edd3..aa04781 100644 --- a/WebCore/storage/StorageAreaImpl.cpp +++ b/WebCore/storage/StorageAreaImpl.cpp @@ -28,16 +28,12 @@ #if ENABLE(DOM_STORAGE) -#include "DOMWindow.h" -#include "EventNames.h" #include "ExceptionCode.h" #include "Frame.h" #include "Page.h" -#include "PageGroup.h" -#include "SecurityOrigin.h" #include "Settings.h" -#include "StorageEvent.h" #include "StorageAreaSync.h" +#include "StorageEventDispatcher.h" #include "StorageMap.h" #include "StorageSyncManager.h" @@ -45,24 +41,31 @@ namespace WebCore { StorageAreaImpl::~StorageAreaImpl() { + ASSERT(isMainThread()); } -StorageAreaImpl::StorageAreaImpl(StorageType storageType, PassRefPtr<SecurityOrigin> origin, PassRefPtr<StorageSyncManager> syncManager) +PassRefPtr<StorageAreaImpl> StorageAreaImpl::create(StorageType storageType, PassRefPtr<SecurityOrigin> origin, PassRefPtr<StorageSyncManager> syncManager, unsigned quota) +{ + return adoptRef(new StorageAreaImpl(storageType, origin, syncManager, quota)); +} + +StorageAreaImpl::StorageAreaImpl(StorageType storageType, PassRefPtr<SecurityOrigin> origin, PassRefPtr<StorageSyncManager> syncManager, unsigned quota) : m_storageType(storageType) , m_securityOrigin(origin) - , m_storageMap(StorageMap::create()) + , m_storageMap(StorageMap::create(quota)) , m_storageSyncManager(syncManager) #ifndef NDEBUG , m_isShutdown(false) #endif { + ASSERT(isMainThread()); ASSERT(m_securityOrigin); ASSERT(m_storageMap); // 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); } } @@ -82,6 +85,7 @@ StorageAreaImpl::StorageAreaImpl(StorageAreaImpl* area) , m_isShutdown(area->m_isShutdown) #endif { + ASSERT(isMainThread()); ASSERT(m_securityOrigin); ASSERT(m_storageMap); ASSERT(!m_isShutdown); @@ -103,6 +107,8 @@ static bool privateBrowsingEnabled(Frame* frame) unsigned StorageAreaImpl::length() const { ASSERT(!m_isShutdown); + blockUntilImportComplete(); + return m_storageMap->length(); } @@ -110,6 +116,7 @@ String StorageAreaImpl::key(unsigned index) const { ASSERT(!m_isShutdown); blockUntilImportComplete(); + return m_storageMap->key(index); } @@ -121,7 +128,7 @@ String StorageAreaImpl::getItem(const String& key) const return m_storageMap->getItem(key); } -void StorageAreaImpl::setItem(const String& key, const String& value, ExceptionCode& ec, Frame* frame) +String StorageAreaImpl::setItem(const String& key, const String& value, ExceptionCode& ec, Frame* frame) { ASSERT(!m_isShutdown); ASSERT(!value.isNull()); @@ -129,64 +136,69 @@ void StorageAreaImpl::setItem(const String& key, const String& value, ExceptionC if (privateBrowsingEnabled(frame)) { ec = QUOTA_EXCEEDED_ERR; - return; + return String(); } - // FIXME: For LocalStorage where a disk quota will be enforced, here is where we need to do quota checking. - // If we decide to enforce a memory quota for SessionStorage, this is where we'd do that, also. - // if (<over quota>) { - // ec = QUOTA_EXCEEDED_ERR; - // return; - // } - String oldValue; - RefPtr<StorageMap> newMap = m_storageMap->setItem(key, value, oldValue); - + bool quotaException; + RefPtr<StorageMap> newMap = m_storageMap->setItem(key, value, oldValue, quotaException); if (newMap) m_storageMap = newMap.release(); - // Only notify the client if an item was actually changed - if (oldValue != value) { - if (m_storageAreaSync) - m_storageAreaSync->scheduleItemForSync(key, value); - dispatchStorageEvent(key, oldValue, value, frame); + if (quotaException) { + ec = QUOTA_EXCEEDED_ERR; + return oldValue; } + + if (oldValue == value) + return oldValue; + + if (m_storageAreaSync) + m_storageAreaSync->scheduleItemForSync(key, value); + StorageEventDispatcher::dispatch(key, oldValue, value, m_storageType, m_securityOrigin.get(), frame); + return oldValue; } -void StorageAreaImpl::removeItem(const String& key, Frame* frame) +String StorageAreaImpl::removeItem(const String& key, Frame* frame) { ASSERT(!m_isShutdown); blockUntilImportComplete(); if (privateBrowsingEnabled(frame)) - return; + return String(); String oldValue; RefPtr<StorageMap> newMap = m_storageMap->removeItem(key, oldValue); 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()); - dispatchStorageEvent(key, oldValue, String(), frame); - } + if (oldValue.isNull()) + return oldValue; + + if (m_storageAreaSync) + m_storageAreaSync->scheduleItemForSync(key, String()); + StorageEventDispatcher::dispatch(key, oldValue, String(), m_storageType, m_securityOrigin.get(), frame); + return oldValue; } -void StorageAreaImpl::clear(Frame* frame) +bool StorageAreaImpl::clear(Frame* frame) { ASSERT(!m_isShutdown); blockUntilImportComplete(); if (privateBrowsingEnabled(frame)) - return; + return false; + + if (!m_storageMap->length()) + return false; - m_storageMap = StorageMap::create(); + unsigned quota = m_storageMap->quota(); + m_storageMap = StorageMap::create(quota); if (m_storageAreaSync) m_storageAreaSync->scheduleClear(); - dispatchStorageEvent(String(), String(), String(), frame); + StorageEventDispatcher::dispatch(String(), String(), String(), m_storageType, m_securityOrigin.get(), frame); + return true; } bool StorageAreaImpl::contains(const String& key) const @@ -203,11 +215,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) @@ -224,46 +231,6 @@ void StorageAreaImpl::blockUntilImportComplete() const m_storageAreaSync->blockUntilImportComplete(); } -void StorageAreaImpl::dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame) -{ -#if PLATFORM(CHROMIUM) - // FIXME: Events are currently broken in Chromium. - return; -#endif - - Page* page = sourceFrame->page(); - if (!page) - return; - - // We need to copy all relevant frames from every page to a vector since sending the event to one frame might mutate the frame tree - // of any given page in the group or mutate the page group itself. - Vector<RefPtr<Frame> > frames; - if (m_storageType == SessionStorage) { - // Send events only to our page. - for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { - if (frame->document()->securityOrigin()->equal(securityOrigin())) - frames.append(frame); - } - - for (unsigned i = 0; i < frames.size(); ++i) - frames[i]->document()->dispatchWindowEvent(StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, sourceFrame->document()->documentURI(), sourceFrame->domWindow(), frames[i]->domWindow()->sessionStorage())); - } else { - // Send events to every page. - const HashSet<Page*>& pages = page->group().pages(); - HashSet<Page*>::const_iterator end = pages.end(); - for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) { - for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) { - if (frame->document()->securityOrigin()->equal(securityOrigin())) - frames.append(frame); - } - } - - for (unsigned i = 0; i < frames.size(); ++i) - frames[i]->document()->dispatchWindowEvent(StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, sourceFrame->document()->documentURI(), sourceFrame->domWindow(), frames[i]->domWindow()->localStorage())); - } -} - } #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/StorageAreaImpl.h b/WebCore/storage/StorageAreaImpl.h index ef93d98..60d72cb 100644 --- a/WebCore/storage/StorageAreaImpl.h +++ b/WebCore/storage/StorageAreaImpl.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef StorageAreaImpl_h @@ -30,6 +30,7 @@ #include "StorageArea.h" +#include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> namespace WebCore { @@ -40,32 +41,30 @@ namespace WebCore { class StorageAreaImpl : public StorageArea { public: - StorageAreaImpl(StorageType, PassRefPtr<SecurityOrigin>, PassRefPtr<StorageSyncManager>); + static PassRefPtr<StorageAreaImpl> create(StorageType, PassRefPtr<SecurityOrigin>, PassRefPtr<StorageSyncManager>, unsigned quota); virtual ~StorageAreaImpl(); // The HTML5 DOM Storage API (and contains) virtual unsigned length() const; virtual String key(unsigned index) const; virtual String getItem(const String& key) const; - virtual void setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame); - virtual void removeItem(const String& key, Frame* sourceFrame); - virtual void clear(Frame* sourceFrame); + virtual String setItem(const String& key, const String& value, ExceptionCode& ec, Frame* sourceFrame); + virtual String removeItem(const String& key, Frame* sourceFrame); + virtual bool clear(Frame* sourceFrame); virtual bool contains(const String& key) const; PassRefPtr<StorageAreaImpl> copy(); void close(); - // 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); StorageAreaImpl(StorageAreaImpl*); void blockUntilImportComplete() const; - void dispatchStorageEvent(const String& key, const String& oldValue, const String& newValue, Frame* sourceFrame); - StorageType m_storageType; RefPtr<SecurityOrigin> m_securityOrigin; RefPtr<StorageMap> m_storageMap; diff --git a/WebCore/storage/StorageAreaSync.cpp b/WebCore/storage/StorageAreaSync.cpp index 01d2a65..d4eba76 100644 --- a/WebCore/storage/StorageAreaSync.cpp +++ b/WebCore/storage/StorageAreaSync.cpp @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -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,21 +43,23 @@ 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) { + ASSERT(isMainThread()); ASSERT(m_storageArea); ASSERT(m_syncManager); @@ -68,7 +71,9 @@ StorageAreaSync::StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManag StorageAreaSync::~StorageAreaSync() { + ASSERT(isMainThread()); ASSERT(!m_syncTimer.isActive()); + ASSERT(m_finalSyncScheduled); } void StorageAreaSync::scheduleFinalSync() @@ -76,7 +81,8 @@ void StorageAreaSync::scheduleFinalSync() ASSERT(isMainThread()); // FIXME: We do this to avoid races, but it'd be better to make things safe without blocking. blockUntilImportComplete(); - + m_storageArea = 0; // This is done in blockUntilImportComplete() but this is here as a form of documentation that we must be absolutely sure the ref count cycle is broken. + if (m_syncTimer.isActive()) m_syncTimer.stop(); else { @@ -127,7 +133,7 @@ void StorageAreaSync::syncTimerFired(Timer<StorageAreaSync>*) HashMap<String, String>::iterator it = m_changedItems.begin(); HashMap<String, String>::iterator end = m_changedItems.end(); - + { MutexLocker locker(m_syncLock); @@ -138,7 +144,7 @@ void StorageAreaSync::syncTimerFired(Timer<StorageAreaSync>*) } for (; it != end; ++it) - m_itemsPendingSync.set(it->first.copy(), it->second.copy()); + m_itemsPendingSync.set(it->first.crossThreadString(), it->second.crossThreadString()); if (!m_syncScheduled) { m_syncScheduled = true; @@ -163,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"); @@ -182,14 +188,14 @@ void StorageAreaSync::performImport() markImported(); return; } - + SQLiteStatement query(m_database, "SELECT key, value FROM ItemTable"); if (query.prepare() != SQLResultOk) { LOG_ERROR("Unable to select items from ItemTable for local storage"); markImported(); return; } - + HashMap<String, String> itemMap; int result = query.step(); @@ -204,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(); } @@ -236,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) @@ -265,7 +261,7 @@ void StorageAreaSync::sync(bool clearItems, const HashMap<String, String>& items LOG_ERROR("Failed to prepare clear statement - cannot write to local storage database"); return; } - + int result = clear.step(); if (result != SQLResultDone) { LOG_ERROR("Failed to clear all items in the local storage database - %i", result); @@ -289,11 +285,11 @@ void StorageAreaSync::sync(bool clearItems, const HashMap<String, String>& items for (HashMap<String, String>::const_iterator it = items.begin(); it != end; ++it) { // Based on the null-ness of the second argument, decide whether this is an insert or a delete. - SQLiteStatement& query = it->second.isNull() ? remove : insert; + SQLiteStatement& query = it->second.isNull() ? remove : insert; query.bindText(1, it->first); - // If the second argument is non-null, we're doing an insert, so bind it as the value. + // If the second argument is non-null, we're doing an insert, so bind it as the value. if (!it->second.isNull()) query.bindText(2, it->second); @@ -335,4 +331,3 @@ void StorageAreaSync::performSync() } // namespace WebCore #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/StorageAreaSync.h b/WebCore/storage/StorageAreaSync.h index e436bef..9afdfde 100644 --- a/WebCore/storage/StorageAreaSync.h +++ b/WebCore/storage/StorageAreaSync.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef StorageAreaSync_h @@ -39,27 +39,27 @@ namespace WebCore { class Frame; class StorageAreaImpl; class StorageSyncManager; - + 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); - Timer<StorageAreaSync> m_syncTimer; + Timer<StorageAreaSync> m_syncTimer; HashMap<String, String> m_changedItems; bool m_itemsCleared; - + bool m_finalSyncScheduled; RefPtr<StorageAreaImpl> m_storageArea; @@ -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/StorageEvent.cpp b/WebCore/storage/StorageEvent.cpp index 2e620d5..126aca0 100644 --- a/WebCore/storage/StorageEvent.cpp +++ b/WebCore/storage/StorageEvent.cpp @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -28,7 +28,6 @@ #if ENABLE(DOM_STORAGE) -#include "DOMWindow.h" #include "Storage.h" namespace WebCore { @@ -42,23 +41,22 @@ StorageEvent::StorageEvent() { } -PassRefPtr<StorageEvent> StorageEvent::create(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source, Storage* storageArea) +PassRefPtr<StorageEvent> StorageEvent::create(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, Storage* storageArea) { - return adoptRef(new StorageEvent(type, key, oldValue, newValue, uri, source, storageArea)); + return adoptRef(new StorageEvent(type, key, oldValue, newValue, uri, storageArea)); } -StorageEvent::StorageEvent(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source, Storage* storageArea) - : Event(type, false, true) +StorageEvent::StorageEvent(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, Storage* storageArea) + : Event(type, false, false) , m_key(key) , m_oldValue(oldValue) , m_newValue(newValue) , m_uri(uri) - , m_source(source) , m_storageArea(storageArea) { } -void StorageEvent::initStorageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source, Storage* storageArea) +void StorageEvent::initStorageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& key, const String& oldValue, const String& newValue, const String& uri, Storage* storageArea) { if (dispatched()) return; @@ -69,11 +67,9 @@ void StorageEvent::initStorageEvent(const AtomicString& type, bool canBubble, bo m_oldValue = oldValue; m_newValue = newValue; m_uri = uri; - m_source = source; m_storageArea = storageArea; } } // namespace WebCore #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/StorageEvent.h b/WebCore/storage/StorageEvent.h index 703fb5a..fa7535b 100644 --- a/WebCore/storage/StorageEvent.h +++ b/WebCore/storage/StorageEvent.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef StorageEvent_h @@ -33,38 +33,35 @@ namespace WebCore { - class DOMWindow; class Storage; class StorageEvent : public Event { public: static PassRefPtr<StorageEvent> create(); - static PassRefPtr<StorageEvent> create(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source, Storage* storageArea); + static PassRefPtr<StorageEvent> create(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, Storage* storageArea); const String& key() const { return m_key; } const String& oldValue() const { return m_oldValue; } const String& newValue() const { return m_newValue; } const String& uri() const { return m_uri; } - DOMWindow* source() const { return m_source.get(); } Storage* storageArea() const { return m_storageArea.get(); } - void initStorageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source, Storage* storageArea); + void initStorageEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& key, const String& oldValue, const String& newValue, const String& uri, Storage* storageArea); // Needed once we support init<blank>EventNS - // void initStorageEventNS(in DOMString namespaceURI, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString uriArg, in Window sourceArg, Storage storageAreaArg); + // void initStorageEventNS(in DOMString namespaceURI, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString uriArg, Storage storageAreaArg); virtual bool isStorageEvent() const { return true; } - private: + private: StorageEvent(); - StorageEvent(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, PassRefPtr<DOMWindow> source, Storage* storageArea); - + StorageEvent(const AtomicString& type, const String& key, const String& oldValue, const String& newValue, const String& uri, Storage* storageArea); + String m_key; String m_oldValue; String m_newValue; String m_uri; - RefPtr<DOMWindow> m_source; - RefPtr<Storage> m_storageArea; + RefPtr<Storage> m_storageArea; }; } // namespace WebCore diff --git a/WebCore/storage/StorageEvent.idl b/WebCore/storage/StorageEvent.idl index 5a3f993..3e77eda 100644 --- a/WebCore/storage/StorageEvent.idl +++ b/WebCore/storage/StorageEvent.idl @@ -26,19 +26,17 @@ module storage { interface [ - GenerateConstructor, Conditional=DOM_STORAGE ] StorageEvent : Event { - readonly attribute DOMString key; + readonly attribute [ConvertNullStringTo=Null] DOMString key; readonly attribute [ConvertNullStringTo=Null] DOMString oldValue; readonly attribute [ConvertNullStringTo=Null] DOMString newValue; readonly attribute DOMString uri; - readonly attribute DOMWindow source; readonly attribute Storage storageArea; - void initStorageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in [ConvertNullToNullString] DOMString oldValueArg, in [ConvertNullToNullString] DOMString newValueArg, in DOMString uriArg, in DOMWindow sourceArg, in Storage storageAreaArg); + void initStorageEvent(in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in [ConvertNullToNullString] DOMString oldValueArg, in [ConvertNullToNullString] DOMString newValueArg, in DOMString uriArg, in Storage storageAreaArg); // Needed once we support init<blank>EventNS - // void initStorageEventNS(in DOMString namespaceURI, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString uriArg, in DOMWindow sourceArg, in Storage storageAreaArg); + // void initStorageEventNS(in DOMString namespaceURI, in DOMString typeArg, in boolean canBubbleArg, in boolean cancelableArg, in DOMString keyArg, in DOMString oldValueArg, in DOMString newValueArg, in DOMString uriArg, in Storage storageAreaArg); }; } diff --git a/WebCore/storage/StorageEventDispatcher.cpp b/WebCore/storage/StorageEventDispatcher.cpp new file mode 100644 index 0000000..dc0295b --- /dev/null +++ b/WebCore/storage/StorageEventDispatcher.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "StorageEventDispatcher.h" + +#if ENABLE(DOM_STORAGE) + +#include "DOMWindow.h" +#include "EventNames.h" +#include "Frame.h" +#include "Page.h" +#include "PageGroup.h" +#include "SecurityOrigin.h" +#include "StorageEvent.h" + +namespace WebCore { + +void StorageEventDispatcher::dispatch(const String& key, const String& oldValue, const String& newValue, StorageType storageType, SecurityOrigin* securityOrigin, Frame* sourceFrame) +{ + Page* page = sourceFrame->page(); + if (!page) + return; + + // We need to copy all relevant frames from every page to a vector since sending the event to one frame might mutate the frame tree + // of any given page in the group or mutate the page group itself. + Vector<RefPtr<Frame> > frames; + if (storageType == SessionStorage) { + // Send events only to our page. + for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (sourceFrame != frame && frame->document()->securityOrigin()->equal(securityOrigin)) + frames.append(frame); + } + + for (unsigned i = 0; i < frames.size(); ++i) + frames[i]->document()->enqueueStorageEvent(StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, sourceFrame->document()->url(), frames[i]->domWindow()->sessionStorage())); + } else { + // Send events to every page. + const HashSet<Page*>& pages = page->group().pages(); + HashSet<Page*>::const_iterator end = pages.end(); + for (HashSet<Page*>::const_iterator it = pages.begin(); it != end; ++it) { + for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) { + if (sourceFrame != frame && frame->document()->securityOrigin()->equal(securityOrigin)) + frames.append(frame); + } + } + + for (unsigned i = 0; i < frames.size(); ++i) + frames[i]->document()->enqueueStorageEvent(StorageEvent::create(eventNames().storageEvent, key, oldValue, newValue, sourceFrame->document()->url(), frames[i]->domWindow()->localStorage())); + } +} + +} + +#endif // ENABLE(DOM_STORAGE) diff --git a/WebCore/storage/StorageEventDispatcher.h b/WebCore/storage/StorageEventDispatcher.h new file mode 100644 index 0000000..f4a98ef --- /dev/null +++ b/WebCore/storage/StorageEventDispatcher.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef StorageEventDispatcher_h +#define StorageEventDispatcher_h + +#if ENABLE(DOM_STORAGE) + +#include "PlatformString.h" +#include "StorageArea.h" + +namespace WebCore { + + // This is in its own class since Chromium must override it. + class StorageEventDispatcher { + public: + static void dispatch(const String& key, const String& oldValue, const String& newValue, StorageType, SecurityOrigin*, Frame* sourceFrame); + + private: + // Do not instantiate. + StorageEventDispatcher(); + }; + +} // namespace WebCore + +#endif // ENABLE(DOM_STORAGE) + +#endif // StorageEventDispatcher_h diff --git a/WebCore/storage/StorageMap.cpp b/WebCore/storage/StorageMap.cpp index 32eb350..790fde2 100644 --- a/WebCore/storage/StorageMap.cpp +++ b/WebCore/storage/StorageMap.cpp @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -30,21 +30,24 @@ namespace WebCore { -PassRefPtr<StorageMap> StorageMap::create() +PassRefPtr<StorageMap> StorageMap::create(unsigned quota) { - return adoptRef(new StorageMap); + return adoptRef(new StorageMap(quota)); } - -StorageMap::StorageMap() + +StorageMap::StorageMap(unsigned quota) : m_iterator(m_map.end()) , m_iteratorIndex(UINT_MAX) + , m_quotaSize(quota) // quota measured in bytes + , m_currentLength(0) { } PassRefPtr<StorageMap> StorageMap::copy() { - RefPtr<StorageMap> newMap = create(); + RefPtr<StorageMap> newMap = create(m_quotaSize); newMap->m_map = m_map; + newMap->m_currentLength = m_currentLength; return newMap.release(); } @@ -54,22 +57,22 @@ void StorageMap::invalidateIterator() m_iteratorIndex = UINT_MAX; } -void StorageMap::setIteratorToIndex(unsigned index) const +void StorageMap::setIteratorToIndex(unsigned index) { // FIXME: Once we have bidirectional iterators for HashMap we can be more intelligent about this. - // The requested index will be closest to begin(), our current iterator, or end(), and we + // The requested index will be closest to begin(), our current iterator, or end(), and we // can take the shortest route. // Until that mechanism is available, we'll always increment our iterator from begin() or current. - + if (m_iteratorIndex == index) return; - + if (index < m_iteratorIndex) { m_iteratorIndex = 0; m_iterator = m_map.begin(); ASSERT(m_iterator != m_map.end()); } - + while (m_iteratorIndex < index) { ++m_iteratorIndex; ++m_iterator; @@ -82,11 +85,11 @@ unsigned StorageMap::length() const return m_map.size(); } -String StorageMap::key(unsigned index) const +String StorageMap::key(unsigned index) { if (index >= length()) return String(); - + setIteratorToIndex(index); return m_iterator->first; } @@ -96,27 +99,43 @@ String StorageMap::getItem(const String& key) const return m_map.get(key); } -PassRefPtr<StorageMap> StorageMap::setItem(const String& key, const String& value, String& oldValue) +PassRefPtr<StorageMap> StorageMap::setItem(const String& key, const String& value, String& oldValue, bool& quotaException) { ASSERT(!value.isNull()); - + quotaException = false; + // Implement copy-on-write semantics here. We're guaranteed that the only refs of StorageMaps belong to Storage objects // so if more than one Storage object refs this map, copy it before mutating it. if (refCount() > 1) { RefPtr<StorageMap> newStorageMap = copy(); - newStorageMap->setItem(key, value, oldValue); + newStorageMap->setItem(key, value, oldValue, quotaException); return newStorageMap.release(); } - pair<HashMap<String, String>::iterator, bool> addResult = m_map.add(key, value); + // 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(); - if (addResult.second) { - // There was no "oldValue" so null it out. - oldValue = String(); - } else { - oldValue = addResult.first->second; - addResult.first->second = value; + oldValue = m_map.get(key); + overflow |= newLength - oldValue.length() > newLength; + newLength -= oldValue.length(); + + unsigned adjustedKeyLength = oldValue.isNull() ? key.length() : 0; + overflow |= newLength + adjustedKeyLength < newLength; + newLength += adjustedKeyLength; + + ASSERT(!overflow); // Overflow is bad...even if quotas are off. + bool overQuota = newLength > m_quotaSize / sizeof(UChar); + if (m_quotaSize != noQuota && (overflow || overQuota)) { + quotaException = true; + return 0; } + m_currentLength = newLength; + + pair<HashMap<String, String>::iterator, bool> addResult = m_map.add(key, value); + if (!addResult.second) + addResult.first->second = value; invalidateIterator(); @@ -134,8 +153,13 @@ PassRefPtr<StorageMap> StorageMap::removeItem(const String& key, String& oldValu } oldValue = m_map.take(key); - if (!oldValue.isNull()) + if (!oldValue.isNull()) { invalidateIterator(); + ASSERT(m_currentLength - key.length() <= m_currentLength); + m_currentLength -= key.length(); + } + ASSERT(m_currentLength - oldValue.length() <= m_currentLength); + m_currentLength -= oldValue.length(); return 0; } @@ -145,17 +169,19 @@ bool StorageMap::contains(const String& key) const return m_map.contains(key); } -void StorageMap::importItem(const String& key, const String& value) const +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.copy(), String()); + 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. - if (result.second) - result.first->second = value.copy(); + ASSERT(m_currentLength + key.length() >= m_currentLength); + m_currentLength += key.length(); + ASSERT(m_currentLength + value.length() >= m_currentLength); + m_currentLength += value.length(); } } #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/StorageMap.h b/WebCore/storage/StorageMap.h index 95b4047..fa5f46c 100644 --- a/WebCore/storage/StorageMap.h +++ b/WebCore/storage/StorageMap.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef StorageMap_h @@ -39,27 +39,35 @@ namespace WebCore { class StorageMap : public RefCounted<StorageMap> { public: - static PassRefPtr<StorageMap> create(); + // Quota size mesured in bytes. + static PassRefPtr<StorageMap> create(unsigned quotaSize); unsigned length() const; - String key(unsigned index) const; + String key(unsigned index); String getItem(const String&) const; - PassRefPtr<StorageMap> setItem(const String& key, const String& value, String& oldValue); + PassRefPtr<StorageMap> setItem(const String& key, const String& value, String& oldValue, bool& quota_exception); PassRefPtr<StorageMap> removeItem(const String&, String& oldValue); bool contains(const String& key) const; - void importItem(const String& key, const String& value) const; + void importItem(const String& key, const String& value); + + unsigned quota() const { return m_quotaSize; } + + static const unsigned noQuota = UINT_MAX; private: - StorageMap(); + StorageMap(unsigned quota); PassRefPtr<StorageMap> copy(); void invalidateIterator(); - void setIteratorToIndex(unsigned) const; + void setIteratorToIndex(unsigned); + + HashMap<String, String> m_map; + HashMap<String, String>::iterator m_iterator; + unsigned m_iteratorIndex; - mutable HashMap<String, String> m_map; - mutable HashMap<String, String>::iterator m_iterator; - mutable unsigned m_iteratorIndex; + unsigned m_quotaSize; // Measured in bytes. + unsigned m_currentLength; // Measured in UChars. }; } // namespace WebCore diff --git a/WebCore/storage/StorageNamespace.cpp b/WebCore/storage/StorageNamespace.cpp index 6fcae63..b54ba16 100644 --- a/WebCore/storage/StorageNamespace.cpp +++ b/WebCore/storage/StorageNamespace.cpp @@ -36,12 +36,13 @@ namespace WebCore { -PassRefPtr<StorageNamespace> StorageNamespace::localStorageNamespace(const String& path) +PassRefPtr<StorageNamespace> StorageNamespace::localStorageNamespace(const String& path, unsigned quota) { - return StorageNamespaceImpl::localStorageNamespace(path); + return StorageNamespaceImpl::localStorageNamespace(path, quota); } -PassRefPtr<StorageNamespace> StorageNamespace::sessionStorageNamespace() +// The page argument is only used by the Chromium port. +PassRefPtr<StorageNamespace> StorageNamespace::sessionStorageNamespace(Page*) { return StorageNamespaceImpl::sessionStorageNamespace(); } diff --git a/WebCore/storage/StorageNamespace.h b/WebCore/storage/StorageNamespace.h index edbe339..e84e5a6 100644 --- a/WebCore/storage/StorageNamespace.h +++ b/WebCore/storage/StorageNamespace.h @@ -35,20 +35,22 @@ namespace WebCore { - class SecurityOrigin; - class StorageArea; - - // This interface is required for Chromium since these actions need to be proxied between processes. - class StorageNamespace : public RefCounted<StorageNamespace> { - public: - static PassRefPtr<StorageNamespace> localStorageNamespace(const String& path); - static PassRefPtr<StorageNamespace> sessionStorageNamespace(); - - virtual ~StorageNamespace() { } - virtual PassRefPtr<StorageArea> storageArea(SecurityOrigin*) = 0; - virtual PassRefPtr<StorageNamespace> copy() = 0; - virtual void close() = 0; - }; +class Page; +class SecurityOrigin; +class StorageArea; + +// This interface is required for Chromium since these actions need to be proxied between processes. +class StorageNamespace : public RefCounted<StorageNamespace> { +public: + static PassRefPtr<StorageNamespace> localStorageNamespace(const String& path, unsigned quota); + static PassRefPtr<StorageNamespace> sessionStorageNamespace(Page*); + + virtual ~StorageNamespace() { } + virtual PassRefPtr<StorageArea> storageArea(PassRefPtr<SecurityOrigin>) = 0; + virtual PassRefPtr<StorageNamespace> copy() = 0; + virtual void close() = 0; + virtual void unlock() = 0; +}; } // namespace WebCore diff --git a/WebCore/storage/StorageNamespaceImpl.cpp b/WebCore/storage/StorageNamespaceImpl.cpp index b4caaeb..19ff6b4 100644 --- a/WebCore/storage/StorageNamespaceImpl.cpp +++ b/WebCore/storage/StorageNamespaceImpl.cpp @@ -31,6 +31,7 @@ #include "SecurityOriginHash.h" #include "StringHash.h" #include "StorageAreaImpl.h" +#include "StorageMap.h" #include "StorageSyncManager.h" #include <wtf/StdLibExtras.h> @@ -44,12 +45,12 @@ static LocalStorageNamespaceMap& localStorageNamespaceMap() return localStorageNamespaceMap; } -PassRefPtr<StorageNamespace> StorageNamespaceImpl::localStorageNamespace(const String& path) +PassRefPtr<StorageNamespace> StorageNamespaceImpl::localStorageNamespace(const String& path, unsigned quota) { const String lookupPath = path.isNull() ? String("") : path; LocalStorageNamespaceMap::iterator it = localStorageNamespaceMap().find(lookupPath); if (it == localStorageNamespaceMap().end()) { - RefPtr<StorageNamespace> storageNamespace = adoptRef(new StorageNamespaceImpl(LocalStorage, lookupPath)); + RefPtr<StorageNamespace> storageNamespace = adoptRef(new StorageNamespaceImpl(LocalStorage, lookupPath, quota)); localStorageNamespaceMap().set(lookupPath, storageNamespace.get()); return storageNamespace.release(); } @@ -59,13 +60,14 @@ PassRefPtr<StorageNamespace> StorageNamespaceImpl::localStorageNamespace(const S PassRefPtr<StorageNamespace> StorageNamespaceImpl::sessionStorageNamespace() { - return adoptRef(new StorageNamespaceImpl(SessionStorage, String())); + return adoptRef(new StorageNamespaceImpl(SessionStorage, String(), StorageMap::noQuota)); } -StorageNamespaceImpl::StorageNamespaceImpl(StorageType storageType, const String& path) +StorageNamespaceImpl::StorageNamespaceImpl(StorageType storageType, const String& path, unsigned quota) : m_storageType(storageType) - , m_path(path.copy()) // Copy makes it safe for our other thread to access the path. + , m_path(path.crossThreadString()) , m_syncManager(0) + , m_quota(quota) , m_isShutdown(false) { if (m_storageType == LocalStorage && !m_path.isEmpty()) @@ -80,7 +82,7 @@ StorageNamespaceImpl::~StorageNamespaceImpl() ASSERT(localStorageNamespaceMap().get(m_path) == this); localStorageNamespaceMap().remove(m_path); } - + if (!m_isShutdown) close(); } @@ -91,7 +93,7 @@ PassRefPtr<StorageNamespace> StorageNamespaceImpl::copy() ASSERT(!m_isShutdown); ASSERT(m_storageType == SessionStorage); - StorageNamespaceImpl* newNamespace = new StorageNamespaceImpl(m_storageType, m_path); + StorageNamespaceImpl* newNamespace = new StorageNamespaceImpl(m_storageType, m_path, m_quota); StorageAreaMap::iterator end = m_storageAreaMap.end(); for (StorageAreaMap::iterator i = m_storageAreaMap.begin(); i != end; ++i) @@ -99,17 +101,18 @@ PassRefPtr<StorageNamespace> StorageNamespaceImpl::copy() return adoptRef(newNamespace); } -PassRefPtr<StorageArea> StorageNamespaceImpl::storageArea(SecurityOrigin* origin) +PassRefPtr<StorageArea> StorageNamespaceImpl::storageArea(PassRefPtr<SecurityOrigin> prpOrigin) { ASSERT(isMainThread()); ASSERT(!m_isShutdown); + RefPtr<SecurityOrigin> origin = prpOrigin; RefPtr<StorageAreaImpl> storageArea; if (storageArea = m_storageAreaMap.get(origin)) return storageArea.release(); - storageArea = adoptRef(new StorageAreaImpl(m_storageType, origin, m_syncManager)); - m_storageAreaMap.set(origin, storageArea); + storageArea = StorageAreaImpl::create(m_storageType, origin, m_syncManager, m_quota); + m_storageAreaMap.set(origin.release(), storageArea); return storageArea.release(); } @@ -117,7 +120,7 @@ void StorageNamespaceImpl::close() { ASSERT(isMainThread()); ASSERT(!m_isShutdown); - + // If we're session storage, we shouldn't need to do any work here. if (m_storageType == SessionStorage) { ASSERT(!m_syncManager); @@ -127,13 +130,18 @@ void StorageNamespaceImpl::close() StorageAreaMap::iterator end = m_storageAreaMap.end(); for (StorageAreaMap::iterator it = m_storageAreaMap.begin(); it != end; ++it) it->second->close(); - + if (m_syncManager) m_syncManager->close(); m_isShutdown = true; } +void StorageNamespaceImpl::unlock() +{ + // Because there's a single event loop per-process, this is a no-op. +} + } // namespace WebCore #endif // ENABLE(DOM_STORAGE) diff --git a/WebCore/storage/StorageNamespaceImpl.h b/WebCore/storage/StorageNamespaceImpl.h index 0b15e43..b81b55a 100644 --- a/WebCore/storage/StorageNamespaceImpl.h +++ b/WebCore/storage/StorageNamespaceImpl.h @@ -42,16 +42,17 @@ namespace WebCore { class StorageNamespaceImpl : public StorageNamespace { public: - static PassRefPtr<StorageNamespace> localStorageNamespace(const String& path); + static PassRefPtr<StorageNamespace> localStorageNamespace(const String& path, unsigned quota); static PassRefPtr<StorageNamespace> sessionStorageNamespace(); virtual ~StorageNamespaceImpl(); - virtual PassRefPtr<StorageArea> storageArea(SecurityOrigin*); + virtual PassRefPtr<StorageArea> storageArea(PassRefPtr<SecurityOrigin>); virtual PassRefPtr<StorageNamespace> copy(); virtual void close(); + virtual void unlock(); private: - StorageNamespaceImpl(StorageType, const String& path); + StorageNamespaceImpl(StorageType, const String& path, unsigned quota); typedef HashMap<RefPtr<SecurityOrigin>, RefPtr<StorageAreaImpl>, SecurityOriginHash> StorageAreaMap; StorageAreaMap m_storageAreaMap; @@ -62,6 +63,7 @@ namespace WebCore { String m_path; RefPtr<StorageSyncManager> m_syncManager; + unsigned m_quota; // The default quota for each new storage area. bool m_isShutdown; }; diff --git a/WebCore/storage/StorageSyncManager.cpp b/WebCore/storage/StorageSyncManager.cpp index a935242..d9641b7 100644 --- a/WebCore/storage/StorageSyncManager.cpp +++ b/WebCore/storage/StorageSyncManager.cpp @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -48,26 +48,29 @@ PassRefPtr<StorageSyncManager> StorageSyncManager::create(const String& path) } StorageSyncManager::StorageSyncManager(const String& path) - : m_path(path.copy()) + : 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() @@ -83,22 +86,20 @@ void StorageSyncManager::close() bool StorageSyncManager::scheduleImport(PassRefPtr<StorageAreaSync> area) { ASSERT(isMainThread()); - + ASSERT(m_thread); if (m_thread) - m_thread->scheduleImport(area); - + 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); + m_thread->scheduleTask(LocalStorageTask::createSync(area.get())); } } // namespace WebCore #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/StorageSyncManager.h b/WebCore/storage/StorageSyncManager.h index 4c5e821..e2dfa76 100644 --- a/WebCore/storage/StorageSyncManager.h +++ b/WebCore/storage/StorageSyncManager.h @@ -20,7 +20,7 @@ * 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. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef StorageSyncManager_h @@ -31,8 +31,8 @@ #include "PlatformString.h" #include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> -#include <wtf/Threading.h> +#include <wtf/RefCounted.h> +#include <wtf/OwnPtr.h> namespace WebCore { @@ -40,7 +40,7 @@ namespace WebCore { class SecurityOrigin; class StorageAreaSync; - class StorageSyncManager : public ThreadSafeShared<StorageSyncManager> { + class StorageSyncManager : public RefCounted<StorageSyncManager> { public: static PassRefPtr<StorageSyncManager> create(const String& path); ~StorageSyncManager(); @@ -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..ac58e07 --- /dev/null +++ b/WebCore/storage/chromium/DatabaseTrackerChromium.cpp @@ -0,0 +1,181 @@ +/* + * 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 "QuotaTracker.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include "SecurityOriginHash.h" +#include "SQLiteFileSystem.h" +#include <wtf/HashSet.h> +#include <wtf/MainThread.h> +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +DatabaseTracker& DatabaseTracker::tracker() +{ + DEFINE_STATIC_LOCAL(DatabaseTracker, tracker, ()); + return tracker; +} + +DatabaseTracker::DatabaseTracker() +{ + SQLiteFileSystem::registerSQLiteVFS(); +} + +bool DatabaseTracker::canEstablishDatabase(ScriptExecutionContext*, 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(database->scriptExecutionContext()->isContextThread()); + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + if (!m_openDatabaseMap) + m_openDatabaseMap.set(new DatabaseOriginMap()); + + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); + if (!nameMap) { + nameMap = new DatabaseNameMap(); + m_openDatabaseMap->set(database->securityOrigin(), nameMap); + } + + String name(database->stringIdentifier()); + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) { + databaseSet = new DatabaseSet(); + nameMap->set(name, databaseSet); + } + + databaseSet->add(database); + + DatabaseObserver::databaseOpened(database); +} + +class TrackerRemoveOpenDatabaseTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<TrackerRemoveOpenDatabaseTask> create(PassRefPtr<Database> database) + { + return new TrackerRemoveOpenDatabaseTask(database); + } + + virtual void performTask(ScriptExecutionContext* context) + { + DatabaseTracker::tracker().removeOpenDatabase(m_database.get()); + } + +private: + TrackerRemoveOpenDatabaseTask(PassRefPtr<Database> database) + : m_database(database) + { + } + + RefPtr<Database> m_database; +}; + +void DatabaseTracker::removeOpenDatabase(Database* database) +{ + if (!database->scriptExecutionContext()->isContextThread()) { + database->scriptExecutionContext()->postTask(TrackerRemoveOpenDatabaseTask::create(database)); + return; + } + + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + ASSERT(m_openDatabaseMap); + DatabaseNameMap* nameMap = m_openDatabaseMap->get(database->securityOrigin()); + ASSERT(nameMap); + String name(database->stringIdentifier()); + DatabaseSet* databaseSet = nameMap->get(name); + ASSERT(databaseSet); + databaseSet->remove(database); + + if (databaseSet->isEmpty()) { + nameMap->remove(name); + delete databaseSet; + if (nameMap->isEmpty()) { + m_openDatabaseMap->remove(database->securityOrigin()); + delete nameMap; + } + } + + DatabaseObserver::databaseClosed(database); +} + + +void DatabaseTracker::getOpenDatabases(SecurityOrigin* origin, const String& name, HashSet<RefPtr<Database> >* databases) +{ + MutexLocker openDatabaseMapLock(m_openDatabaseMapGuard); + if (!m_openDatabaseMap) + return; + + DatabaseNameMap* nameMap = m_openDatabaseMap->get(origin); + if (!nameMap) + return; + + DatabaseSet* databaseSet = nameMap->get(name); + if (!databaseSet) + return; + + for (DatabaseSet::iterator it = databaseSet->begin(); it != databaseSet->end(); ++it) + databases->add(*it); +} + +unsigned long long DatabaseTracker::getMaxSizeForDatabase(const Database* database) +{ + ASSERT(currentThread() == database->scriptExecutionContext()->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..a10ca3e --- /dev/null +++ b/WebCore/storage/chromium/SQLTransactionClientChromium.cpp @@ -0,0 +1,87 @@ +/* + * 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> + +namespace WebCore { + +class NotifyDatabaseChangedTask : public ScriptExecutionContext::Task { +public: + static PassOwnPtr<NotifyDatabaseChangedTask> create(Database *database) + { + return new NotifyDatabaseChangedTask(database); + } + + virtual void performTask(ScriptExecutionContext*) + { + WebCore::DatabaseObserver::databaseModified(m_database.get()); + } + +private: + NotifyDatabaseChangedTask(PassRefPtr<Database> database) + : m_database(database) + { + } + + RefPtr<Database> m_database; +}; + +void SQLTransactionClient::didCommitTransaction(SQLTransaction* transaction) +{ + ASSERT(currentThread() == transaction->database()->scriptExecutionContext()->databaseThread()->getThreadID()); + if (!transaction->isReadOnly()) { + transaction->database()->scriptExecutionContext()->postTask(NotifyDatabaseChangedTask::create(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()->scriptExecutionContext()->databaseThread()->getThreadID()); +} + +bool SQLTransactionClient::didExceedQuota(SQLTransaction* transaction) +{ + // Chromium does not allow users to manually change the quota for an origin (for now, at least). + // Don't do anything. + ASSERT(transaction->database()->scriptExecutionContext()->isContextThread()); + return false; +} + +} |
