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 #if ENABLE(DATABASE)
     33 #include "ChangeVersionWrapper.h"
     34 #include "DatabaseCallback.h"
     35 #include "DatabaseTask.h"
     36 #include "DatabaseThread.h"
     37 #include "DatabaseTracker.h"
     38 #include "Document.h"
     39 #include "InspectorDatabaseInstrumentation.h"
     40 #include "Logging.h"
     41 #include "NotImplemented.h"
     42 #include "Page.h"
     43 #include "SQLTransactionCallback.h"
     44 #include "SQLTransactionClient.h"
     45 #include "SQLTransactionCoordinator.h"
     46 #include "SQLTransactionErrorCallback.h"
     47 #include "SQLiteStatement.h"
     48 #include "ScriptController.h"
     49 #include "ScriptExecutionContext.h"
     50 #include "SecurityOrigin.h"
     51 #include "VoidCallback.h"
     52 #include <wtf/OwnPtr.h>
     53 #include <wtf/PassOwnPtr.h>
     54 #include <wtf/PassRefPtr.h>
     55 #include <wtf/RefPtr.h>
     56 #include <wtf/StdLibExtras.h>
     57 #include <wtf/text/CString.h>
     58 
     59 #if USE(JSC)
     60 #include "JSDOMWindow.h"
     61 #endif
     62 
     63 namespace WebCore {
     64 
     65 class DatabaseCreationCallbackTask : public ScriptExecutionContext::Task {
     66 public:
     67     static PassOwnPtr<DatabaseCreationCallbackTask> create(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> creationCallback)
     68     {
     69         return adoptPtr(new DatabaseCreationCallbackTask(database, creationCallback));
     70     }
     71 
     72     virtual void performTask(ScriptExecutionContext*)
     73     {
     74         m_creationCallback->handleEvent(m_database.get());
     75     }
     76 
     77 private:
     78     DatabaseCreationCallbackTask(PassRefPtr<Database> database, PassRefPtr<DatabaseCallback> callback)
     79         : m_database(database)
     80         , m_creationCallback(callback)
     81     {
     82     }
     83 
     84     RefPtr<Database> m_database;
     85     RefPtr<DatabaseCallback> m_creationCallback;
     86 };
     87 
     88 PassRefPtr<Database> Database::openDatabase(ScriptExecutionContext* context, const String& name,
     89                                             const String& expectedVersion, const String& displayName,
     90                                             unsigned long estimatedSize, PassRefPtr<DatabaseCallback> creationCallback,
     91                                             ExceptionCode& e)
     92 {
     93     if (!DatabaseTracker::tracker().canEstablishDatabase(context, name, displayName, estimatedSize)) {
     94         LOG(StorageAPI, "Database %s for origin %s not allowed to be established", name.ascii().data(), context->securityOrigin()->toString().ascii().data());
     95         return 0;
     96     }
     97 
     98     RefPtr<Database> database = adoptRef(new Database(context, name, expectedVersion, displayName, estimatedSize));
     99 
    100     if (!database->openAndVerifyVersion(!creationCallback, e)) {
    101         LOG(StorageAPI, "Failed to open and verify version (expected %s) of database %s", expectedVersion.ascii().data(), database->databaseDebugName().ascii().data());
    102         DatabaseTracker::tracker().removeOpenDatabase(database.get());
    103         return 0;
    104     }
    105 
    106     DatabaseTracker::tracker().setDatabaseDetails(context->securityOrigin(), name, displayName, estimatedSize);
    107 
    108     context->setHasOpenDatabases();
    109 
    110     InspectorInstrumentation::didOpenDatabase(context, database, context->securityOrigin()->host(), name, expectedVersion);
    111 
    112     // If it's a new database and a creation callback was provided, reset the expected
    113     // version to "" and schedule the creation callback. Because of some subtle String
    114     // implementation issues, we have to reset m_expectedVersion here instead of doing
    115     // it inside performOpenAndVerify() which is run on the DB thread.
    116     if (database->isNew() && creationCallback.get()) {
    117         database->m_expectedVersion = "";
    118         LOG(StorageAPI, "Scheduling DatabaseCreationCallbackTask for database %p\n", database.get());
    119         database->m_scriptExecutionContext->postTask(DatabaseCreationCallbackTask::create(database, creationCallback));
    120     }
    121 
    122     return database;
    123 }
    124 
    125 Database::Database(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
    126     : AbstractDatabase(context, name, expectedVersion, displayName, estimatedSize)
    127     , m_transactionInProgress(false)
    128     , m_isTransactionQueueEnabled(true)
    129     , m_deleted(false)
    130 {
    131     m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->threadsafeCopy();
    132 
    133     ScriptController::initializeThreading();
    134     ASSERT(m_scriptExecutionContext->databaseThread());
    135 }
    136 
    137 class DerefContextTask : public ScriptExecutionContext::Task {
    138 public:
    139     static PassOwnPtr<DerefContextTask> create(PassRefPtr<ScriptExecutionContext> context)
    140     {
    141         return adoptPtr(new DerefContextTask(context));
    142     }
    143 
    144     virtual void performTask(ScriptExecutionContext* context)
    145     {
    146         ASSERT_UNUSED(context, context == m_context);
    147         m_context.clear();
    148     }
    149 
    150     virtual bool isCleanupTask() const { return true; }
    151 
    152 private:
    153     DerefContextTask(PassRefPtr<ScriptExecutionContext> context)
    154         : m_context(context)
    155     {
    156     }
    157 
    158     RefPtr<ScriptExecutionContext> m_context;
    159 };
    160 
    161 Database::~Database()
    162 {
    163     // 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.
    164     if (!m_scriptExecutionContext->isContextThread()) {
    165         // Grab a pointer to the script execution here because we're releasing it when we pass it to
    166         // DerefContextTask::create.
    167         ScriptExecutionContext* scriptExecutionContext = m_scriptExecutionContext.get();
    168 
    169         scriptExecutionContext->postTask(DerefContextTask::create(m_scriptExecutionContext.release()));
    170     }
    171 }
    172 
    173 String Database::version() const
    174 {
    175     if (m_deleted)
    176         return String();
    177     return AbstractDatabase::version();
    178 }
    179 
    180 bool Database::openAndVerifyVersion(bool setVersionInNewDatabase, ExceptionCode& e)
    181 {
    182     DatabaseTaskSynchronizer synchronizer;
    183     if (!m_scriptExecutionContext->databaseThread() || m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer))
    184         return false;
    185 
    186     bool success = false;
    187     OwnPtr<DatabaseOpenTask> task = DatabaseOpenTask::create(this, setVersionInNewDatabase, &synchronizer, e, success);
    188     m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
    189     synchronizer.waitForTaskCompletion();
    190 
    191     return success;
    192 }
    193 
    194 void Database::markAsDeletedAndClose()
    195 {
    196     if (m_deleted || !m_scriptExecutionContext->databaseThread())
    197         return;
    198 
    199     LOG(StorageAPI, "Marking %s (%p) as deleted", stringIdentifier().ascii().data(), this);
    200     m_deleted = true;
    201 
    202     DatabaseTaskSynchronizer synchronizer;
    203     if (m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer)) {
    204         LOG(StorageAPI, "Database handle %p is on a terminated DatabaseThread, cannot be marked for normal closure\n", this);
    205         return;
    206     }
    207 
    208     OwnPtr<DatabaseCloseTask> task = DatabaseCloseTask::create(this, &synchronizer);
    209     m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
    210     synchronizer.waitForTaskCompletion();
    211 }
    212 
    213 void Database::close()
    214 {
    215     ASSERT(m_scriptExecutionContext->databaseThread());
    216     ASSERT(currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID());
    217 
    218     {
    219         MutexLocker locker(m_transactionInProgressMutex);
    220         m_isTransactionQueueEnabled = false;
    221         m_transactionInProgress = false;
    222     }
    223 
    224     closeDatabase();
    225 
    226     // Must ref() before calling databaseThread()->recordDatabaseClosed().
    227     RefPtr<Database> protect = this;
    228     m_scriptExecutionContext->databaseThread()->recordDatabaseClosed(this);
    229     m_scriptExecutionContext->databaseThread()->unscheduleDatabaseTasks(this);
    230     DatabaseTracker::tracker().removeOpenDatabase(this);
    231 }
    232 
    233 void Database::closeImmediately()
    234 {
    235     DatabaseThread* databaseThread = scriptExecutionContext()->databaseThread();
    236     if (databaseThread && !databaseThread->terminationRequested() && opened())
    237         databaseThread->scheduleImmediateTask(DatabaseCloseTask::create(this, 0));
    238 }
    239 
    240 unsigned long long Database::maximumSize() const
    241 {
    242     return DatabaseTracker::tracker().getMaxSizeForDatabase(this);
    243 }
    244 
    245 bool Database::performOpenAndVerify(bool setVersionInNewDatabase, ExceptionCode& e)
    246 {
    247     if (AbstractDatabase::performOpenAndVerify(setVersionInNewDatabase, e)) {
    248         if (m_scriptExecutionContext->databaseThread())
    249             m_scriptExecutionContext->databaseThread()->recordDatabaseOpen(this);
    250 
    251         return true;
    252     }
    253 
    254     return false;
    255 }
    256 
    257 void Database::changeVersion(const String& oldVersion, const String& newVersion,
    258                              PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
    259                              PassRefPtr<VoidCallback> successCallback)
    260 {
    261     RefPtr<SQLTransaction> transaction =
    262         SQLTransaction::create(this, callback, errorCallback, successCallback, ChangeVersionWrapper::create(oldVersion, newVersion));
    263     MutexLocker locker(m_transactionInProgressMutex);
    264     m_transactionQueue.append(transaction.release());
    265     if (!m_transactionInProgress)
    266         scheduleTransaction();
    267 }
    268 
    269 void Database::transaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
    270 {
    271     runTransaction(callback, errorCallback, successCallback, false);
    272 }
    273 
    274 void Database::readTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback, PassRefPtr<VoidCallback> successCallback)
    275 {
    276     runTransaction(callback, errorCallback, successCallback, true);
    277 }
    278 
    279 void Database::runTransaction(PassRefPtr<SQLTransactionCallback> callback, PassRefPtr<SQLTransactionErrorCallback> errorCallback,
    280                               PassRefPtr<VoidCallback> successCallback, bool readOnly)
    281 {
    282     RefPtr<SQLTransaction> transaction =
    283         SQLTransaction::create(this, callback, errorCallback, successCallback, 0, readOnly);
    284     MutexLocker locker(m_transactionInProgressMutex);
    285     m_transactionQueue.append(transaction.release());
    286     if (!m_transactionInProgress)
    287         scheduleTransaction();
    288 }
    289 
    290 void Database::inProgressTransactionCompleted()
    291 {
    292     MutexLocker locker(m_transactionInProgressMutex);
    293     m_transactionInProgress = false;
    294     scheduleTransaction();
    295 }
    296 
    297 void Database::scheduleTransaction()
    298 {
    299     ASSERT(!m_transactionInProgressMutex.tryLock()); // Locked by caller.
    300     RefPtr<SQLTransaction> transaction;
    301 
    302     if (m_isTransactionQueueEnabled && !m_transactionQueue.isEmpty()) {
    303         transaction = m_transactionQueue.takeFirst();
    304     }
    305 
    306     if (transaction && m_scriptExecutionContext->databaseThread()) {
    307         OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
    308         LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for transaction %p\n", task.get(), task->transaction());
    309         m_transactionInProgress = true;
    310         m_scriptExecutionContext->databaseThread()->scheduleTask(task.release());
    311     } else
    312         m_transactionInProgress = false;
    313 }
    314 
    315 void Database::scheduleTransactionStep(SQLTransaction* transaction, bool immediately)
    316 {
    317     if (!m_scriptExecutionContext->databaseThread())
    318         return;
    319 
    320     OwnPtr<DatabaseTransactionTask> task = DatabaseTransactionTask::create(transaction);
    321     LOG(StorageAPI, "Scheduling DatabaseTransactionTask %p for the transaction step\n", task.get());
    322     if (immediately)
    323         m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
    324     else
    325         m_scriptExecutionContext->databaseThread()->scheduleTask(task.release());
    326 }
    327 
    328 class DeliverPendingCallbackTask : public ScriptExecutionContext::Task {
    329 public:
    330     static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
    331     {
    332         return adoptPtr(new DeliverPendingCallbackTask(transaction));
    333     }
    334 
    335     virtual void performTask(ScriptExecutionContext*)
    336     {
    337         m_transaction->performPendingCallback();
    338     }
    339 
    340 private:
    341     DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
    342         : m_transaction(transaction)
    343     {
    344     }
    345 
    346     RefPtr<SQLTransaction> m_transaction;
    347 };
    348 
    349 void Database::scheduleTransactionCallback(SQLTransaction* transaction)
    350 {
    351     m_scriptExecutionContext->postTask(DeliverPendingCallbackTask::create(transaction));
    352 }
    353 
    354 Vector<String> Database::performGetTableNames()
    355 {
    356     disableAuthorizer();
    357 
    358     SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
    359     if (statement.prepare() != SQLResultOk) {
    360         LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
    361         enableAuthorizer();
    362         return Vector<String>();
    363     }
    364 
    365     Vector<String> tableNames;
    366     int result;
    367     while ((result = statement.step()) == SQLResultRow) {
    368         String name = statement.getColumnText(0);
    369         if (name != databaseInfoTableName())
    370             tableNames.append(name);
    371     }
    372 
    373     enableAuthorizer();
    374 
    375     if (result != SQLResultDone) {
    376         LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
    377         return Vector<String>();
    378     }
    379 
    380     return tableNames;
    381 }
    382 
    383 SQLTransactionClient* Database::transactionClient() const
    384 {
    385     return m_scriptExecutionContext->databaseThread()->transactionClient();
    386 }
    387 
    388 SQLTransactionCoordinator* Database::transactionCoordinator() const
    389 {
    390     return m_scriptExecutionContext->databaseThread()->transactionCoordinator();
    391 }
    392 
    393 Vector<String> Database::tableNames()
    394 {
    395     // FIXME: Not using threadsafeCopy on these strings looks ok since threads take strict turns
    396     // in dealing with them. However, if the code changes, this may not be true anymore.
    397     Vector<String> result;
    398     DatabaseTaskSynchronizer synchronizer;
    399     if (!m_scriptExecutionContext->databaseThread() || m_scriptExecutionContext->databaseThread()->terminationRequested(&synchronizer))
    400         return result;
    401 
    402     OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
    403     m_scriptExecutionContext->databaseThread()->scheduleImmediateTask(task.release());
    404     synchronizer.waitForTaskCompletion();
    405 
    406     return result;
    407 }
    408 
    409 SecurityOrigin* Database::securityOrigin() const
    410 {
    411     if (m_scriptExecutionContext->isContextThread())
    412         return m_contextThreadSecurityOrigin.get();
    413     if (currentThread() == m_scriptExecutionContext->databaseThread()->getThreadID())
    414         return m_databaseThreadSecurityOrigin.get();
    415     return 0;
    416 }
    417 
    418 } // namespace WebCore
    419 
    420 #endif // ENABLE(DATABASE)
    421