diff options
Diffstat (limited to 'WebCore/storage')
51 files changed, 1007 insertions, 426 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 8118e76..8b0b432 100644 --- a/WebCore/storage/Database.cpp +++ b/WebCore/storage/Database.cpp @@ -50,6 +50,8 @@ #include "SQLiteFileSystem.h" #include "SQLiteStatement.h" #include "SQLResultSet.h" +#include "SQLTransactionClient.h" +#include "SQLTransactionCoordinator.h" #include <wtf/MainThread.h> #endif @@ -102,7 +104,11 @@ static inline void updateGuidVersionMap(int guid, String newVersion) // FIXME: This is a quite-awkward restriction to have to program with. // Map null string to empty string (see comment above). +<<<<<<< HEAD:WebCore/storage/Database.cpp guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.copy()); +======= + guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.threadsafeCopy()); +>>>>>>> Merge webkit.org at R49305 : Automatic merge by git.:WebCore/storage/Database.cpp } typedef HashMap<int, HashSet<Database*>*> GuidDatabaseMap; @@ -127,20 +133,22 @@ PassRefPtr<Database> Database::openDatabase(Document* document, const String& na LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), document->securityOrigin()->toString().ascii().data()); return 0; } - + RefPtr<Database> database = adoptRef(new Database(document, name, expectedVersion)); 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; } - + DatabaseTracker::tracker().setDatabaseDetails(document->securityOrigin(), name, displayName, estimatedSize); document->setHasOpenDatabases(); +#if ENABLE(INSPECTOR) if (Page* page = document->frame()->page()) page->inspectorController()->didOpenDatabase(database.get(), document->securityOrigin()->host(), name, expectedVersion); +#endif return database; } @@ -148,7 +156,7 @@ PassRefPtr<Database> Database::openDatabase(Document* document, const String& na Database::Database(Document* document, const String& name, const String& expectedVersion) : m_transactionInProgress(false) , m_document(document) - , m_name(name.copy()) + , m_name(name.crossThreadString()) , m_guid(0) , m_expectedVersion(expectedVersion) , m_deleted(false) @@ -163,11 +171,14 @@ Database::Database(Document* document, const String& name, const String& expecte #if USE(JSC) JSC::initializeThreading(); +<<<<<<< HEAD:WebCore/storage/Database.cpp // 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(); +======= +>>>>>>> webkit.org at 49305:WebCore/storage/Database.cpp #endif m_guid = guidForOriginAndName(m_securityOrigin->toString(), name); @@ -248,7 +259,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()); @@ -286,7 +297,7 @@ bool Database::setVersionInDatabase(const String& version) 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()); @@ -301,7 +312,7 @@ bool Database::versionMatchesExpected() const MutexLocker locker(guidMutex()); return m_expectedVersion == guidToVersionMap().get(m_guid); } - + return true; } @@ -357,8 +368,8 @@ 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; @@ -378,10 +389,10 @@ 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(); } @@ -464,14 +475,6 @@ bool Database::performOpenAndVerify(ExceptionCode& e) 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()); @@ -483,6 +486,15 @@ bool Database::performOpenAndVerify(ExceptionCode& e) 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; + return false; + } + } + if (!getVersionFromDatabase(currentVersion)) { LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data()); e = INVALID_STATE_ERR; @@ -523,7 +535,7 @@ bool Database::performOpenAndVerify(ExceptionCode& e) 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) { @@ -534,9 +546,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(); @@ -555,13 +567,17 @@ void Database::scheduleTransaction() 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()); + if (!m_document->databaseThread()) + return; + + RefPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction); + LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get()); + if (immediately) + m_document->databaseThread()->scheduleImmediateTask(task.release()); + else m_document->databaseThread()->scheduleTask(task.release()); - } } void Database::scheduleTransactionCallback(SQLTransaction* transaction) @@ -599,12 +615,22 @@ Vector<String> Database::performGetTableNames() return tableNames; } +SQLTransactionClient* Database::transactionClient() const +{ + return m_document->databaseThread()->transactionClient(); +} + +SQLTransactionCoordinator* Database::transactionCoordinator() const +{ + return m_document->databaseThread()->transactionCoordinator(); +} + String Database::version() const { if (m_deleted) return String(); MutexLocker locker(guidMutex()); - return guidToVersionMap().get(m_guid).copy(); + return guidToVersionMap().get(m_guid).threadsafeCopy(); } void Database::deliverPendingCallback(void* context) @@ -629,7 +655,11 @@ Vector<String> Database::tableNames() void Database::setExpectedVersion(const String& version) { +<<<<<<< HEAD:WebCore/storage/Database.cpp m_expectedVersion = version.copy(); +======= + m_expectedVersion = version.threadsafeCopy(); +>>>>>>> Merge webkit.org at R49305 : Automatic merge by git.:WebCore/storage/Database.cpp // Update the in memory database version map. MutexLocker locker(guidMutex()); updateGuidVersionMap(m_guid, version); @@ -637,13 +667,13 @@ void Database::setExpectedVersion(const String& version) PassRefPtr<SecurityOrigin> Database::securityOriginCopy() const { - return m_securityOrigin->copy(); + return m_securityOrigin->threadsafeCopy(); } String Database::stringIdentifier() const { // Return a deep copy for ref counting thread safety - return m_name.copy(); + return m_name.threadsafeCopy(); } #endif diff --git a/WebCore/storage/Database.h b/WebCore/storage/Database.h index 0bdb37b..b850686 100644 --- a/WebCore/storage/Database.h +++ b/WebCore/storage/Database.h @@ -41,7 +41,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> @@ -57,9 +56,11 @@ class DatabaseThread; class Document; class SQLResultSet; class SQLTransactionCallback; +class SQLTransactionClient; +class SQLTransactionCoordinator; class SQLTransactionErrorCallback; class SQLValue; - + typedef int ExceptionCode; class Database : public ThreadSafeShared<Database> { @@ -72,12 +73,12 @@ public: // 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&); 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(); @@ -90,7 +91,7 @@ public: Document* document() const { return m_document.get(); } PassRefPtr<SecurityOrigin> securityOriginCopy() const; String stringIdentifier() const; - + bool getVersionFromDatabase(String&); bool setVersionInDatabase(const String&); void setExpectedVersion(const String&); @@ -101,7 +102,7 @@ public: void close(); bool opened() const { return m_opened; } - + void stop(); bool stopped() const { return m_stopped; } @@ -116,6 +117,9 @@ public: Vector<String> performGetTableNames(); + SQLTransactionClient* transactionClient() const; + SQLTransactionCoordinator* transactionCoordinator() const; + private: Database(Document* document, const String& name, const String& expectedVersion); @@ -123,8 +127,8 @@ private: void scheduleTransaction(); void scheduleTransactionCallback(SQLTransaction*); - void scheduleTransactionStep(SQLTransaction* transaction); - + void scheduleTransactionStep(SQLTransaction* transaction, bool immediately = false); + MessageQueue<RefPtr<SQLTransaction> > m_transactionQueue; Mutex m_transactionInProgressMutex; bool m_transactionInProgress; @@ -139,7 +143,7 @@ private: String m_filename; bool m_deleted; - + bool m_stopped; bool m_opened; diff --git a/WebCore/storage/Database.idl b/WebCore/storage/Database.idl index 1e4b316..6ca9c95 100644 --- a/WebCore/storage/Database.idl +++ b/WebCore/storage/Database.idl @@ -34,6 +34,7 @@ module storage { 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..93f9106 100644 --- a/WebCore/storage/DatabaseAuthorizer.cpp +++ b/WebCore/storage/DatabaseAuthorizer.cpp @@ -58,6 +58,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 +77,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 +106,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 +125,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 +145,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 +164,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 +253,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); diff --git a/WebCore/storage/DatabaseAuthorizer.h b/WebCore/storage/DatabaseAuthorizer.h index e53ea50..248b659 100644 --- a/WebCore/storage/DatabaseAuthorizer.h +++ b/WebCore/storage/DatabaseAuthorizer.h @@ -59,10 +59,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 +75,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); 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/DatabaseThread.cpp b/WebCore/storage/DatabaseThread.cpp index b6c9b5d..9e3afdd 100644 --- a/WebCore/storage/DatabaseThread.cpp +++ b/WebCore/storage/DatabaseThread.cpp @@ -35,11 +35,15 @@ #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_selfRef = this; } @@ -97,6 +101,9 @@ void* DatabaseThread::databaseThread() 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 @@ -119,7 +126,7 @@ void* DatabaseThread::databaseThread() return 0; } -void DatabaseThread::recordDatabaseOpen(Database* database) +void DatabaseThread::recordDatabaseOpen(Database* database) { ASSERT(currentThread() == m_threadID); ASSERT(database); @@ -127,7 +134,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); diff --git a/WebCore/storage/DatabaseThread.h b/WebCore/storage/DatabaseThread.h index 5aab5fd..83b1baf 100644 --- a/WebCore/storage/DatabaseThread.h +++ b/WebCore/storage/DatabaseThread.h @@ -33,6 +33,7 @@ #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/MessageQueue.h> +#include <wtf/OwnPtr.h> #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/Threading.h> @@ -42,6 +43,8 @@ namespace WebCore { class Database; class DatabaseTask; class Document; +class SQLTransactionClient; +class SQLTransactionCoordinator; class DatabaseThread : public ThreadSafeShared<DatabaseThread> { public: @@ -60,6 +63,9 @@ public: void recordDatabaseClosed(Database*); ThreadIdentifier getThreadID() { return m_threadID; } + SQLTransactionClient* transactionClient() { return m_transactionClient.get(); } + SQLTransactionCoordinator* transactionCoordinator() { return m_transactionCoordinator.get(); } + private: DatabaseThread(); @@ -75,6 +81,9 @@ private: // 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; }; } // namespace WebCore diff --git a/WebCore/storage/DatabaseTracker.cpp b/WebCore/storage/DatabaseTracker.cpp index e7c9485..265cd0d 100644 --- a/WebCore/storage/DatabaseTracker.cpp +++ b/WebCore/storage/DatabaseTracker.cpp @@ -131,7 +131,7 @@ bool DatabaseTracker::canEstablishDatabase(Document* document, const String& nam // Since we're imminently opening a database within this Document'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; @@ -197,11 +197,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 +220,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 +239,7 @@ String DatabaseTracker::fullPathForDatabase(SecurityOrigin* origin, const String if (originQuotaManager().tracksOrigin(origin)) originQuotaManager().addDatabase(origin, name, fullFilePath); } - + return fullFilePath; } @@ -321,19 +321,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 +343,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 +371,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 +395,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); } @@ -481,16 +481,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 +529,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 +556,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 +573,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 +603,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 +631,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 +674,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 +762,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..2f6e06d 100644 --- a/WebCore/storage/DatabaseTracker.h +++ b/WebCore/storage/DatabaseTracker.h @@ -70,18 +70,18 @@ public: 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*); @@ -93,12 +93,12 @@ private: 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; @@ -117,7 +117,7 @@ private: OwnPtr<OriginQuotaManager> m_quotaManager; String m_databaseDirectoryPath; - + DatabaseTrackerClient* m_client; std::pair<SecurityOrigin*, DatabaseDetails>* m_proposedDatabase; 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..f03d851 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 @@ -44,19 +44,19 @@ namespace WebCore { ~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 PassRefPtr<LocalStorageTask> createImport(StorageAreaSync* area) { return adoptRef(new LocalStorageTask(AreaImport, area)); } + static PassRefPtr<LocalStorageTask> createSync(StorageAreaSync* area) { return adoptRef(new LocalStorageTask(AreaSync, area)); } + static PassRefPtr<LocalStorageTask> createTerminate(LocalStorageThread* thread) { return adoptRef(new LocalStorageTask(TerminateThread, thread)); } 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..78640a9 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" @@ -86,13 +86,13 @@ void* LocalStorageThread::localStorageThread() return 0; } -void LocalStorageThread::scheduleImport(PassRefPtr<StorageAreaSync> area) +void LocalStorageThread::scheduleImport(StorageAreaSync* area) { ASSERT(!m_queue.killed() && m_threadID); m_queue.append(LocalStorageTask::createImport(area)); } -void LocalStorageThread::scheduleSync(PassRefPtr<StorageAreaSync> area) +void LocalStorageThread::scheduleSync(StorageAreaSync* area) { ASSERT(!m_queue.killed() && m_threadID); m_queue.append(LocalStorageTask::createSync(area)); @@ -101,7 +101,7 @@ void LocalStorageThread::scheduleSync(PassRefPtr<StorageAreaSync> area) 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); @@ -109,9 +109,9 @@ void LocalStorageThread::terminate() return; MutexLocker locker(m_terminateLock); - + m_queue.append(LocalStorageTask::createTerminate(this)); - + m_terminateCondition.wait(m_terminateLock); } @@ -120,7 +120,7 @@ void LocalStorageThread::performTerminate() ASSERT(!isMainThread()); m_queue.kill(); - + MutexLocker locker(m_terminateLock); m_terminateCondition.signal(); } @@ -128,4 +128,3 @@ void LocalStorageThread::performTerminate() } #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/LocalStorageThread.h b/WebCore/storage/LocalStorageThread.h index 3d58427..e9e2b58 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 @@ -45,8 +45,8 @@ namespace WebCore { bool start(); - void scheduleImport(PassRefPtr<StorageAreaSync>); - void scheduleSync(PassRefPtr<StorageAreaSync>); + void scheduleImport(StorageAreaSync*); + void scheduleSync(StorageAreaSync*); // Called from the main thread to synchronously shut down this thread void terminate(); @@ -64,7 +64,7 @@ namespace WebCore { RefPtr<LocalStorageThread> m_selfRef; MessageQueue<RefPtr<LocalStorageTask> > m_queue; - + Mutex m_terminateLock; ThreadCondition m_terminateCondition; }; diff --git a/WebCore/storage/OriginQuotaManager.cpp b/WebCore/storage/OriginQuotaManager.cpp index 2b98ab7..20bb34d 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; @@ -107,21 +107,20 @@ void OriginQuotaManager::markDatabase(Database* database) RefPtr<SecurityOrigin> origin = database->securityOriginCopy(); OriginUsageRecord* usageRecord = m_usageMap.get(origin); 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..609a793 100644 --- a/WebCore/storage/OriginUsageRecord.h +++ b/WebCore/storage/OriginUsageRecord.h @@ -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/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/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..149b384 100644 --- a/WebCore/storage/SQLTransaction.cpp +++ b/WebCore/storage/SQLTransaction.cpp @@ -35,11 +35,9 @@ #include "Database.h" #include "DatabaseAuthorizer.h" #include "DatabaseDetails.h" -#include "DatabaseTracker.h" #include "Document.h" #include "ExceptionCode.h" #include "Logging.h" -#include "OriginQuotaManager.h" #include "Page.h" #include "PlatformString.h" #include "SecurityOrigin.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,6 +76,8 @@ SQLTransaction::SQLTransaction(Database* db, PassRefPtr<SQLTransactionCallback> , m_errorCallback(errorCallback) , m_shouldRetryCurrentStatement(false) , m_modifiedDatabase(false) + , m_lockAcquired(false) + , m_readOnly(readOnly) { ASSERT(m_database); } @@ -91,11 +93,13 @@ 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) { + Page* page = m_database->document()->page(); + if (!page || page->settings()->privateBrowsingEnabled()) + 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,28 @@ void SQLTransaction::performPendingCallback() m_nextStep == &SQLTransaction::deliverSuccessCallback); checkAndHandleClosedDatabase(); - + if (m_nextStep) (this->*m_nextStep)(); } +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 +232,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 +251,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 +263,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,6 +298,8 @@ 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 { @@ -280,8 +307,11 @@ void SQLTransaction::runStatements() 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 +320,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 +336,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 +348,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,16 +367,16 @@ 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; } @@ -372,7 +399,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 +417,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 +437,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 +451,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 +474,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 +487,16 @@ 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_nextStep = 0; + + // Release the lock on this database + m_database->transactionCoordinator()->releaseLock(this); } void SQLTransaction::handleTransactionError(bool inCallback) @@ -488,7 +511,7 @@ void SQLTransaction::handleTransactionError(bool inCallback) } return; } - + // No error callback, so fast-forward to: // Transaction Step 12 - Rollback the transaction. if (inCallback) { @@ -503,7 +526,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 +539,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 +565,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..6d6a8d7 100644 --- a/WebCore/storage/SQLTransaction.h +++ b/WebCore/storage/SQLTransaction.h @@ -60,34 +60,39 @@ 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; } 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 +114,9 @@ private: #endif RefPtr<SQLStatement> m_currentStatement; - + bool m_executeSqlAllowed; - + RefPtr<Database> m_database; RefPtr<SQLTransactionWrapper> m_wrapper; RefPtr<SQLTransactionCallback> m_callback; @@ -120,13 +125,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/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..e72f5ed --- /dev/null +++ b/WebCore/storage/SQLTransactionClient.cpp @@ -0,0 +1,76 @@ +/* + * 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 "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()->document()->databaseThread()->getThreadID()); + Database* database = transaction->database(); + DatabaseTracker::tracker().scheduleNotifyDatabaseChanged( + database->document()->securityOrigin(), database->stringIdentifier()); +} + +void SQLTransactionClient::didExecuteStatement(SQLTransaction* transaction) +{ + ASSERT(currentThread() == transaction->database()->document()->databaseThread()->getThreadID()); + OriginQuotaManager& manager(DatabaseTracker::tracker().originQuotaManager()); + Locker<OriginQuotaManager> locker(manager); + manager.markDatabase(transaction->database()); +} + +bool SQLTransactionClient::didExceedQuota(SQLTransaction* transaction) +{ + ASSERT(isMainThread()); + Database* database = transaction->database(); + Page* page = database->document()->page(); + ASSERT(page); + + RefPtr<SecurityOrigin> origin = database->securityOriginCopy(); + + unsigned long long currentQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get()); + page->chrome()->client()->exceededDatabaseQuota(database->document()->frame(), database->stringIdentifier()); + unsigned long long newQuota = DatabaseTracker::tracker().quotaForOrigin(origin.get()); + return (newQuota > currentQuota); +} + +} diff --git a/WebCore/storage/SQLTransactionClient.h b/WebCore/storage/SQLTransactionClient.h new file mode 100644 index 0000000..941c163 --- /dev/null +++ b/WebCore/storage/SQLTransactionClient.h @@ -0,0 +1,48 @@ +/* + * 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 + +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: + void didCommitTransaction(SQLTransaction*); + void didExecuteStatement(SQLTransaction*); + bool didExceedQuota(SQLTransaction*); + }; +} + +#endif // SQLTransactionClient_h diff --git a/WebCore/storage/SQLTransactionCoordinator.cpp b/WebCore/storage/SQLTransactionCoordinator.cpp new file mode 100644 index 0000000..30b0c4a --- /dev/null +++ b/WebCore/storage/SQLTransactionCoordinator.cpp @@ -0,0 +1,114 @@ +/* + * 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" + +#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() +{ + // Clean up all pending transactions for all databases + m_coordinationInfoMap.clear(); +} + +} diff --git a/WebCore/storage/SQLTransactionCoordinator.h b/WebCore/storage/SQLTransactionCoordinator.h new file mode 100644 index 0000000..20cc863 --- /dev/null +++ b/WebCore/storage/SQLTransactionCoordinator.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLTransactionCoordinator_h +#define SQLTransactionCoordinator_h + +#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: + 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 // 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/StorageArea.h b/WebCore/storage/StorageArea.h index f74405a..a64d44a 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() { } diff --git a/WebCore/storage/StorageAreaImpl.cpp b/WebCore/storage/StorageAreaImpl.cpp index f53edd3..612cb5f 100644 --- a/WebCore/storage/StorageAreaImpl.cpp +++ b/WebCore/storage/StorageAreaImpl.cpp @@ -28,16 +28,11 @@ #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,17 +40,24 @@ 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); @@ -82,6 +84,7 @@ StorageAreaImpl::StorageAreaImpl(StorageAreaImpl* area) , m_isShutdown(area->m_isShutdown) #endif { + ASSERT(isMainThread()); ASSERT(m_securityOrigin); ASSERT(m_storageMap); ASSERT(!m_isShutdown); @@ -132,15 +135,14 @@ void StorageAreaImpl::setItem(const String& key, const String& value, ExceptionC return; } - // 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 (quotaException) { + ec = QUOTA_EXCEEDED_ERR; + return; + } if (newMap) m_storageMap = newMap.release(); @@ -149,7 +151,7 @@ void StorageAreaImpl::setItem(const String& key, const String& value, ExceptionC if (oldValue != value) { if (m_storageAreaSync) m_storageAreaSync->scheduleItemForSync(key, value); - dispatchStorageEvent(key, oldValue, value, frame); + StorageEventDispatcher::dispatch(key, oldValue, value, m_storageType, m_securityOrigin.get(), frame); } } @@ -170,7 +172,7 @@ void StorageAreaImpl::removeItem(const String& key, Frame* frame) if (!oldValue.isNull()) { if (m_storageAreaSync) m_storageAreaSync->scheduleItemForSync(key, String()); - dispatchStorageEvent(key, oldValue, String(), frame); + StorageEventDispatcher::dispatch(key, oldValue, String(), m_storageType, m_securityOrigin.get(), frame); } } @@ -182,11 +184,12 @@ void StorageAreaImpl::clear(Frame* frame) if (privateBrowsingEnabled(frame)) return; - 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); } bool StorageAreaImpl::contains(const String& key) const @@ -224,46 +227,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..fe21a45 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,7 +41,7 @@ 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) @@ -60,12 +61,11 @@ namespace WebCore { 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..ad41e28 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" @@ -57,6 +57,7 @@ StorageAreaSync::StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManag , m_syncScheduled(false) , m_importComplete(false) { + ASSERT(isMainThread()); ASSERT(m_storageArea); ASSERT(m_syncManager); @@ -68,6 +69,7 @@ StorageAreaSync::StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManag StorageAreaSync::~StorageAreaSync() { + ASSERT(isMainThread()); ASSERT(!m_syncTimer.isActive()); } @@ -76,7 +78,7 @@ void StorageAreaSync::scheduleFinalSync() ASSERT(isMainThread()); // FIXME: We do this to avoid races, but it'd be better to make things safe without blocking. blockUntilImportComplete(); - + if (m_syncTimer.isActive()) m_syncTimer.stop(); else { @@ -127,7 +129,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 +140,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; @@ -182,14 +184,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(); @@ -205,13 +207,13 @@ void StorageAreaSync::performImport() } 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; @@ -265,7 +267,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 +291,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 +337,3 @@ void StorageAreaSync::performSync() } // namespace WebCore #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/StorageAreaSync.h b/WebCore/storage/StorageAreaSync.h index e436bef..9d6436f 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,7 +39,7 @@ namespace WebCore { class Frame; class StorageAreaImpl; class StorageSyncManager; - + class StorageAreaSync : public RefCounted<StorageAreaSync> { public: static PassRefPtr<StorageAreaSync> create(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea); @@ -50,16 +50,16 @@ namespace WebCore { void scheduleItemForSync(const String& key, const String& value); void scheduleClear(); - + private: StorageAreaSync(PassRefPtr<StorageSyncManager> storageSyncManager, PassRefPtr<StorageAreaImpl> storageArea); 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; diff --git a/WebCore/storage/StorageEvent.cpp b/WebCore/storage/StorageEvent.cpp index 2e620d5..f3b3b70 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" @@ -76,4 +76,3 @@ void StorageEvent::initStorageEvent(const AtomicString& type, bool canBubble, bo } // namespace WebCore #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/StorageEvent.h b/WebCore/storage/StorageEvent.h index 703fb5a..7e2bcff 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 @@ -55,16 +55,16 @@ namespace WebCore { 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); - + 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/StorageEventDispatcher.cpp b/WebCore/storage/StorageEventDispatcher.cpp new file mode 100644 index 0000000..496ff6d --- /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 (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/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..5498d9e 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,20 +30,22 @@ 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; return newMap.release(); } @@ -54,22 +56,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 +84,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 +98,34 @@ 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. If the quota is enabled and this would go over it, bail. + oldValue = m_map.get(key); + unsigned newLength = m_currentLength + value.length() - oldValue.length(); + bool overQuota = newLength > m_quotaSize / sizeof(UChar); + bool overflow = (newLength > m_currentLength) != (value.length() > oldValue.length()); + ASSERT(!overflow); // If we're debugging, make a fuss. But it's still worth checking this in the following if statement. + if (m_quotaSize != noQuota && (overflow || overQuota)) { + quotaException = true; + return 0; + } + m_currentLength = newLength; - if (addResult.second) { - // There was no "oldValue" so null it out. - oldValue = String(); - } else { - oldValue = addResult.first->second; + pair<HashMap<String, String>::iterator, bool> addResult = m_map.add(key, value); + if (!addResult.second) addResult.first->second = value; - } invalidateIterator(); @@ -137,6 +146,10 @@ PassRefPtr<StorageMap> StorageMap::removeItem(const String& key, String& oldValu if (!oldValue.isNull()) invalidateIterator(); + // Update quota. + ASSERT(m_currentLength - oldValue.length() <= m_currentLength); + m_currentLength -= oldValue.length(); + return 0; } @@ -145,17 +158,20 @@ 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(), String()); if (result.second) - result.first->second = value.copy(); + result.first->second = value.threadsafeCopy(); + + // Update quota. + 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..6b8caeb 100644 --- a/WebCore/storage/StorageNamespace.cpp +++ b/WebCore/storage/StorageNamespace.cpp @@ -36,9 +36,9 @@ 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() diff --git a/WebCore/storage/StorageNamespace.h b/WebCore/storage/StorageNamespace.h index edbe339..0ac5f86 100644 --- a/WebCore/storage/StorageNamespace.h +++ b/WebCore/storage/StorageNamespace.h @@ -41,13 +41,14 @@ namespace WebCore { // 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> localStorageNamespace(const String& path, unsigned quota); static PassRefPtr<StorageNamespace> sessionStorageNamespace(); virtual ~StorageNamespace() { } - virtual PassRefPtr<StorageArea> storageArea(SecurityOrigin*) = 0; + 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..f9276dd 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,8 +48,9 @@ PassRefPtr<StorageSyncManager> StorageSyncManager::create(const String& path) } StorageSyncManager::StorageSyncManager(const String& path) - : m_path(path.copy()) + : m_path(path.crossThreadString()) { + ASSERT(isMainThread()); ASSERT(!m_path.isEmpty()); m_thread = LocalStorageThread::create(); m_thread->start(); @@ -57,6 +58,7 @@ StorageSyncManager::StorageSyncManager(const String& path) StorageSyncManager::~StorageSyncManager() { + ASSERT(isMainThread()); } String StorageSyncManager::fullDatabaseFilename(SecurityOrigin* origin) @@ -85,7 +87,7 @@ bool StorageSyncManager::scheduleImport(PassRefPtr<StorageAreaSync> area) ASSERT(isMainThread()); if (m_thread) - m_thread->scheduleImport(area); + m_thread->scheduleImport(area.get()); return m_thread; } @@ -95,10 +97,9 @@ void StorageSyncManager::scheduleSync(PassRefPtr<StorageAreaSync> area) ASSERT(isMainThread()); if (m_thread) - m_thread->scheduleSync(area); + m_thread->scheduleSync(area.get()); } } // namespace WebCore #endif // ENABLE(DOM_STORAGE) - diff --git a/WebCore/storage/StorageSyncManager.h b/WebCore/storage/StorageSyncManager.h index 4c5e821..fe35e3d 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/RefCounted.h> #include <wtf/RefPtr.h> -#include <wtf/Threading.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(); |