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
create(Database * db,PassOwnPtr<SQLTransactionCallback> callback,PassOwnPtr<VoidCallback> successCallback,PassOwnPtr<SQLTransactionErrorCallback> errorCallback,bool readOnly)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
SQLTransaction(Database * db,PassOwnPtr<SQLTransactionCallback> callback,PassOwnPtr<VoidCallback> successCallback,PassOwnPtr<SQLTransactionErrorCallback> errorCallback,bool readOnly)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
hasCallback() const72 bool SQLTransaction::hasCallback() const
73 {
74 return m_callbackWrapper.hasCallback();
75 }
76
hasSuccessCallback() const77 bool SQLTransaction::hasSuccessCallback() const
78 {
79 return m_successCallbackWrapper.hasCallback();
80 }
81
hasErrorCallback() const82 bool SQLTransaction::hasErrorCallback() const
83 {
84 return m_errorCallbackWrapper.hasCallback();
85 }
86
setBackend(AbstractSQLTransactionBackend * backend)87 void SQLTransaction::setBackend(AbstractSQLTransactionBackend* backend)
88 {
89 ASSERT(!m_backend);
90 m_backend = backend;
91 }
92
stateFunctionFor(SQLTransactionState state)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.
requestTransitToState(SQLTransactionState nextState)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
nextStateForTransactionError()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
deliverTransactionCallback()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
deliverTransactionErrorCallback()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
deliverStatementCallback()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
deliverQuotaIncreaseCallback()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
deliverSuccessCallback()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.
unreachableState()235 SQLTransactionState SQLTransaction::unreachableState()
236 {
237 ASSERT_NOT_REACHED();
238 return SQLTransactionState::End;
239 }
240
sendToBackendState()241 SQLTransactionState SQLTransaction::sendToBackendState()
242 {
243 ASSERT(m_nextState != SQLTransactionState::Idle);
244 m_backend->requestTransitToState(m_nextState);
245 return SQLTransactionState::Idle;
246 }
247
performPendingCallback()248 void SQLTransaction::performPendingCallback()
249 {
250 computeNextStateAndCleanupIfNeeded();
251 runStateMachine();
252 }
253
executeSQL(const String & sqlStatement,const Vector<SQLValue> & arguments,PassOwnPtr<SQLStatementCallback> callback,PassOwnPtr<SQLStatementErrorCallback> callbackError,ExceptionState & exceptionState)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
computeNextStateAndCleanupIfNeeded()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
clearCallbackWrappers()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
releaseErrorCallback()302 PassOwnPtr<SQLTransactionErrorCallback> SQLTransaction::releaseErrorCallback()
303 {
304 return m_errorCallbackWrapper.unwrap();
305 }
306
307 } // namespace WebCore
308