WebCore: Adding support for the optional creation callback that could be
[WebKit-https.git] / WebCore / storage / Database.cpp
1 /*
2  * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer.
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution.
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "Database.h"
31
32 #include <wtf/StdLibExtras.h>
33
34 #if ENABLE(DATABASE)
35 #include "ChangeVersionWrapper.h"
36 #include "CString.h"
37 #include "DatabaseAuthorizer.h"
38 #include "DatabaseCallback.h"
39 #include "DatabaseTask.h"
40 #include "DatabaseThread.h"
41 #include "DatabaseTracker.h"
42 #include "Document.h"
43 #include "ExceptionCode.h"
44 #include "Frame.h"
45 #include "InspectorController.h"
46 #include "Logging.h"
47 #include "NotImplemented.h"
48 #include "Page.h"
49 #include "OriginQuotaManager.h"
50 #include "ScriptController.h"
51 #include "SQLiteDatabase.h"
52 #include "SQLiteFileSystem.h"
53 #include "SQLiteStatement.h"
54 #include "SQLResultSet.h"
55 #include "SQLTransactionClient.h"
56 #include "SQLTransactionCoordinator.h"
57
58 #endif // ENABLE(DATABASE)
59
60 #if USE(JSC)
61 #include "JSDOMWindow.h"
62 #endif
63
64 namespace WebCore {
65
66 // If we sleep for more the 30 seconds while blocked on SQLITE_BUSY, give up.
67 static const int maxSqliteBusyWaitTime = 30000;
68
69 const String& Database::databaseInfoTableName()
70 {
71     DEFINE_STATIC_LOCAL(String, name, ("__WebKitDatabaseInfoTable__"));
72     return name;
73 }
74
75 #if ENABLE(DATABASE)
76
77 static bool isDatabaseAvailable = true;
78
79 void Database::setIsAvailable(bool available)
80 {
81     isDatabaseAvailable = available;
82 }
83
84 bool Database::isAvailable()
85 {
86     return isDatabaseAvailable;
87 }
88
89 static Mutex& guidMutex()
90 {
91     // Note: We don't have to use AtomicallyInitializedStatic here because
92     // this function is called once in the constructor on the main thread
93     // before any other threads that call this function are used.
94     DEFINE_STATIC_LOCAL(Mutex, mutex, ());
95     return mutex;
96 }
97
98 typedef HashMap<int, String> GuidVersionMap;
99 static GuidVersionMap& guidToVersionMap()
100 {
101     DEFINE_STATIC_LOCAL(GuidVersionMap, map, ());
102     return map;
103 }
104
105 // NOTE: Caller must lock guidMutex().
106 static inline void updateGuidVersionMap(int guid, String newVersion)
107 {
108     // Ensure the the mutex is locked.
109     ASSERT(!guidMutex().tryLock());
110
111     // Note: It is not safe to put an empty string into the guidToVersionMap() map.
112     // That's because the map is cross-thread, but empty strings are per-thread.
113     // The copy() function makes a version of the string you can use on the current
114     // thread, but we need a string we can keep in a cross-thread data structure.
115     // FIXME: This is a quite-awkward restriction to have to program with.
116
117     // Map null string to empty string (see comment above).
118     guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.threadsafeCopy());
119 }
120
121 typedef HashMap<int, HashSet<Database*>*> GuidDatabaseMap;
122 static GuidDatabaseMap& guidToDatabaseMap()
123 {
124     DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ());
125     return map;
126 }
127
128 static const String& databaseVersionKey()
129 {
130     DEFINE_STATIC_LOCAL(String, key, ("WebKitDatabaseVersionKey"));
131     return key;
132 }
133
134 static int guidForOriginAndName(const String& origin, const String& name);
135
136 class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task {
137 public:
138     static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database)
139     {
140         return new DatabaseCreationCallbackTask(database);
141     }
142
143     virtual void performTask(ScriptExecutionContext*)
144     {
145         m_database->performCreationCallback();
146     }
147
148 private:
149     DatabaseCreationCallbackTask(PassRefPtr<Database> database)
150         : m_database(database)
151     {
152     }
153
154     RefPtr<Database> m_database;
155 };
156
157 PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, const String& name,
158                                             const String& expectedVersion, const String& displayName,
159                                             unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback,
160                                             ExceptionCode& e)
161 {
162     if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) {
163         // FIXME: There should be an exception raised here in addition to returning a null Database object.  The question has been raised with the WHATWG.
164         LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data());
165         return 0;
166     }
167
168     RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize, creationCallback));
169
170     if (!database->openAndVerifyVersion(e)) {
171         LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data());
172         context->removeOpenDatabase(database.get());
173         DatabaseTracker::tracker().removeOpenDatabase(database.get());
174         return 0;
175     }
176
177     DatabaseTracker::tracker().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
178
179     context->setHasOpenDatabases();
180 #if ENABLE(INSPECTOR)
181     if (context->isDocument()) {
182         Document* document = static_cast<Document*>(context);
183         if (Page* page = document->page())
184             page->inspectorController()->didOpenDatabase(database.get(), context->securityOrigin()->host(), name, expectedVersion);
185     }
186 #endif
187
188     // If it's a new database and a creation callback was provided, reset the expected
189     // version to "" and schedule the creation callback. Because of some subtle String
190     // implementation issues, we have to reset m_expectedVersion here instead of doing
191     // it inside performOpenAndVerify() which is run on the DB thread.
192     if (database->isNew() && database->m_creationCallback.get()) {
193         database->m_expectedVersion = "";
194         LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get());
195         database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database));
196     }
197
198     return database;
199 }
200
201 Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback)
202     : m_transactionInProgress(false)
203     , m_isTransactionQueueEnabled(true)
204     , m_scriptExecutionContext(context)
205     , m_name(name.crossThreadString())
206     , m_guid(0)
207     , m_expectedVersion(expectedVersion.crossThreadString())
208     , m_displayName(displayName.crossThreadString())
209     , m_estimatedSize(estimatedSize)
210     , m_deleted(false)
211     , m_stopped(false)
212     , m_opened(false)
213     , m_new(false)
214     , m_creationCallback(creationCallback)
215 {
216     ASSERT(m_scriptExecutionContext.get());
217     m_mainThreadSecurityOrigin = m_scriptExecutionContext->securityOrigin();
218     m_databaseThreadSecurityOrigin = m_mainThreadSecurityOrigin->threadsafeCopy();
219     if (m_name.isNull())
220         m_name = "";
221
222     ScriptController::initializeThreading();
223
224     m_guid = guidForOriginAndName(securityOrigin()->toString(), name);
225
226     {
227         MutexLocker locker(guidMutex());
228
229         HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
230         if (!hashSet) {
231             hashSet = new HashSet<Database*>;
232             guidToDatabaseMap().set(m_guid, hashSet);
233         }
234
235         hashSet->add(this);
236     }
237
238     ASSERT(m_scriptExecutionContext->databaseThread());
239     m_filename = DatabaseTracker::tracker().fullPathForDatabase(securityOrigin(), m_name);
240
241     DatabaseTracker::tracker().addOpenDatabase(this);
242     context->addOpenDatabase(this);
243 }
244
245 class DerefContextTask : public ScriptExecutionContext::Task {
246 public:
247     static PassOwnPtr<DerefContextTask> create()
248     {
249         return new DerefContextTask();
250     }
251
252     virtual void performTask(ScriptExecutionContext* context)
253     {
254         context->deref();
255     }
256
257     virtual bool isCleanupTask() const { return true; }
258 };
259
260 Database::~Database()
261 {
262     // The reference to the ScriptExecutionContext needs to be cleared on the JavaScript thread.  If we're on that thread already, we can just let the RefPtr's destruction do the dereffing.
263     if (!m_scriptExecutionContext->isContextThread()) {
264         m_scriptExecutionContext->postTask(DerefContextTask::create());
265         m_scriptExecutionContext.release().releaseRef();
266     }
267 }
268
269 bool Database::openAndVerifyVersion(ExceptionCode& e)
270 {
271     if (!m_scriptExecutionContext->databaseThread())
272         return false;
273     m_databaseAuthorizer = DatabaseAuthorizer::create();
274
275     bool success = false;
276     DatabaseTaskSynchronizer synchronizer;
277     OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, &synchronizer, e, success);
278
279     m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
280     synchronizer.waitForTaskCompletion();
281
282     return success;
283 }
284
285
286 static bool retrieveTextResultFromDatabase(SQLiteDatabase& db, const String& query, String& resultString)
287 {
288     SQLiteStatement statement(db, query);
289     int result = statement.prepare();
290
291     if (result != SQLResultOk) {
292         LOG_ERROR("Error (%i) preparing statement to read text result from database (%s)", result, query.ascii().data());
293         return false;
294     }
295
296     result = statement.step();
297     if (result == SQLResultRow) {
298         resultString = statement.getColumnText(0);
299         return true;
300     } else if (result == SQLResultDone) {
301         resultString = String();
302         return true;
303     } else {
304         LOG_ERROR("Error (%i) reading text result from database (%s)", result, query.ascii().data());
305         return false;
306     }
307 }
308
309 bool Database::getVersionFromDatabase(String& version)
310 {
311     DEFINE_STATIC_LOCAL(String, getVersionQuery, ("SELECT value FROM " + databaseInfoTableName() + " WHERE key = '" + databaseVersionKey() + "';"));
312
313     m_databaseAuthorizer->disable();
314
315     bool result = retrieveTextResultFromDatabase(m_sqliteDatabase, getVersionQuery.threadsafeCopy(), version);
316     if (!result)
317         LOG_ERROR("Failed to retrieve version from database %s", databaseDebugName().ascii().data());
318
319     m_databaseAuthorizer->enable();
320
321     return result;
322 }
323
324 static bool setTextValueInDatabase(SQLiteDatabase& db, const String& query, const String& value)
325 {
326     SQLiteStatement statement(db, query);
327     int result = statement.prepare();
328
329     if (result != SQLResultOk) {
330         LOG_ERROR("Failed to prepare statement to set value in database (%s)", query.ascii().data());
331         return false;
332     }
333
334     statement.bindText(1, value);
335
336     result = statement.step();
337     if (result != SQLResultDone) {
338         LOG_ERROR("Failed to step statement to set value in database (%s)", query.ascii().data());
339         return false;
340     }
341
342     return true;
343 }
344
345 bool Database::setVersionInDatabase(const String& version)
346 {
347     // The INSERT will replace an existing entry for the database with the new version number, due to the UNIQUE ON CONFLICT REPLACE
348     // clause in the CREATE statement (see Database::performOpenAndVerify()).
349     DEFINE_STATIC_LOCAL(String, setVersionQuery, ("INSERT INTO " + databaseInfoTableName() + " (key, value) VALUES ('" + databaseVersionKey() + "', ?);"));
350
351     m_databaseAuthorizer->disable();
352
353     bool result = setTextValueInDatabase(m_sqliteDatabase, setVersionQuery.threadsafeCopy(), version);
354     if (!result)
355         LOG_ERROR("Failed to set version %s in database (%s)", version.ascii().data(), setVersionQuery.ascii().data());
356
357     m_databaseAuthorizer->enable();
358
359     return result;
360 }
361
362 bool Database::versionMatchesExpected() const
363 {
364     if (!m_expectedVersion.isEmpty()) {
365         MutexLocker locker(guidMutex());
366         return m_expectedVersion == guidToVersionMap().get(m_guid);
367     }
368
369     return true;
370 }
371
372 void Database::markAsDeletedAndClose()
373 {
374     if (m_deleted || !m_scriptExecutionContext->databaseThread())
375         return;
376
377     LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this);
378     m_deleted = true;
379
380     if (m_scriptExecutionContext->databaseThread()->terminationRequested()) {
381         LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this);
382         return;
383     }
384
385     m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this);
386
387     DatabaseTaskSynchronizer synchronizer;
388     OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer);
389
390     m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
391     synchronizer.waitForTaskCompletion();
392 }
393
394 class ContextRemoveOpenDatabaseTask : public ScriptExecutionContext::Task {
395 public:
396     static PassOwnPtr<ContextRemoveOpenDatabaseTask> create(PassRefPtr<Database> database)
397     {
398         return new ContextRemoveOpenDatabaseTask(database);
399     }
400
401     virtual void performTask(ScriptExecutionContext* context)
402     {
403         context->removeOpenDatabase(m_database.get());
404         DatabaseTracker::tracker().removeOpenDatabase(m_database.get());
405     }
406
407     virtual bool isCleanupTask() const { return true; }
408
409 private:
410     ContextRemoveOpenDatabaseTask(PassRefPtr<Database> database)
411         : m_database(database)
412     {
413     }
414
415     RefPtr<Database> m_database;
416 };
417
418 void Database::close()
419 {
420     RefPtr<Database> protect = this;
421
422     if (!m_opened)
423         return;
424
425     ASSERT(m_scriptExecutionContext->databaseThread());
426     ASSERT(currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID());
427     m_sqliteDatabase.close();
428     // Must ref() before calling databaseThread()->recordDatabaseClosed().
429     m_scriptExecutionContext->databaseThread()->recordDatabaseClosed(this);
430     m_opened = false;
431
432     {
433         MutexLocker locker(guidMutex());
434
435         HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid);
436         ASSERT(hashSet);
437         ASSERT(hashSet->contains(this));
438         hashSet->remove(this);
439         if (hashSet->isEmpty()) {
440             guidToDatabaseMap().remove(m_guid);
441             delete hashSet;
442             guidToVersionMap().remove(m_guid);
443         }
444     }
445
446     m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this);
447     m_scriptExecutionContext->postTask(ContextRemoveOpenDatabaseTask::create(this));
448 }
449
450 void Database::stop()
451 {
452     // FIXME: The net effect of the following code is to remove all pending transactions and statements, but allow the current statement
453     // to run to completion.  In the future we can use the sqlite3_progress_handler or sqlite3_interrupt interfaces to cancel the current
454     // statement in response to close(), as well.
455
456     // 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
457     // a page load or closing the page
458     m_stopped = true;
459
460     {
461         MutexLocker locker(m_transactionInProgressMutex);
462         m_isTransactionQueueEnabled = false;
463         m_transactionInProgress = false;
464     }
465 }
466
467 unsigned long long Database::maximumSize() const
468 {
469     return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
470 }
471
472 void Database::disableAuthorizer()
473 {
474     ASSERT(m_databaseAuthorizer);
475     m_databaseAuthorizer->disable();
476 }
477
478 void Database::enableAuthorizer()
479 {
480     ASSERT(m_databaseAuthorizer);
481     m_databaseAuthorizer->enable();
482 }
483
484 void Database::setAuthorizerReadOnly()
485 {
486     ASSERT(m_databaseAuthorizer);
487     m_databaseAuthorizer->setReadOnly();
488 }
489
490 static int guidForOriginAndName(const String& origin, const String& name)
491 {
492     String stringID;
493     if (origin.endsWith("/"))
494         stringID = origin + name;
495     else
496         stringID = origin + "/" + name;
497
498     // Note: We don't have to use AtomicallyInitializedStatic here because
499     // this function is called once in the constructor on the main thread
500     // before any other threads that call this function are used.
501     DEFINE_STATIC_LOCAL(Mutex, stringIdentifierMutex, ());
502     MutexLocker locker(stringIdentifierMutex);
503     typedef HashMap<String, int> IDGuidMap;
504     DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ());
505     int guid = stringIdentifierToGUIDMap.get(stringID);
506     if (!guid) {
507         static int currentNewGUID = 1;
508         guid = currentNewGUID++;
509         stringIdentifierToGUIDMap.set(stringID, guid);
510     }
511
512     return guid;
513 }
514
515 void Database::resetAuthorizer()
516 {
517     if (m_databaseAuthorizer)
518         m_databaseAuthorizer->reset();
519 }
520
521 void Database::performPolicyChecks()
522 {
523     // FIXME: Code similar to the following will need to be run to enforce the per-origin size limit the spec mandates.
524     // Additionally, we might need a way to pause the database thread while the UA prompts the user for permission to
525     // increase the size limit
526
527     /*
528     if (m_databaseAuthorizer->lastActionIncreasedSize())
529         DatabaseTracker::scheduleFileSizeCheckOnMainThread(this);
530     */
531
532     notImplemented();
533 }
534
535 bool Database::performOpenAndVerify(ExceptionCode& e)
536 {
537     if (!m_sqliteDatabase.open(m_filename)) {
538         LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data());
539         e = INVALID_STATE_ERR;
540         return false;
541     }
542
543     ASSERT(m_databaseAuthorizer);
544     m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer);
545     m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime);
546
547     String currentVersion;
548     {
549         MutexLocker locker(guidMutex());
550
551         GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid);
552         if (entry != guidToVersionMap().end()) {
553             // Map null string to empty string (see updateGuidVersionMap()).
554             currentVersion = entry->second.isNull() ? String("") : entry->second;
555             LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data());
556         } else {
557             LOG(StorageAPI, "No cached version for guid %i", m_guid);
558
559             if (!m_sqliteDatabase.tableExists(databaseInfoTableName())) {
560                 m_new = true;
561
562                 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);")) {
563                     LOG_ERROR("Unable to create table %s in database %s", databaseInfoTableName().ascii().data(), databaseDebugName().ascii().data());
564                     e = INVALID_STATE_ERR;
565                     // Close the handle to the database file.
566                     m_sqliteDatabase.close();
567                     return false;
568                 }
569             }
570
571             if (!getVersionFromDatabase(currentVersion)) {
572                 LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data());
573                 e = INVALID_STATE_ERR;
574                 // Close the handle to the database file.
575                 m_sqliteDatabase.close();
576                 return false;
577             }
578             if (currentVersion.length()) {
579                 LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data());
580             } else if (!m_new || !m_creationCallback) {
581                 LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
582                 if (!setVersionInDatabase(m_expectedVersion)) {
583                     LOG_ERROR("Failed to set version %s in database %s", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data());
584                     e = INVALID_STATE_ERR;
585                     // Close the handle to the database file.
586                     m_sqliteDatabase.close();
587                     return false;
588                 }
589                 currentVersion = m_expectedVersion;
590             }
591
592             updateGuidVersionMap(m_guid, currentVersion);
593         }
594     }
595
596     if (currentVersion.isNull()) {
597         LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data());
598         currentVersion = "";
599     }
600
601     // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception.
602     // If the expected version is the empty string, then we always return with whatever version of the database we have.
603     if ((!m_new || !m_creationCallback) && m_expectedVersion.length() && m_expectedVersion != currentVersion) {
604         LOG(StorageAPI, "page expects version %s from database %s, which actually has version name %s - openDatabase() call will fail", m_expectedVersion.ascii().data(),
605             databaseDebugName().ascii().data(), currentVersion.ascii().data());
606         e = INVALID_STATE_ERR;
607         // Close the handle to the database file.
608         m_sqliteDatabase.close();
609         return false;
610     }
611
612     // All checks passed and we still have a handle to this database file.
613     // Make sure DatabaseThread closes it when DatabaseThread goes away.
614     m_opened = true;
615     if (m_scriptExecutionContext->databaseThread())
616         m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this);
617
618     return true;
619 }
620
621 void Database::changeVersion(const String& oldVersion, const String& newVersion,
622                              PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
623                              PassRefPtr<VoidCallback> successCallback)
624 {
625     m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion)));
626     MutexLocker locker(m_transactionInProgressMutex);
627     if (!m_transactionInProgress)
628         scheduleTransaction();
629 }
630
631 void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
632                            PassRefPtr<VoidCallback> successCallback, bool readOnly)
633 {
634     m_transactionQueue.append(SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly));
635     MutexLocker locker(m_transactionInProgressMutex);
636     if (!m_transactionInProgress)
637         scheduleTransaction();
638 }
639
640 void Database::scheduleTransaction()
641 {
642     ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
643     RefPtr<SQLTransaction> transaction;
644
645     if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty()) {
646         transaction = m_transactionQueue.first();
647         m_transactionQueue.removeFirst();
648     }
649
650     if (transaction && m_scriptExecutionContext->databaseThread()) {
651         OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
652         LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
653         m_transactionInProgress = true;
654         m_scriptExecutionContext->databaseThread()->scheduleTask(task.release());
655     } else
656         m_transactionInProgress = false;
657 }
658
659 void Database::scheduleTransactionStep(SQLTransaction* transaction, bool immediately)
660 {
661     if (!m_scriptExecutionContext->databaseThread())
662         return;
663
664     OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
665     LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
666     if (immediately)
667         m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
668     else
669         m_scriptExecutionContext->databaseThread()->scheduleTask(task.release());
670 }
671
672 class DeliverPendingCallbackTask : public ScriptExecutionContext::Task {
673 public:
674     static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
675     {
676         return new DeliverPendingCallbackTask(transaction);
677     }
678
679     virtual void performTask(ScriptExecutionContext*)
680     {
681         m_transaction->performPendingCallback();
682     }
683
684 private:
685     DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
686         : m_transaction(transaction)
687     {
688     }
689
690     RefPtr<SQLTransaction> m_transaction;
691 };
692
693 void Database::scheduleTransactionCallback(SQLTransaction* transaction)
694 {
695     m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction));
696 }
697
698 Vector<String> Database::performGetTableNames()
699 {
700     disableAuthorizer();
701
702     SQLiteStatement statement(m_sqliteDatabase, "SELECT name FROM sqlite_master WHERE type='table';");
703     if (statement.prepare() != SQLResultOk) {
704         LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
705         enableAuthorizer();
706         return Vector<String>();
707     }
708
709     Vector<String> tableNames;
710     int result;
711     while ((result = statement.step()) == SQLResultRow) {
712         String name = statement.getColumnText(0);
713         if (name != databaseInfoTableName())
714             tableNames.append(name);
715     }
716
717     enableAuthorizer();
718
719     if (result != SQLResultDone) {
720         LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
721         return Vector<String>();
722     }
723
724     return tableNames;
725 }
726
727 void Database::performCreationCallback()
728 {
729     m_creationCallback->handleEvent(m_scriptExecutionContext.get(), this);
730 }
731
732 SQLTransactionClient* Database::transactionClient() const
733 {
734     return m_scriptExecutionContext->databaseThread()->transactionClient();
735 }
736
737 SQLTransactionCoordinator* Database::transactionCoordinator() const
738 {
739     return m_scriptExecutionContext->databaseThread()->transactionCoordinator();
740 }
741
742 String Database::version() const
743 {
744     if (m_deleted)
745         return String();
746     MutexLocker locker(guidMutex());
747     return guidToVersionMap().get(m_guid).threadsafeCopy();
748 }
749
750 Vector<String> Database::tableNames()
751 {
752     // FIXME: Not using threadsafeCopy on these strings looks ok since threads take strict turns
753     // in dealing with them. However, if the code changes, this may not be true anymore.
754     Vector<String> result;
755     if (!m_scriptExecutionContext->databaseThread())
756         return result;
757
758     DatabaseTaskSynchronizer synchronizer;
759     OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
760
761     m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
762     synchronizer.waitForTaskCompletion();
763
764     return result;
765 }
766
767 void Database::setExpectedVersion(const String& version)
768 {
769     m_expectedVersion = version.threadsafeCopy();
770     // Update the in memory database version map.
771     MutexLocker locker(guidMutex());
772     updateGuidVersionMap(m_guid, version);
773 }
774
775 SecurityOrigin* Database::securityOrigin() const
776 {
777     if (scriptExecutionContext()->isContextThread())
778         return m_mainThreadSecurityOrigin.get();
779     if (currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID())
780         return m_databaseThreadSecurityOrigin.get();
781     return 0;
782 }
783
784 String Database::stringIdentifier() const
785 {
786     // Return a deep copy for ref counting thread safety
787     return m_name.threadsafeCopy();
788 }
789
790 String Database::displayName() const
791 {
792     // Return a deep copy for ref counting thread safety
793     return m_displayName.threadsafeCopy();
794 }
795
796 unsigned long Database::estimatedSize() const
797 {
798     return m_estimatedSize;
799 }
800
801 String Database::fileName() const
802 {
803     // Return a deep copy for ref counting thread safety
804     return m_filename.threadsafeCopy();
805 }
806
807 #endif // ENABLE(DATABASE)
808
809 } // namespace WebCore