IDB: indexeddb/mozilla/add-twice-failure.html fails
[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(Unopened)
57     , m_commitPending(false)
58     , m_callbacks(callbacks)
59     , m_database(databaseBackend)
60     , m_taskTimer(this, &IDBTransactionBackend::taskTimerFired)
61     , m_pendingPreemptiveEvents(0)
62     , m_id(id)
63 {
64     // We pass a reference of this object before it can be adopted.
65     relaxAdoptionRequirement();
66
67     m_database->transactionCoordinator()->didCreateTransaction(this);
68
69     RefPtr<IDBTransactionBackend> backend(this);
70     m_database->serverConnection().openTransaction(id, objectStoreIds, mode, [backend](bool success) {
71         if (!success) {
72             callOnMainThread([backend]() {
73                 backend->abort();
74             });
75             return;
76         }
77
78         backend->m_state = Unused;
79         if (backend->hasPendingTasks())
80             backend->start();
81     });
82 }
83
84 IDBTransactionBackend::~IDBTransactionBackend()
85 {
86     // It shouldn't be possible for this object to get deleted until it's either complete or aborted.
87     ASSERT(m_state == Finished);
88 }
89
90 void IDBTransactionBackend::scheduleTask(IDBDatabaseBackend::TaskType type, PassRefPtr<IDBOperation> task, PassRefPtr<IDBSynchronousOperation> abortTask)
91 {
92     if (m_state == Finished)
93         return;
94
95     if (type == IDBDatabaseBackend::NormalTask)
96         m_taskQueue.append(task);
97     else
98         m_preemptiveTaskQueue.append(task);
99
100     if (abortTask)
101         m_abortTaskQueue.prepend(abortTask);
102
103     if (m_state == Unopened)
104         return;
105
106     if (m_state == Unused)
107         start();
108     else if (m_state == Running && !m_taskTimer.isActive())
109         m_taskTimer.startOneShot(0);
110 }
111
112 void IDBTransactionBackend::abort()
113 {
114     abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error (unknown cause)"));
115 }
116
117 void IDBTransactionBackend::abort(PassRefPtr<IDBDatabaseError> error)
118 {
119 #ifndef NDEBUG
120     if (error)
121         LOG(StorageAPI, "IDBTransactionBackend::abort - (%s) %s", error->name().utf8().data(), error->message().utf8().data());
122     else
123         LOG(StorageAPI, "IDBTransactionBackend::abort (no error)");
124 #endif
125
126     if (m_state == Finished)
127         return;
128
129     bool wasRunning = m_state == Running;
130
131     // The last reference to this object may be released while performing the
132     // abort steps below. We therefore take a self reference to keep ourselves
133     // alive while executing this method.
134     Ref<IDBTransactionBackend> protect(*this);
135
136     m_state = Finished;
137     m_taskTimer.stop();
138
139     if (wasRunning)
140         m_database->serverConnection().rollbackTransaction(m_id, []() { });
141
142     // Run the abort tasks, if any.
143     while (!m_abortTaskQueue.isEmpty()) {
144         RefPtr<IDBSynchronousOperation> task(m_abortTaskQueue.takeFirst());
145         task->perform();
146     }
147
148     // Backing store resources (held via cursors) must be released before script callbacks
149     // are fired, as the script callbacks may release references and allow the backing store
150     // itself to be released, and order is critical.
151     closeOpenCursors();
152
153     m_database->serverConnection().resetTransaction(m_id, []() { });
154
155     // Transactions must also be marked as completed before the front-end is notified, as
156     // the transaction completion unblocks operations like closing connections.
157     m_database->transactionCoordinator()->didFinishTransaction(this);
158     ASSERT(!m_database->transactionCoordinator()->isActive(this));
159     m_database->transactionFinished(this);
160
161     if (m_callbacks)
162         m_callbacks->onAbort(id(), error);
163
164     m_database->transactionFinishedAndAbortFired(this);
165
166     m_database = 0;
167 }
168
169 bool IDBTransactionBackend::isTaskQueueEmpty() const
170 {
171     return m_preemptiveTaskQueue.isEmpty() && m_taskQueue.isEmpty();
172 }
173
174 bool IDBTransactionBackend::hasPendingTasks() const
175 {
176     return m_pendingPreemptiveEvents || !isTaskQueueEmpty();
177 }
178
179 void IDBTransactionBackend::registerOpenCursor(IDBCursorBackend* cursor)
180 {
181     m_openCursors.add(cursor);
182 }
183
184 void IDBTransactionBackend::unregisterOpenCursor(IDBCursorBackend* cursor)
185 {
186     m_openCursors.remove(cursor);
187 }
188
189 void IDBTransactionBackend::run()
190 {
191     // TransactionCoordinator has started this transaction. Schedule a timer
192     // to process the first task.
193     ASSERT(m_state == StartPending || m_state == Running);
194     ASSERT(!m_taskTimer.isActive());
195
196     m_taskTimer.startOneShot(0);
197 }
198
199 void IDBTransactionBackend::start()
200 {
201     ASSERT(m_state == Unused);
202
203     m_state = StartPending;
204     m_database->transactionCoordinator()->didStartTransaction(this);
205     m_database->transactionStarted(this);
206 }
207
208 void IDBTransactionBackend::commit()
209 {
210     LOG(StorageAPI, "IDBTransactionBackend::commit (Transaction %lli)", static_cast<long long>(m_id));
211
212     // In multiprocess ports, front-end may have requested a commit but an abort has already
213     // been initiated asynchronously by the back-end.
214     if (m_state == Finished)
215         return;
216
217     ASSERT(m_state == Unused || m_state == Running);
218     m_commitPending = true;
219
220     // Front-end has requested a commit, but there may be tasks like createIndex which
221     // are considered synchronous by the front-end but are processed asynchronously.
222     if (hasPendingTasks()) {
223         LOG(StorageAPI, "IDBTransactionBackend::commit - Not committing now, transaction still has pending tasks (Transaction %lli)", static_cast<long long>(m_id));
224         return;
225     }
226
227     // The last reference to this object may be released while performing the
228     // commit steps below. We therefore take a self reference to keep ourselves
229     // alive while executing this method.
230     RefPtr<IDBTransactionBackend> backend(this);
231
232     bool unused = m_state == Unused;
233     m_state = Finished;
234
235     bool committed = unused;
236
237     m_database->serverConnection().commitTransaction(m_id, [backend, this, committed, unused](bool success) mutable {
238         committed |= success;
239
240         // Backing store resources (held via cursors) must be released before script callbacks
241         // are fired, as the script callbacks may release references and allow the backing store
242         // itself to be released, and order is critical.
243         closeOpenCursors();
244
245         m_database->serverConnection().resetTransaction(m_id, []() { });
246
247         // Transactions must also be marked as completed before the front-end is notified, as
248         // the transaction completion unblocks operations like closing connections.
249         if (!unused)
250             m_database->transactionCoordinator()->didFinishTransaction(this);
251         m_database->transactionFinished(this);
252
253         if (committed) {
254             m_callbacks->onComplete(id());
255             m_database->transactionFinishedAndCompleteFired(this);
256         } else {
257             m_callbacks->onAbort(id(), IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error committing transaction."));
258             m_database->transactionFinishedAndAbortFired(this);
259         }
260
261         m_database = 0;
262     });
263 }
264
265 void IDBTransactionBackend::taskTimerFired(Timer<IDBTransactionBackend>&)
266 {
267     LOG(StorageAPI, "IDBTransactionBackend::taskTimerFired");
268
269     if (m_state == StartPending) {
270         m_database->serverConnection().beginTransaction(m_id, []() { });
271         m_state = Running;
272     }
273
274     // The last reference to this object may be released while performing a task.
275     // Take a self reference to keep this object alive so that tasks can
276     // successfully make their completion callbacks.
277     RefPtr<IDBTransactionBackend> self(this);
278
279     TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
280     if (!taskQueue->isEmpty() && m_state != Finished) {
281         ASSERT(m_state == Running);
282         RefPtr<IDBOperation> task(taskQueue->takeFirst());
283         task->perform([self, this, task]() {
284             m_taskTimer.startOneShot(0);
285         });
286
287         return;
288     }
289
290     // If there are no pending tasks, we haven't already committed/aborted,
291     // and the front-end requested a commit, it is now safe to do so.
292     if (!hasPendingTasks() && m_state != Finished && m_commitPending)
293         commit();
294 }
295
296 void IDBTransactionBackend::closeOpenCursors()
297 {
298     for (HashSet<IDBCursorBackend*>::iterator i = m_openCursors.begin(); i != m_openCursors.end(); ++i)
299         (*i)->close();
300     m_openCursors.clear();
301 }
302
303 void IDBTransactionBackend::scheduleCreateObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
304 {
305     scheduleTask(CreateObjectStoreOperation::create(this, objectStoreMetadata), CreateObjectStoreAbortOperation::create(this, objectStoreMetadata.id));
306 }
307
308 void IDBTransactionBackend::scheduleDeleteObjectStoreOperation(const IDBObjectStoreMetadata& objectStoreMetadata)
309 {
310     scheduleTask(DeleteObjectStoreOperation::create(this, objectStoreMetadata), DeleteObjectStoreAbortOperation::create(this, objectStoreMetadata));
311 }
312
313 void IDBTransactionBackend::scheduleVersionChangeOperation(int64_t requestedVersion, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, const IDBDatabaseMetadata& metadata)
314 {
315     scheduleTask(IDBDatabaseBackend::VersionChangeOperation::create(this, requestedVersion, callbacks, databaseCallbacks), IDBDatabaseBackend::VersionChangeAbortOperation::create(this, String::number(metadata.version), metadata.version));
316 }
317
318 void IDBTransactionBackend::scheduleCreateIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
319 {
320     scheduleTask(CreateIndexOperation::create(this, objectStoreId, indexMetadata), CreateIndexAbortOperation::create(this, objectStoreId, indexMetadata.id));
321 }
322
323 void IDBTransactionBackend::scheduleDeleteIndexOperation(int64_t objectStoreId, const IDBIndexMetadata& indexMetadata)
324 {
325     scheduleTask(DeleteIndexOperation::create(this, objectStoreId, indexMetadata), DeleteIndexAbortOperation::create(this, objectStoreId, indexMetadata));
326 }
327
328 void IDBTransactionBackend::scheduleGetOperation(const IDBDatabaseMetadata& metadata, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorType cursorType, PassRefPtr<IDBCallbacks> callbacks)
329 {
330     scheduleTask(GetOperation::create(this, metadata, objectStoreId, indexId, keyRange, cursorType, callbacks));
331 }
332
333 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)
334 {
335     scheduleTask(PutOperation::create(this, objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys));
336 }
337
338 void IDBTransactionBackend::scheduleSetIndexesReadyOperation(size_t indexCount)
339 {
340     scheduleTask(IDBDatabaseBackend::PreemptiveTask, SetIndexesReadyOperation::create(this, indexCount));
341 }
342
343 void IDBTransactionBackend::scheduleOpenCursorOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, IndexedDB::CursorType cursorType, IDBDatabaseBackend::TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
344 {
345     scheduleTask(OpenCursorOperation::create(this, objectStoreId, indexId, keyRange, direction, cursorType, taskType, callbacks));
346 }
347
348 void IDBTransactionBackend::scheduleCountOperation(int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
349 {
350     scheduleTask(CountOperation::create(this, objectStoreId, indexId, keyRange, callbacks));
351 }
352
353 void IDBTransactionBackend::scheduleDeleteRangeOperation(int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
354 {
355     scheduleTask(DeleteRangeOperation::create(this, objectStoreId, keyRange, callbacks));
356 }
357
358 void IDBTransactionBackend::scheduleClearObjectStoreOperation(int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
359 {
360     scheduleTask(ClearObjectStoreOperation::create(this, objectStoreId, callbacks));
361 }
362
363 };
364
365 #endif // ENABLE(INDEXED_DATABASE)