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
49namespace WebCore {
50
51PassRefPtr<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
58SQLTransaction::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
72bool SQLTransaction::hasCallback() const
73{
74    return m_callbackWrapper.hasCallback();
75}
76
77bool SQLTransaction::hasSuccessCallback() const
78{
79    return m_successCallbackWrapper.hasCallback();
80}
81
82bool SQLTransaction::hasErrorCallback() const
83{
84    return m_errorCallbackWrapper.hasCallback();
85}
86
87void SQLTransaction::setBackend(AbstractSQLTransactionBackend* backend)
88{
89    ASSERT(!m_backend);
90    m_backend = backend;
91}
92
93SQLTransaction::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.
120void 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
127SQLTransactionState 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
138SQLTransactionState 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
161SQLTransactionState 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
186SQLTransactionState 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
208SQLTransactionState 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
218SQLTransactionState 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.
235SQLTransactionState SQLTransaction::unreachableState()
236{
237    ASSERT_NOT_REACHED();
238    return SQLTransactionState::End;
239}
240
241SQLTransactionState SQLTransaction::sendToBackendState()
242{
243    ASSERT(m_nextState != SQLTransactionState::Idle);
244    m_backend->requestTransitToState(m_nextState);
245    return SQLTransactionState::Idle;
246}
247
248void SQLTransaction::performPendingCallback()
249{
250    computeNextStateAndCleanupIfNeeded();
251    runStateMachine();
252}
253
254void 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
271bool 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
294void 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
302PassOwnPtr<SQLTransactionErrorCallback> SQLTransaction::releaseErrorCallback()
303{
304    return m_errorCallbackWrapper.unwrap();
305}
306
307} // namespace WebCore
308