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/SQLTransaction.h"
     31 
     32 #include "bindings/core/v8/ExceptionState.h"
     33 #include "core/dom/ExceptionCode.h"
     34 #include "core/html/VoidCallback.h"
     35 #include "modules/webdatabase/Database.h"
     36 #include "modules/webdatabase/DatabaseAuthorizer.h"
     37 #include "modules/webdatabase/DatabaseContext.h"
     38 #include "modules/webdatabase/SQLError.h"
     39 #include "modules/webdatabase/SQLStatementCallback.h"
     40 #include "modules/webdatabase/SQLStatementErrorCallback.h"
     41 #include "modules/webdatabase/SQLTransactionBackend.h"
     42 #include "modules/webdatabase/SQLTransactionCallback.h"
     43 #include "modules/webdatabase/SQLTransactionClient.h" // FIXME: Should be used in the backend only.
     44 #include "modules/webdatabase/SQLTransactionErrorCallback.h"
     45 #include "platform/Logging.h"
     46 #include "wtf/StdLibExtras.h"
     47 #include "wtf/Vector.h"
     48 
     49 namespace blink {
     50 
     51 PassRefPtrWillBeRawPtr<SQLTransaction> SQLTransaction::create(Database* db, SQLTransactionCallback* callback,
     52     VoidCallback* successCallback, SQLTransactionErrorCallback* errorCallback,
     53     bool readOnly)
     54 {
     55     return adoptRefWillBeNoop(new SQLTransaction(db, callback, successCallback, errorCallback, readOnly));
     56 }
     57 
     58 SQLTransaction::SQLTransaction(Database* db, SQLTransactionCallback* callback,
     59     VoidCallback* successCallback, SQLTransactionErrorCallback* errorCallback,
     60     bool readOnly)
     61     : m_database(db)
     62     , m_callback(callback)
     63     , m_successCallback(successCallback)
     64     , m_errorCallback(errorCallback)
     65     , m_executeSqlAllowed(false)
     66     , m_readOnly(readOnly)
     67 {
     68     ASSERT(m_database);
     69 }
     70 
     71 SQLTransaction::~SQLTransaction()
     72 {
     73 }
     74 
     75 void SQLTransaction::trace(Visitor* visitor)
     76 {
     77     visitor->trace(m_database);
     78     visitor->trace(m_backend);
     79     visitor->trace(m_callback);
     80     visitor->trace(m_successCallback);
     81     visitor->trace(m_errorCallback);
     82 }
     83 
     84 bool SQLTransaction::hasCallback() const
     85 {
     86     return m_callback;
     87 }
     88 
     89 bool SQLTransaction::hasSuccessCallback() const
     90 {
     91     return m_successCallback;
     92 }
     93 
     94 bool SQLTransaction::hasErrorCallback() const
     95 {
     96     return m_errorCallback;
     97 }
     98 
     99 void SQLTransaction::setBackend(SQLTransactionBackend* backend)
    100 {
    101     ASSERT(!m_backend);
    102     m_backend = backend;
    103 }
    104 
    105 SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionState state)
    106 {
    107     static const StateFunction stateFunctions[] = {
    108         &SQLTransaction::unreachableState,                // 0. illegal
    109         &SQLTransaction::unreachableState,                // 1. idle
    110         &SQLTransaction::unreachableState,                // 2. acquireLock
    111         &SQLTransaction::unreachableState,                // 3. openTransactionAndPreflight
    112         &SQLTransaction::sendToBackendState,              // 4. runStatements
    113         &SQLTransaction::unreachableState,                // 5. postflightAndCommit
    114         &SQLTransaction::sendToBackendState,              // 6. cleanupAndTerminate
    115         &SQLTransaction::sendToBackendState,              // 7. cleanupAfterTransactionErrorCallback
    116         &SQLTransaction::deliverTransactionCallback,      // 8.
    117         &SQLTransaction::deliverTransactionErrorCallback, // 9.
    118         &SQLTransaction::deliverStatementCallback,        // 10.
    119         &SQLTransaction::deliverQuotaIncreaseCallback,    // 11.
    120         &SQLTransaction::deliverSuccessCallback           // 12.
    121     };
    122 
    123     ASSERT(WTF_ARRAY_LENGTH(stateFunctions) == static_cast<int>(SQLTransactionState::NumberOfStates));
    124     ASSERT(state < SQLTransactionState::NumberOfStates);
    125 
    126     return stateFunctions[static_cast<int>(state)];
    127 }
    128 
    129 // requestTransitToState() can be called from the backend. Hence, it should
    130 // NOT be modifying SQLTransactionBackend in general. The only safe field to
    131 // modify is m_requestedState which is meant for this purpose.
    132 void SQLTransaction::requestTransitToState(SQLTransactionState nextState)
    133 {
    134     WTF_LOG(StorageAPI, "Scheduling %s for transaction %p\n", nameForSQLTransactionState(nextState), this);
    135     m_requestedState = nextState;
    136     m_database->scheduleTransactionCallback(this);
    137 }
    138 
    139 SQLTransactionState SQLTransaction::nextStateForTransactionError()
    140 {
    141     ASSERT(m_transactionError);
    142     if (hasErrorCallback())
    143         return SQLTransactionState::DeliverTransactionErrorCallback;
    144 
    145     // No error callback, so fast-forward to:
    146     // Transaction Step 11 - Rollback the transaction.
    147     return SQLTransactionState::CleanupAfterTransactionErrorCallback;
    148 }
    149 
    150 SQLTransactionState SQLTransaction::deliverTransactionCallback()
    151 {
    152     bool shouldDeliverErrorCallback = false;
    153 
    154     // Spec 4.3.2 4: Invoke the transaction callback with the new SQLTransaction object
    155     if (SQLTransactionCallback* callback = m_callback.release()) {
    156         m_executeSqlAllowed = true;
    157         shouldDeliverErrorCallback = !callback->handleEvent(this);
    158         m_executeSqlAllowed = false;
    159     }
    160 
    161     // Spec 4.3.2 5: If the transaction callback was null or raised an exception, jump to the error callback
    162     SQLTransactionState nextState = SQLTransactionState::RunStatements;
    163     if (shouldDeliverErrorCallback) {
    164         m_database->reportStartTransactionResult(5, SQLError::UNKNOWN_ERR, 0);
    165         m_transactionError = SQLErrorData::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception");
    166         nextState = SQLTransactionState::DeliverTransactionErrorCallback;
    167     }
    168     m_database->reportStartTransactionResult(0, -1, 0); // OK
    169     return nextState;
    170 }
    171 
    172 SQLTransactionState SQLTransaction::deliverTransactionErrorCallback()
    173 {
    174     // Spec 4.3.2.10: If exists, invoke error callback with the last
    175     // error to have occurred in this transaction.
    176     if (SQLTransactionErrorCallback* errorCallback = m_errorCallback.release()) {
    177         // If we get here with an empty m_transactionError, then the backend
    178         // must be waiting in the idle state waiting for this state to finish.
    179         // Hence, it's thread safe to fetch the backend transactionError without
    180         // a lock.
    181         if (!m_transactionError) {
    182             ASSERT(m_backend->transactionError());
    183             m_transactionError = SQLErrorData::create(*m_backend->transactionError());
    184         }
    185         ASSERT(m_transactionError);
    186         RefPtrWillBeRawPtr<SQLError> error = SQLError::create(*m_transactionError);
    187         errorCallback->handleEvent(error.get());
    188 
    189         m_transactionError = nullptr;
    190     }
    191 
    192     clearCallbacks();
    193 
    194     // Spec 4.3.2.10: Rollback the transaction.
    195     return SQLTransactionState::CleanupAfterTransactionErrorCallback;
    196 }
    197 
    198 SQLTransactionState SQLTransaction::deliverStatementCallback()
    199 {
    200     // Spec 4.3.2.6.6 and 4.3.2.6.3: If the statement callback went wrong, jump to the transaction error callback
    201     // Otherwise, continue to loop through the statement queue
    202     m_executeSqlAllowed = true;
    203 
    204     SQLStatement* currentStatement = m_backend->currentStatement();
    205     ASSERT(currentStatement);
    206 
    207     bool result = currentStatement->performCallback(this);
    208 
    209     m_executeSqlAllowed = false;
    210 
    211     if (result) {
    212         m_database->reportCommitTransactionResult(2, SQLError::UNKNOWN_ERR, 0);
    213         m_transactionError = SQLErrorData::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false");
    214         return nextStateForTransactionError();
    215     }
    216     return SQLTransactionState::RunStatements;
    217 }
    218 
    219 SQLTransactionState SQLTransaction::deliverQuotaIncreaseCallback()
    220 {
    221     ASSERT(m_backend->currentStatement());
    222 
    223     bool shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(database());
    224     m_backend->setShouldRetryCurrentStatement(shouldRetryCurrentStatement);
    225 
    226     return SQLTransactionState::RunStatements;
    227 }
    228 
    229 SQLTransactionState SQLTransaction::deliverSuccessCallback()
    230 {
    231     // Spec 4.3.2.8: Deliver success callback.
    232     if (VoidCallback* successCallback = m_successCallback.release())
    233         successCallback->handleEvent();
    234 
    235     clearCallbacks();
    236 
    237     // Schedule a "post-success callback" step to return control to the database thread in case there
    238     // are further transactions queued up for this Database
    239     return SQLTransactionState::CleanupAndTerminate;
    240 }
    241 
    242 // This state function is used as a stub function to plug unimplemented states
    243 // in the state dispatch table. They are unimplemented because they should
    244 // never be reached in the course of correct execution.
    245 SQLTransactionState SQLTransaction::unreachableState()
    246 {
    247     ASSERT_NOT_REACHED();
    248     return SQLTransactionState::End;
    249 }
    250 
    251 SQLTransactionState SQLTransaction::sendToBackendState()
    252 {
    253     ASSERT(m_nextState != SQLTransactionState::Idle);
    254     m_backend->requestTransitToState(m_nextState);
    255     return SQLTransactionState::Idle;
    256 }
    257 
    258 void SQLTransaction::performPendingCallback()
    259 {
    260     computeNextStateAndCleanupIfNeeded();
    261     runStateMachine();
    262 }
    263 
    264 void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, SQLStatementCallback* callback, SQLStatementErrorCallback* callbackError, ExceptionState& exceptionState)
    265 {
    266     if (!m_executeSqlAllowed) {
    267         exceptionState.throwDOMException(InvalidStateError, "SQL execution is disallowed.");
    268         return;
    269     }
    270 
    271     if (!m_database->opened()) {
    272         exceptionState.throwDOMException(InvalidStateError, "The database has not been opened.");
    273         return;
    274     }
    275 
    276     int permissions = DatabaseAuthorizer::ReadWriteMask;
    277     if (!m_database->databaseContext()->allowDatabaseAccess())
    278         permissions |= DatabaseAuthorizer::NoAccessMask;
    279     else if (m_readOnly)
    280         permissions |= DatabaseAuthorizer::ReadOnlyMask;
    281 
    282     OwnPtrWillBeRawPtr<SQLStatement> statement = SQLStatement::create(m_database.get(), callback, callbackError);
    283     m_backend->executeSQL(statement.release(), sqlStatement, arguments, permissions);
    284 }
    285 
    286 bool SQLTransaction::computeNextStateAndCleanupIfNeeded()
    287 {
    288     // Only honor the requested state transition if we're not supposed to be
    289     // cleaning up and shutting down:
    290     if (m_database->opened()) {
    291         setStateToRequestedState();
    292         ASSERT(m_nextState == SQLTransactionState::End
    293             || m_nextState == SQLTransactionState::DeliverTransactionCallback
    294             || m_nextState == SQLTransactionState::DeliverTransactionErrorCallback
    295             || m_nextState == SQLTransactionState::DeliverStatementCallback
    296             || m_nextState == SQLTransactionState::DeliverQuotaIncreaseCallback
    297             || m_nextState == SQLTransactionState::DeliverSuccessCallback);
    298 
    299         WTF_LOG(StorageAPI, "Callback %s\n", nameForSQLTransactionState(m_nextState));
    300         return false;
    301     }
    302 
    303     clearCallbacks();
    304     m_nextState = SQLTransactionState::CleanupAndTerminate;
    305 
    306     return true;
    307 }
    308 
    309 void SQLTransaction::clearCallbacks()
    310 {
    311     m_callback.clear();
    312     m_successCallback.clear();
    313     m_errorCallback.clear();
    314 }
    315 
    316 SQLTransactionErrorCallback* SQLTransaction::releaseErrorCallback()
    317 {
    318     return m_errorCallback.release();
    319 }
    320 
    321 } // namespace blink
    322