2 * Copyright (C) 2007, 2008, 2013 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #include "modules/webdatabase/SQLTransaction.h"
32 #include "bindings/core/v8/ExceptionState.h"
33 #include "core/dom/ExceptionCode.h"
34 #include "core/html/VoidCallback.h"
35 #include "core/inspector/InspectorInstrumentation.h"
36 #include "modules/webdatabase/Database.h"
37 #include "modules/webdatabase/DatabaseAuthorizer.h"
38 #include "modules/webdatabase/DatabaseContext.h"
39 #include "modules/webdatabase/SQLError.h"
40 #include "modules/webdatabase/SQLStatementCallback.h"
41 #include "modules/webdatabase/SQLStatementErrorCallback.h"
42 #include "modules/webdatabase/SQLTransactionBackend.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 "platform/Logging.h"
47 #include "wtf/StdLibExtras.h"
48 #include "wtf/Vector.h"
52 SQLTransaction* SQLTransaction::create(Database* db, SQLTransactionCallback* callback,
53 VoidCallback* successCallback, SQLTransactionErrorCallback* errorCallback, bool readOnly)
55 return new SQLTransaction(db, callback, successCallback, errorCallback, readOnly);
58 SQLTransaction::SQLTransaction(Database* db, SQLTransactionCallback* callback,
59 VoidCallback* successCallback, SQLTransactionErrorCallback* errorCallback,
62 , m_callback(callback)
63 , m_successCallback(successCallback)
64 , m_errorCallback(errorCallback)
65 , m_executeSqlAllowed(false)
66 , m_readOnly(readOnly)
69 m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(db->executionContext(), "SQLTransaction");
72 SQLTransaction::~SQLTransaction()
76 void SQLTransaction::trace(Visitor* visitor)
78 visitor->trace(m_database);
79 visitor->trace(m_backend);
80 visitor->trace(m_callback);
81 visitor->trace(m_successCallback);
82 visitor->trace(m_errorCallback);
85 bool SQLTransaction::hasCallback() const
90 bool SQLTransaction::hasSuccessCallback() const
92 return m_successCallback;
95 bool SQLTransaction::hasErrorCallback() const
97 return m_errorCallback;
100 void SQLTransaction::setBackend(SQLTransactionBackend* backend)
106 SQLTransaction::StateFunction SQLTransaction::stateFunctionFor(SQLTransactionState state)
108 static const StateFunction stateFunctions[] = {
109 &SQLTransaction::unreachableState, // 0. illegal
110 &SQLTransaction::unreachableState, // 1. idle
111 &SQLTransaction::unreachableState, // 2. acquireLock
112 &SQLTransaction::unreachableState, // 3. openTransactionAndPreflight
113 &SQLTransaction::sendToBackendState, // 4. runStatements
114 &SQLTransaction::unreachableState, // 5. postflightAndCommit
115 &SQLTransaction::sendToBackendState, // 6. cleanupAndTerminate
116 &SQLTransaction::sendToBackendState, // 7. cleanupAfterTransactionErrorCallback
117 &SQLTransaction::deliverTransactionCallback, // 8.
118 &SQLTransaction::deliverTransactionErrorCallback, // 9.
119 &SQLTransaction::deliverStatementCallback, // 10.
120 &SQLTransaction::deliverQuotaIncreaseCallback, // 11.
121 &SQLTransaction::deliverSuccessCallback // 12.
124 ASSERT(WTF_ARRAY_LENGTH(stateFunctions) == static_cast<int>(SQLTransactionState::NumberOfStates));
125 ASSERT(state < SQLTransactionState::NumberOfStates);
127 return stateFunctions[static_cast<int>(state)];
130 // requestTransitToState() can be called from the backend. Hence, it should
131 // NOT be modifying SQLTransactionBackend in general. The only safe field to
132 // modify is m_requestedState which is meant for this purpose.
133 void SQLTransaction::requestTransitToState(SQLTransactionState nextState)
135 WTF_LOG(StorageAPI, "Scheduling %s for transaction %p\n", nameForSQLTransactionState(nextState), this);
136 m_requestedState = nextState;
137 m_database->scheduleTransactionCallback(this);
140 SQLTransactionState SQLTransaction::nextStateForTransactionError()
142 ASSERT(m_transactionError);
143 if (hasErrorCallback())
144 return SQLTransactionState::DeliverTransactionErrorCallback;
146 // No error callback, so fast-forward to:
147 // Transaction Step 11 - Rollback the transaction.
148 return SQLTransactionState::CleanupAfterTransactionErrorCallback;
151 SQLTransactionState SQLTransaction::deliverTransactionCallback()
153 bool shouldDeliverErrorCallback = false;
155 // Spec 4.3.2 4: Invoke the transaction callback with the new SQLTransaction object
156 if (SQLTransactionCallback* callback = m_callback.release()) {
157 m_executeSqlAllowed = true;
158 InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(m_database->executionContext(), m_asyncOperationId);
159 shouldDeliverErrorCallback = !callback->handleEvent(this);
160 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
161 m_executeSqlAllowed = false;
164 // Spec 4.3.2 5: If the transaction callback was null or raised an exception, jump to the error callback
165 SQLTransactionState nextState = SQLTransactionState::RunStatements;
166 if (shouldDeliverErrorCallback) {
167 m_database->reportStartTransactionResult(5, SQLError::UNKNOWN_ERR, 0);
168 m_transactionError = SQLErrorData::create(SQLError::UNKNOWN_ERR, "the SQLTransactionCallback was null or threw an exception");
169 nextState = SQLTransactionState::DeliverTransactionErrorCallback;
171 m_database->reportStartTransactionResult(0, -1, 0); // OK
175 SQLTransactionState SQLTransaction::deliverTransactionErrorCallback()
177 InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncOperationCompletedCallbackStarting(m_database->executionContext(), m_asyncOperationId);
179 // Spec 4.3.2.10: If exists, invoke error callback with the last
180 // error to have occurred in this transaction.
181 if (SQLTransactionErrorCallback* errorCallback = m_errorCallback.release()) {
182 // If we get here with an empty m_transactionError, then the backend
183 // must be waiting in the idle state waiting for this state to finish.
184 // Hence, it's thread safe to fetch the backend transactionError without
186 if (!m_transactionError) {
187 ASSERT(m_backend->transactionError());
188 m_transactionError = SQLErrorData::create(*m_backend->transactionError());
190 ASSERT(m_transactionError);
191 errorCallback->handleEvent(SQLError::create(*m_transactionError));
193 m_transactionError = nullptr;
196 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
199 // Spec 4.3.2.10: Rollback the transaction.
200 return SQLTransactionState::CleanupAfterTransactionErrorCallback;
203 SQLTransactionState SQLTransaction::deliverStatementCallback()
205 // Spec 4.3.2.6.6 and 4.3.2.6.3: If the statement callback went wrong, jump to the transaction error callback
206 // Otherwise, continue to loop through the statement queue
207 m_executeSqlAllowed = true;
209 SQLStatement* currentStatement = m_backend->currentStatement();
210 ASSERT(currentStatement);
212 bool result = currentStatement->performCallback(this);
214 m_executeSqlAllowed = false;
217 m_database->reportCommitTransactionResult(2, SQLError::UNKNOWN_ERR, 0);
218 m_transactionError = SQLErrorData::create(SQLError::UNKNOWN_ERR, "the statement callback raised an exception or statement error callback did not return false");
219 return nextStateForTransactionError();
221 return SQLTransactionState::RunStatements;
224 SQLTransactionState SQLTransaction::deliverQuotaIncreaseCallback()
226 ASSERT(m_backend->currentStatement());
228 bool shouldRetryCurrentStatement = m_database->transactionClient()->didExceedQuota(database());
229 m_backend->setShouldRetryCurrentStatement(shouldRetryCurrentStatement);
231 return SQLTransactionState::RunStatements;
234 SQLTransactionState SQLTransaction::deliverSuccessCallback()
236 InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncOperationCompletedCallbackStarting(m_database->executionContext(), m_asyncOperationId);
238 // Spec 4.3.2.8: Deliver success callback.
239 if (VoidCallback* successCallback = m_successCallback.release())
240 successCallback->handleEvent();
242 InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
245 // Schedule a "post-success callback" step to return control to the database thread in case there
246 // are further transactions queued up for this Database
247 return SQLTransactionState::CleanupAndTerminate;
250 // This state function is used as a stub function to plug unimplemented states
251 // in the state dispatch table. They are unimplemented because they should
252 // never be reached in the course of correct execution.
253 SQLTransactionState SQLTransaction::unreachableState()
255 ASSERT_NOT_REACHED();
256 return SQLTransactionState::End;
259 SQLTransactionState SQLTransaction::sendToBackendState()
261 ASSERT(m_nextState != SQLTransactionState::Idle);
262 m_backend->requestTransitToState(m_nextState);
263 return SQLTransactionState::Idle;
266 void SQLTransaction::performPendingCallback()
268 computeNextStateAndCleanupIfNeeded();
272 void SQLTransaction::executeSQL(const String& sqlStatement, const Vector<SQLValue>& arguments, SQLStatementCallback* callback, SQLStatementErrorCallback* callbackError, ExceptionState& exceptionState)
274 if (!m_executeSqlAllowed) {
275 exceptionState.throwDOMException(InvalidStateError, "SQL execution is disallowed.");
279 if (!m_database->opened()) {
280 exceptionState.throwDOMException(InvalidStateError, "The database has not been opened.");
284 int permissions = DatabaseAuthorizer::ReadWriteMask;
285 if (!m_database->databaseContext()->allowDatabaseAccess())
286 permissions |= DatabaseAuthorizer::NoAccessMask;
288 permissions |= DatabaseAuthorizer::ReadOnlyMask;
290 SQLStatement* statement = SQLStatement::create(m_database.get(), callback, callbackError);
291 m_backend->executeSQL(statement, sqlStatement, arguments, permissions);
294 bool SQLTransaction::computeNextStateAndCleanupIfNeeded()
296 // Only honor the requested state transition if we're not supposed to be
297 // cleaning up and shutting down:
298 if (m_database->opened()) {
299 setStateToRequestedState();
300 ASSERT(m_nextState == SQLTransactionState::End
301 || m_nextState == SQLTransactionState::DeliverTransactionCallback
302 || m_nextState == SQLTransactionState::DeliverTransactionErrorCallback
303 || m_nextState == SQLTransactionState::DeliverStatementCallback
304 || m_nextState == SQLTransactionState::DeliverQuotaIncreaseCallback
305 || m_nextState == SQLTransactionState::DeliverSuccessCallback);
307 WTF_LOG(StorageAPI, "Callback %s\n", nameForSQLTransactionState(m_nextState));
312 m_nextState = SQLTransactionState::CleanupAndTerminate;
317 void SQLTransaction::clearCallbacks()
320 m_successCallback.clear();
321 m_errorCallback.clear();
324 SQLTransactionErrorCallback* SQLTransaction::releaseErrorCallback()
326 return m_errorCallback.release();