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