Home | History | Annotate | Download | only in webdatabase
      1 /*
      2  * Copyright (C) 2007, 2008, 2013 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 "modules/webdatabase/Database.h"
     31 
     32 #include "core/dom/CrossThreadTask.h"
     33 #include "core/dom/Document.h"
     34 #include "core/dom/ExecutionContext.h"
     35 #include "core/html/VoidCallback.h"
     36 #include "core/page/Page.h"
     37 #include "platform/Logging.h"
     38 #include "modules/webdatabase/sqlite/SQLiteStatement.h"
     39 #include "modules/webdatabase/ChangeVersionData.h"
     40 #include "modules/webdatabase/DatabaseCallback.h"
     41 #include "modules/webdatabase/DatabaseContext.h"
     42 #include "modules/webdatabase/DatabaseManager.h"
     43 #include "modules/webdatabase/DatabaseTask.h"
     44 #include "modules/webdatabase/DatabaseThread.h"
     45 #include "modules/webdatabase/DatabaseTracker.h"
     46 #include "modules/webdatabase/SQLError.h"
     47 #include "modules/webdatabase/SQLTransaction.h"
     48 #include "modules/webdatabase/SQLTransactionCallback.h"
     49 #include "modules/webdatabase/SQLTransactionErrorCallback.h"
     50 #include "platform/weborigin/SecurityOrigin.h"
     51 #include "wtf/OwnPtr.h"
     52 #include "wtf/PassOwnPtr.h"
     53 #include "wtf/PassRefPtr.h"
     54 #include "wtf/RefPtr.h"
     55 #include "wtf/StdLibExtras.h"
     56 #include "wtf/text/CString.h"
     57 
     58 namespace WebCore {
     59 
     60 PassRefPtr<Database> Database::create(ExecutionContext*, PassRefPtr<DatabaseBackendBase> backend)
     61 {
     62     // FIXME: Currently, we're only simulating the backend by return the
     63     // frontend database as its own the backend. When we split the 2 apart,
     64     // this create() function should be changed to be a factory method for
     65     // instantiating the backend.
     66     return static_cast<Database*>(backend.get());
     67 }
     68 
     69 Database::Database(PassRefPtr<DatabaseContext> databaseContext,
     70     const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize)
     71     : DatabaseBase(databaseContext->executionContext())
     72     , DatabaseBackend(databaseContext, name, expectedVersion, displayName, estimatedSize)
     73     , m_databaseContext(DatabaseBackend::databaseContext())
     74 {
     75     ScriptWrappable::init(this);
     76     m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy();
     77     setFrontend(this);
     78 
     79     ASSERT(m_databaseContext->databaseThread());
     80 }
     81 
     82 class DerefContextTask : public ExecutionContextTask {
     83 public:
     84     static PassOwnPtr<DerefContextTask> create(PassRefPtr<ExecutionContext> context)
     85     {
     86         return adoptPtr(new DerefContextTask(context));
     87     }
     88 
     89     virtual void performTask(ExecutionContext* context)
     90     {
     91         ASSERT_UNUSED(context, context == m_context);
     92         m_context.clear();
     93     }
     94 
     95     virtual bool isCleanupTask() const { return true; }
     96 
     97 private:
     98     DerefContextTask(PassRefPtr<ExecutionContext> context)
     99         : m_context(context)
    100     {
    101     }
    102 
    103     RefPtr<ExecutionContext> m_context;
    104 };
    105 
    106 Database::~Database()
    107 {
    108     // The reference to the ExecutionContext 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.
    109     if (!m_executionContext->isContextThread()) {
    110         // Grab a pointer to the script execution here because we're releasing it when we pass it to
    111         // DerefContextTask::create.
    112         ExecutionContext* executionContext = m_executionContext.get();
    113 
    114         executionContext->postTask(DerefContextTask::create(m_executionContext.release()));
    115     }
    116 }
    117 
    118 Database* Database::from(DatabaseBackend* backend)
    119 {
    120     return static_cast<Database*>(backend->m_frontend);
    121 }
    122 
    123 PassRefPtr<DatabaseBackend> Database::backend()
    124 {
    125     return this;
    126 }
    127 
    128 String Database::version() const
    129 {
    130     return DatabaseBackendBase::version();
    131 }
    132 
    133 void Database::closeImmediately()
    134 {
    135     ASSERT(m_executionContext->isContextThread());
    136     DatabaseThread* databaseThread = databaseContext()->databaseThread();
    137     if (databaseThread && !databaseThread->terminationRequested() && opened()) {
    138         logErrorMessage("forcibly closing database");
    139         databaseThread->scheduleTask(DatabaseCloseTask::create(this, 0));
    140     }
    141 }
    142 
    143 void Database::changeVersion(const String& oldVersion, const String& newVersion, PassOwnPtr<SQLTransactionCallback> callback, PassOwnPtr<SQLTransactionErrorCallback> errorCallback, PassOwnPtr<VoidCallback> successCallback)
    144 {
    145     ChangeVersionData data(oldVersion, newVersion);
    146     runTransaction(callback, errorCallback, successCallback, false, &data);
    147 }
    148 
    149 void Database::transaction(PassOwnPtr<SQLTransactionCallback> callback, PassOwnPtr<SQLTransactionErrorCallback> errorCallback, PassOwnPtr<VoidCallback> successCallback)
    150 {
    151     runTransaction(callback, errorCallback, successCallback, false);
    152 }
    153 
    154 void Database::readTransaction(PassOwnPtr<SQLTransactionCallback> callback, PassOwnPtr<SQLTransactionErrorCallback> errorCallback, PassOwnPtr<VoidCallback> successCallback)
    155 {
    156     runTransaction(callback, errorCallback, successCallback, true);
    157 }
    158 
    159 static void callTransactionErrorCallback(ExecutionContext*, PassOwnPtr<SQLTransactionErrorCallback> callback, PassRefPtr<SQLError> error)
    160 {
    161     callback->handleEvent(error.get());
    162 }
    163 
    164 void Database::runTransaction(PassOwnPtr<SQLTransactionCallback> callback, PassOwnPtr<SQLTransactionErrorCallback> errorCallback,
    165     PassOwnPtr<VoidCallback> successCallback, bool readOnly, const ChangeVersionData* changeVersionData)
    166 {
    167     // FIXME: Rather than passing errorCallback to SQLTransaction and then sometimes firing it ourselves,
    168     // this code should probably be pushed down into DatabaseBackend so that we only create the SQLTransaction
    169     // if we're actually going to run it.
    170 #if !ASSERT_DISABLED
    171     SQLTransactionErrorCallback* originalErrorCallback = errorCallback.get();
    172 #endif
    173     RefPtr<SQLTransaction> transaction = SQLTransaction::create(this, callback, successCallback, errorCallback, readOnly);
    174     RefPtr<SQLTransactionBackend> transactionBackend = backend()->runTransaction(transaction, readOnly, changeVersionData);
    175     if (!transactionBackend) {
    176         OwnPtr<SQLTransactionErrorCallback> callback = transaction->releaseErrorCallback();
    177         ASSERT(callback == originalErrorCallback);
    178         if (callback) {
    179             RefPtr<SQLError> error = SQLError::create(SQLError::UNKNOWN_ERR, "database has been closed");
    180             executionContext()->postTask(createCallbackTask(&callTransactionErrorCallback, callback.release(), error.release()));
    181         }
    182     }
    183 }
    184 
    185 class DeliverPendingCallbackTask : public ExecutionContextTask {
    186 public:
    187     static PassOwnPtr<DeliverPendingCallbackTask> create(PassRefPtr<SQLTransaction> transaction)
    188     {
    189         return adoptPtr(new DeliverPendingCallbackTask(transaction));
    190     }
    191 
    192     virtual void performTask(ExecutionContext*)
    193     {
    194         m_transaction->performPendingCallback();
    195     }
    196 
    197 private:
    198     DeliverPendingCallbackTask(PassRefPtr<SQLTransaction> transaction)
    199         : m_transaction(transaction)
    200     {
    201     }
    202 
    203     RefPtr<SQLTransaction> m_transaction;
    204 };
    205 
    206 void Database::scheduleTransactionCallback(SQLTransaction* transaction)
    207 {
    208     m_executionContext->postTask(DeliverPendingCallbackTask::create(transaction));
    209 }
    210 
    211 Vector<String> Database::performGetTableNames()
    212 {
    213     disableAuthorizer();
    214 
    215     SQLiteStatement statement(sqliteDatabase(), "SELECT name FROM sqlite_master WHERE type='table';");
    216     if (statement.prepare() != SQLResultOk) {
    217         WTF_LOG_ERROR("Unable to retrieve list of tables for database %s", databaseDebugName().ascii().data());
    218         enableAuthorizer();
    219         return Vector<String>();
    220     }
    221 
    222     Vector<String> tableNames;
    223     int result;
    224     while ((result = statement.step()) == SQLResultRow) {
    225         String name = statement.getColumnText(0);
    226         if (name != databaseInfoTableName())
    227             tableNames.append(name);
    228     }
    229 
    230     enableAuthorizer();
    231 
    232     if (result != SQLResultDone) {
    233         WTF_LOG_ERROR("Error getting tables for database %s", databaseDebugName().ascii().data());
    234         return Vector<String>();
    235     }
    236 
    237     return tableNames;
    238 }
    239 
    240 Vector<String> Database::tableNames()
    241 {
    242     // FIXME: Not using isolatedCopy on these strings looks ok since threads take strict turns
    243     // in dealing with them. However, if the code changes, this may not be true anymore.
    244     Vector<String> result;
    245     DatabaseTaskSynchronizer synchronizer;
    246     if (!databaseContext()->databaseThread() || databaseContext()->databaseThread()->terminationRequested(&synchronizer))
    247         return result;
    248 
    249     OwnPtr<DatabaseTableNamesTask> task = DatabaseTableNamesTask::create(this, &synchronizer, result);
    250     databaseContext()->databaseThread()->scheduleTask(task.release());
    251     synchronizer.waitForTaskCompletion();
    252 
    253     return result;
    254 }
    255 
    256 SecurityOrigin* Database::securityOrigin() const
    257 {
    258     if (m_executionContext->isContextThread())
    259         return m_contextThreadSecurityOrigin.get();
    260     if (databaseContext()->databaseThread()->isDatabaseThread())
    261         return m_databaseThreadSecurityOrigin.get();
    262     return 0;
    263 }
    264 
    265 void Database::reportStartTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
    266 {
    267     backend()->reportStartTransactionResult(errorSite, webSqlErrorCode, sqliteErrorCode);
    268 }
    269 
    270 void Database::reportCommitTransactionResult(int errorSite, int webSqlErrorCode, int sqliteErrorCode)
    271 {
    272     backend()->reportCommitTransactionResult(errorSite, webSqlErrorCode, sqliteErrorCode);
    273 }
    274 
    275 
    276 } // namespace WebCore
    277