From 124602fa9325bb5b26a8305777dcbdef15762f6d Mon Sep 17 00:00:00 2001 From: Ben Murdoch Date: Tue, 6 Oct 2009 12:05:22 +0100 Subject: [PATCH] Cherry pick of WebKit commit r49018, fix for b/2067397. Enable Database.changVersion(). --- WebCore/bindings/v8/custom/V8DatabaseCustom.cpp | 41 ++++++++++++++++- WebCore/storage/Database.cpp | 60 +++++++++++++++---------- 2 files changed, 77 insertions(+), 24 deletions(-) diff --git a/WebCore/bindings/v8/custom/V8DatabaseCustom.cpp b/WebCore/bindings/v8/custom/V8DatabaseCustom.cpp index 300e82975..cdcef3152 100644 --- a/WebCore/bindings/v8/custom/V8DatabaseCustom.cpp +++ b/WebCore/bindings/v8/custom/V8DatabaseCustom.cpp @@ -45,6 +45,45 @@ namespace WebCore { CALLBACK_FUNC_DECL(DatabaseChangeVersion) { INC_STATS("DOM.Database.changeVersion()"); + + if (args.Length() < 2) + return throwError("The old and new version strings are required.", V8Proxy::SyntaxError); + + if (!(args[0]->IsString() && args[1]->IsString())) + return throwError("The old and new versions must be strings."); + + Database* database = V8DOMWrapper::convertToNativeObject(V8ClassIndex::DATABASE, args.Holder()); + + Frame* frame = V8Proxy::retrieveFrameForCurrentContext(); + if (!frame) + return v8::Undefined(); + + RefPtr callback; + if (args.Length() > 2) { + if (!args[2]->IsObject()) + return throwError("changeVersion transaction callback must be of valid type."); + + callback = V8CustomSQLTransactionCallback::create(args[2], frame); + } + + RefPtr errorCallback; + if (args.Length() > 3) { + if (!args[3]->IsObject()) + return throwError("changeVersion error callback must be of valid type."); + + errorCallback = V8CustomSQLTransactionErrorCallback::create(args[3], frame); + } + + RefPtr successCallback; + if (args.Length() > 4) { + if (!args[4]->IsObject()) + return throwError("changeVersion success callback must be of valid type."); + + successCallback = V8CustomVoidCallback::create(args[4], frame); + } + + database->changeVersion(toWebCoreString(args[0]), toWebCoreString(args[1]), callback.release(), errorCallback.release(), successCallback.release()); + return v8::Undefined(); } @@ -76,7 +115,7 @@ CALLBACK_FUNC_DECL(DatabaseTransaction) RefPtr successCallback; if (args.Length() > 2) { - if (!args[1]->IsObject()) + if (!args[2]->IsObject()) return throwError("Transaction success callback must be of valid type."); successCallback = V8CustomVoidCallback::create(args[2], frame); diff --git a/WebCore/storage/Database.cpp b/WebCore/storage/Database.cpp index 5504cb131..8118e76c5 100644 --- a/WebCore/storage/Database.cpp +++ b/WebCore/storage/Database.cpp @@ -89,6 +89,22 @@ static GuidVersionMap& guidToVersionMap() return map; } +// NOTE: Caller must lock guidMutex(). +static inline void updateGuidVersionMap(int guid, String newVersion) +{ + // Ensure the the mutex is locked. + ASSERT(!guidMutex().tryLock()); + + // Note: It is not safe to put an empty string into the guidToVersionMap() map. + // That's because the map is cross-thread, but empty strings are per-thread. + // The copy() function makes a version of the string you can use on the current + // thread, but we need a string we can keep in a cross-thread data structure. + // FIXME: This is a quite-awkward restriction to have to program with. + + // Map null string to empty string (see comment above). + guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.copy()); +} + typedef HashMap*> GuidDatabaseMap; static GuidDatabaseMap& guidToDatabaseMap() { @@ -178,20 +194,6 @@ Database::Database(Document* document, const String& name, const String& expecte Database::~Database() { - { - MutexLocker locker(guidMutex()); - - HashSet* hashSet = guidToDatabaseMap().get(m_guid); - ASSERT(hashSet); - ASSERT(hashSet->contains(this)); - hashSet->remove(this); - if (hashSet->isEmpty()) { - guidToDatabaseMap().remove(m_guid); - delete hashSet; - guidToVersionMap().remove(m_guid); - } - } - if (m_document->databaseThread()) m_document->databaseThread()->unscheduleDatabaseTasks(this); @@ -278,6 +280,8 @@ static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, cons bool Database::setVersionInDatabase(const String& version) { + // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE + // clause in the CREATE statement (see Database::performOpenAndVerify()). DEFINE_STATIC_LOCAL(String, setVersionQuery, ("INSERT INTO " + databaseInfoTableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);")); m_databaseAuthorizer->disable(); @@ -331,6 +335,20 @@ void Database::close() m_sqliteDatabase.close(); m_document->databaseThread()->recordDatabaseClosed(this); m_opened = false; + + { + MutexLocker locker(guidMutex()); + + HashSet* hashSet = guidToDatabaseMap().get(m_guid); + ASSERT(hashSet); + ASSERT(hashSet->contains(this)); + hashSet->remove(this); + if (hashSet->isEmpty()) { + guidToDatabaseMap().remove(m_guid); + delete hashSet; + guidToVersionMap().remove(m_guid); + } + } } } @@ -458,15 +476,9 @@ bool Database::performOpenAndVerify(ExceptionCode& e) { MutexLocker locker(guidMutex()); - // Note: It is not safe to put an empty string into the guidToVersionMap() map. - // That's because the map is cross-thread, but empty strings are per-thread. - // The copy() function makes a version of the string you can use on the current - // thread, but we need a string we can keep in a cross-thread data structure. - // FIXME: This is a quite-awkward restriction to have to program with. - GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); if (entry != guidToVersionMap().end()) { - // Map null string to empty string (see comment above). + // Map null string to empty string (see updateGuidVersionMap()). currentVersion = entry->second.isNull() ? String("") : entry->second; LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); } else { @@ -488,8 +500,7 @@ bool Database::performOpenAndVerify(ExceptionCode& e) currentVersion = m_expectedVersion; } - // Map empty string to null string (see comment above). - guidToVersionMap().set(m_guid, currentVersion.isEmpty() ? String() : currentVersion.copy()); + updateGuidVersionMap(m_guid, currentVersion); } } @@ -619,6 +630,9 @@ Vector Database::tableNames() void Database::setExpectedVersion(const String& version) { m_expectedVersion = version.copy(); + // Update the in memory database version map. + MutexLocker locker(guidMutex()); + updateGuidVersionMap(m_guid, version); } PassRefPtr Database::securityOriginCopy() const -- 2.11.0