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