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<Database>(V8ClassIndex::DATABASE, args.Holder());
+
+ Frame* frame = V8Proxy::retrieveFrameForCurrentContext();
+ if (!frame)
+ return v8::Undefined();
+
+ RefPtr<V8CustomSQLTransactionCallback> 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<V8CustomSQLTransactionErrorCallback> 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<V8CustomVoidCallback> 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();
}
RefPtr<V8CustomVoidCallback> 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);
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<int, HashSet<Database*>*> GuidDatabaseMap;
static GuidDatabaseMap& guidToDatabaseMap()
{
Database::~Database()
{
- {
- MutexLocker locker(guidMutex());
-
- HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
- ASSERT(hashSet);
- ASSERT(hashSet->contains(this));
- hashSet->remove(this);
- if (hashSet->isEmpty()) {
- guidToDatabaseMap().remove(m_guid);
- delete hashSet;
- guidToVersionMap().remove(m_guid);
- }
- }
-
if (m_document->databaseThread())
m_document->databaseThread()->unscheduleDatabaseTasks(this);
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();
m_sqliteDatabase.close();
m_document->databaseThread()->recordDatabaseClosed(this);
m_opened = false;
+
+ {
+ MutexLocker locker(guidMutex());
+
+ HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
+ ASSERT(hashSet);
+ ASSERT(hashSet->contains(this));
+ hashSet->remove(this);
+ if (hashSet->isEmpty()) {
+ guidToDatabaseMap().remove(m_guid);
+ delete hashSet;
+ guidToVersionMap().remove(m_guid);
+ }
+ }
}
}
{
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 {
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);
}
}
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<SecurityOrigin> Database::securityOriginCopy() const