53bbbea97c2ddcb4ba82d60bb4c2464f79479d57
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBTransactionBackend.cpp
1 /*
2  * Copyright (C) 2010 Google 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  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "IDBTransactionBackend.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "IDBCursorBackend.h"
32 #include "IDBDatabaseBackend.h"
33 #include "IDBDatabaseCallbacks.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBFactoryBackendInterface.h"
36 #include "IDBKeyRange.h"
37 #include "IDBServerConnection.h"
38 #include "IDBTransactionBackendOperations.h"
39 #include "IDBTransactionCoordinator.h"
40 #include "Logging.h"
41
42 namespace WebCore {
43
44 PassRefPtr<IDBTransactionBackend> IDBTransactionBackend::create(IDBDatabaseBackend* databaseBackend, int64_t id, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, IndexedDB::TransactionMode mode)
45 {
46     HashSet<int64_t> objectStoreHashSet;
47     for (size_t i = 0; i < objectStoreIds.size(); ++i)
48         objectStoreHashSet.add(objectStoreIds[i]);
49
50     return adoptRef(new IDBTransactionBackend(databaseBackend, id, callbacks, objectStoreHashSet, mode));
51 }
52
53 IDBTransactionBackend::IDBTransactionBackend(IDBDatabaseBackend* databaseBackend, int64_t id, PassRefPtr<IDBDatabaseCallbacks> callbacks, const HashSet<int64_t>& objectStoreIds, IndexedDB::TransactionMode mode)
54     : m_objectStoreIds(objectStoreIds)
55     , m_mode(mode)
56     , m_state(Unused)
57     , m_commitPending(false)
58     , m_callbacks(callbacks)
59     , m_database(databaseBackend)
60     , m_backingStoreTransaction(m_database->serverConnection().deprecatedBackingStore()->createBackingStoreTransaction())
61     , m_taskTimer(this, &IDBTransactionBackend::taskTimerFired)
62     , m_pendingPreemptiveEvents(0)
63     , m_id(id)
64 {
65     // We pass a reference of this object before it can be adopted.
66     relaxAdoptionRequirement();
67
68     m_database->transactionCoordinator()->didCreateTransaction(this);
69 }
70
71 IDBTransactionBackend::~IDBTransactionBackend()
72 {
73     // It shouldn't be possible for this object to get deleted until it's either complete or aborted.
74     ASSERT(m_state == Finished);
75 }
76
77 void IDBTransactionBackend::scheduleTask(IDBDatabaseBackend::TaskType type, PassRefPtr<IDBOperation> task, PassRefPtr<IDBSynchronousOperation> abortTask)
78 {
79     if (m_state == Finished)
80         return;
81
82     if (type == IDBDatabaseBackend::NormalTask)
83         m_taskQueue.append(task);
84     else
85         m_preemptiveTaskQueue.append(task);
86
87     if (abortTask)
88         m_abortTaskQueue.prepend(abortTask);
89
90     if (m_state == Unused)
91         start();
92     else if (m_state == Running && !m_taskTimer.isActive())
93         m_taskTimer.startOneShot(0);
94 }
95
96 void IDBTransactionBackend::abort()
97 {
98     abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error (unknown cause)"));
99 }
100
101 void IDBTransactionBackend::abort(PassRefPtr<IDBDatabaseError> error)
102 {
103     LOG(StorageAPI, "IDBTransactionBackend::abort");
104     if (m_state == Finished)
105         return;
106
107     bool wasRunning = m_state == Running;
108
109     // The last reference to this object may be released while performing the
110     // abort steps below. We therefore take a self reference to keep ourselves
111     // alive while executing this method.
112     Ref<IDBTransactionBackend> protect(*this);
113
114     m_state = Finished;
115     m_taskTimer.stop();
116
117     if (wasRunning)
118         m_backingStoreTransaction->rollback();
119
120     // Run the abort tasks, if any.
121     while (!m_abortTaskQueue.isEmpty()) {
122         RefPtr<IDBSynchronousOperation> task(m_abortTaskQueue.takeFirst());
123         task->perform();
124     }
125
126     // Backing store resources (held via cursors) must be released before script callbacks
127     // are fired, as the script callbacks may release references and allow the backing store
128     // itself to be released, and order is critical.
129     closeOpenCursors();
130     m_backingStoreTransaction->resetTransaction();
131
132     // Transactions must also be marked as completed before the front-end is notified, as
133     // the transaction completion unblocks operations like closing connections.
134     m_database->transactionCoordinator()->didFinishTransaction(this);
135     ASSERT(!m_database->transactionCoordinator()->isActive(this));
136     m_database->transactionFinished(this);
137
138     if (m_callbacks)
139         m_callbacks->onAbort(id(), error);
140
141     m_database->transactionFinishedAndAbortFired(this);
142
143     m_database = 0;
144 }
145
146 bool IDBTransactionBackend::isTaskQueueEmpty() const
147 {
148     return m_preemptiveTaskQueue.isEmpty() && m_taskQueue.isEmpty();
149 }
150
151 bool IDBTransactionBackend::hasPendingTasks() const
152 {
153     return m_pendingPreemptiveEvents || !isTaskQueueEmpty();
154 }
155
156 void IDBTransactionBackend::registerOpenCursor(IDBCursorBackend* cursor)
157 {
158     m_openCursors.add(cursor);
159 }
160
161 void IDBTransactionBackend::unregisterOpenCursor(IDBCursorBackend* cursor)
162 {
163     m_openCursors.remove(cursor);
164 }
165
166 void IDBTransactionBackend::run()
167 {
168     // TransactionCoordinator has started this transaction. Schedule a timer
169     // to process the first task.
170     ASSERT(m_state == StartPending || m_state == Running);
171     ASSERT(!m_taskTimer.isActive());
172
173     m_taskTimer.startOneShot(0);
174 }
175
176 void IDBTransactionBackend::start()
177 {
178     ASSERT(m_state == Unused);
179
180     m_state = StartPending;
181     m_database->transactionCoordinator()->didStartTransaction(this);
182     m_database->transactionStarted(this);
183 }
184
185 void IDBTransactionBackend::commit()
186 {
187     LOG(StorageAPI, "IDBTransactionBackend::commit");
188
189     // In multiprocess ports, front-end may have requested a commit but an abort has already
190     // been initiated asynchronously by the back-end.
191     if (m_state == Finished)
192         return;
193
194     ASSERT(m_state == Unused || m_state == Running);
195     m_commitPending = true;
196
197     // Front-end has requested a commit, but there may be tasks like createIndex which
198     // are considered synchronous by the front-end but are processed asynchronously.
199     if (hasPendingTasks())
200         return;
201
202     // The last reference to this object may be released while performing the
203     // commit steps below. We therefore take a self reference to keep ourselves
204     // alive while executing this method.
205     Ref<IDBTransactionBackend> protect(*this);
206
207     bool unused = m_state == Unused;
208     m_state = Finished;
209
210     bool committed = unused || m_backingStoreTransaction->commit();
211
212     // Backing store resources (held via cursors) must be released before script callbacks
213     // are fired, as the script callbacks may release references and allow the backing store
214     // itself to be released, and order is critical.
215     closeOpenCursors();
216     m_backingStoreTransaction->resetTransaction();
217
218     // Transactions must also be marked as completed before the front-end is notified, as
219     // the transaction completion unblocks operations like closing connections.
220     if (!unused)
221         m_database->transactionCoordinator()->didFinishTransaction(this);
222     m_database->transactionFinished(this);
223
224     if (committed) {
225         m_callbacks->onComplete(id());
226         m_database->transactionFinishedAndCompleteFired(this);
227     } else {
228         m_callbacks->onAbort(id(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error committing transaction."));
229         m_database->transactionFinishedAndAbortFired(this);
230     }
231
232     m_database = 0;
233 }
234
235 void IDBTransactionBackend::taskTimerFired(Timer<IDBTransactionBackend>*)
236 {
237     LOG(StorageAPI, "IDBTransactionBackend::taskTimerFired");
238
239     if (m_state == StartPending) {
240         m_backingStoreTransaction->begin();
241         m_state = Running;
242     }
243
244     // The last reference to this object may be released while performing a task.
245     // Take a self reference to keep this object alive so that tasks can
246     // successfully make their completion callbacks.
247     RefPtr<IDBTransactionBackend> self(this);
248
249     TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
250     if (!taskQueue->isEmpty() && m_state != Finished) {
251         ASSERT(m_state == Running);
252         RefPtr<IDBOperation> task(taskQueue->takeFirst());
253         task->perform([self, this, task]() {
254             m_taskTimer.startOneShot(0);
255         });
256
257         return;
258     }
259
260     // If there are no pending tasks, we haven't already committed/aborted,
261     // and the front-end requested a commit, it is now safe to do so.
262     if (!hasPendingTasks() && m_state != Finished && m_commitPending)
263         commit();
264 }
265
266 void IDBTransactionBackend::closeOpenCursors()
267 {
268     for (HashSet<IDBCursorBackend*>::iterator i = m_openCursors.begin(); i != m_openCursors.end(); ++i)
269         (*i)->close();
270     m_openCursors.clear();
271 }
272
273 void IDBTransactionBackend::scheduleCreateObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
274 {
275     scheduleTask(CreateObjectStoreOperation::create(this, objectStoreMetadata), CreateObjectStoreAbortOperation::create(this, objectStoreMetadata.id));
276 }
277
278 void IDBTransactionBackend::scheduleDeleteObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
279 {
280     scheduleTask(DeleteObjectStoreOperation::create(this, objectStoreMetadata), DeleteObjectStoreAbortOperation::create(this, objectStoreMetadata));
281 }
282
283 void IDBTransactionBackend::scheduleVersionChangeOperation(int64_t transactionId, int64_t requestedVersion, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, const IDBDatabaseMetadata& metadata)
284 {
285     scheduleTask(IDBDatabaseBackend::VersionChangeOperation::create(this, transactionId, requestedVersion, callbacks, databaseCallbacks), IDBDatabaseBackend::VersionChangeAbortOperation::create(this, String::number(metadata.version), metadata.version));
286 }
287
288 void IDBTransactionBackend::scheduleCreateIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
289 {
290     scheduleTask(CreateIndexOperation::create(this, objectStoreId, indexMetadata), CreateIndexAbortOperation::create(this, objectStoreId, indexMetadata.id));
291 }
292
293 void IDBTransactionBackend::scheduleDeleteIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
294 {
295     scheduleTask(DeleteIndexOperation::create(this, objectStoreId, indexMetadata), DeleteIndexAbortOperation::create(this, objectStoreId, indexMetadata));
296 }
297
298 void IDBTransactionBackend::scheduleGetOperation(const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks)
299 {
300     scheduleTask(GetOperation::create(this, metadata, objectStoreId, indexId, keyRange, cursorType, callbacks));
301 }
302
303 void IDBTransactionBackend::schedulePutOperation(const IDBObjectStoreMetadata& objectStoreMetadata, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, IDBDatabaseBackend::PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
304 {
305     scheduleTask(PutOperation::create(this, database().id(), objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys));
306 }
307
308 void IDBTransactionBackend::scheduleSetIndexesReadyOperation(size_t indexCount)
309 {
310     scheduleTask(IDBDatabaseBackend::PreemptiveTask, SetIndexesReadyOperation::create(this, indexCount));
311 }
312
313 void IDBTransactionBackend::scheduleOpenCursorOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
314 {
315     scheduleTask(OpenCursorOperation::create(this, database().id(), objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks));
316 }
317
318 void IDBTransactionBackend::scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
319 {
320     scheduleTask(CountOperation::create(this, database().id(), objectStoreId, indexId, keyRange, callbacks));
321 }
322
323 void IDBTransactionBackend::scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
324 {
325     scheduleTask(DeleteRangeOperation::create(this, database().id(), objectStoreId, keyRange, callbacks));
326 }
327
328 void IDBTransactionBackend::scheduleClearOperation(int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
329 {
330     scheduleTask(ClearOperation::create(this, database().id(), objectStoreId, callbacks));
331 }
332
333 PassRefPtr<IDBCursorBackend> IDBTransactionBackend::createCursorBackend(IDBBackingStoreCursorInterface& cursor, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, int64_t objectStoreId)
334 {
335     return m_database->factoryBackend().createCursorBackend(*this, cursor, cursorType, taskType, objectStoreId);
336 }
337
338 };
339
340 #endif // ENABLE(INDEXED_DATABASE)