Home | History | Annotate | Download | only in storage
      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
    766