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 "IDBTransactionBackendImpl.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "IDBBackingStore.h"
32 #include "IDBDatabaseBackendImpl.h"
33 #include "IDBDatabaseException.h"
34 #include "IDBTransactionCoordinator.h"
35
36 namespace WebCore {
37
create(DOMStringList * objectStores,unsigned short mode,IDBDatabaseBackendImpl * database)38 PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(DOMStringList* objectStores, unsigned short mode, IDBDatabaseBackendImpl* database)
39 {
40 return adoptRef(new IDBTransactionBackendImpl(objectStores, mode, database));
41 }
42
IDBTransactionBackendImpl(DOMStringList * objectStores,unsigned short mode,IDBDatabaseBackendImpl * database)43 IDBTransactionBackendImpl::IDBTransactionBackendImpl(DOMStringList* objectStores, unsigned short mode, IDBDatabaseBackendImpl* database)
44 : m_objectStoreNames(objectStores)
45 , m_mode(mode)
46 , m_state(Unused)
47 , m_database(database)
48 , m_transaction(database->backingStore()->createTransaction())
49 , m_taskTimer(this, &IDBTransactionBackendImpl::taskTimerFired)
50 , m_taskEventTimer(this, &IDBTransactionBackendImpl::taskEventTimerFired)
51 , m_pendingEvents(0)
52 {
53 ASSERT(m_objectStoreNames);
54 m_database->transactionCoordinator()->didCreateTransaction(this);
55 }
56
~IDBTransactionBackendImpl()57 IDBTransactionBackendImpl::~IDBTransactionBackendImpl()
58 {
59 // It shouldn't be possible for this object to get deleted until it's either complete or aborted.
60 ASSERT(m_state == Finished);
61 }
62
objectStore(const String & name,ExceptionCode & ec)63 PassRefPtr<IDBObjectStoreBackendInterface> IDBTransactionBackendImpl::objectStore(const String& name, ExceptionCode& ec)
64 {
65 if (m_state == Finished) {
66 ec = IDBDatabaseException::NOT_ALLOWED_ERR;
67 return 0;
68 }
69
70 // Does a linear search, but it really shouldn't be that slow in practice.
71 if (!m_objectStoreNames->isEmpty() && !m_objectStoreNames->contains(name)) {
72 ec = IDBDatabaseException::NOT_FOUND_ERR;
73 return 0;
74 }
75
76 RefPtr<IDBObjectStoreBackendInterface> objectStore = m_database->objectStore(name);
77 // FIXME: This is only necessary right now beacuse a setVersion transaction could modify things
78 // between its creation (where another check occurs) and the .objectStore call.
79 // There's a bug to make this impossible in the spec. When we make it impossible here, we
80 // can remove this check.
81 if (!objectStore) {
82 ec = IDBDatabaseException::NOT_FOUND_ERR;
83 return 0;
84 }
85 return objectStore.release();
86 }
87
scheduleTask(PassOwnPtr<ScriptExecutionContext::Task> task,PassOwnPtr<ScriptExecutionContext::Task> abortTask)88 bool IDBTransactionBackendImpl::scheduleTask(PassOwnPtr<ScriptExecutionContext::Task> task, PassOwnPtr<ScriptExecutionContext::Task> abortTask)
89 {
90 if (m_state == Finished)
91 return false;
92
93 m_taskQueue.append(task);
94 if (abortTask)
95 m_abortTaskQueue.prepend(abortTask);
96
97 if (m_state == Unused)
98 start();
99
100 return true;
101 }
102
abort()103 void IDBTransactionBackendImpl::abort()
104 {
105 if (m_state == Finished)
106 return;
107
108 // The last reference to this object may be released while performing the
109 // abort steps below. We therefore take a self reference to keep ourselves
110 // alive while executing this method.
111 RefPtr<IDBTransactionBackendImpl> self(this);
112
113 m_state = Finished;
114 m_taskTimer.stop();
115 m_taskEventTimer.stop();
116 m_transaction->rollback();
117
118 // Run the abort tasks, if any.
119 while (!m_abortTaskQueue.isEmpty()) {
120 OwnPtr<ScriptExecutionContext::Task> task(m_abortTaskQueue.first().release());
121 m_abortTaskQueue.removeFirst();
122 task->performTask(0);
123 }
124
125 m_callbacks->onAbort();
126 m_database->transactionCoordinator()->didFinishTransaction(this);
127 ASSERT(!m_database->transactionCoordinator()->isActive(this));
128 m_database = 0;
129 }
130
didCompleteTaskEvents()131 void IDBTransactionBackendImpl::didCompleteTaskEvents()
132 {
133 if (m_state == Finished)
134 return;
135
136 ASSERT(m_state == Running);
137 ASSERT(m_pendingEvents);
138 m_pendingEvents--;
139
140 if (!m_taskEventTimer.isActive())
141 m_taskEventTimer.startOneShot(0);
142 }
143
run()144 void IDBTransactionBackendImpl::run()
145 {
146 ASSERT(m_state == StartPending || m_state == Running);
147 ASSERT(!m_taskTimer.isActive());
148
149 m_taskTimer.startOneShot(0);
150 }
151
start()152 void IDBTransactionBackendImpl::start()
153 {
154 ASSERT(m_state == Unused);
155
156 m_state = StartPending;
157 m_database->transactionCoordinator()->didStartTransaction(this);
158 }
159
commit()160 void IDBTransactionBackendImpl::commit()
161 {
162 // The last reference to this object may be released while performing the
163 // commit steps below. We therefore take a self reference to keep ourselves
164 // alive while executing this method.
165 RefPtr<IDBTransactionBackendImpl> self(this);
166 ASSERT(m_state == Running);
167
168 m_state = Finished;
169 m_transaction->commit();
170 m_callbacks->onComplete();
171 m_database->transactionCoordinator()->didFinishTransaction(this);
172 m_database = 0;
173 }
174
taskTimerFired(Timer<IDBTransactionBackendImpl> *)175 void IDBTransactionBackendImpl::taskTimerFired(Timer<IDBTransactionBackendImpl>*)
176 {
177 ASSERT(!m_taskQueue.isEmpty());
178
179 if (m_state == StartPending) {
180 m_transaction->begin();
181 m_state = Running;
182 }
183
184 TaskQueue queue;
185 queue.swap(m_taskQueue);
186 while (!queue.isEmpty() && m_state != Finished) {
187 ASSERT(m_state == Running);
188 OwnPtr<ScriptExecutionContext::Task> task(queue.first().release());
189 queue.removeFirst();
190 m_pendingEvents++;
191 task->performTask(0);
192 }
193 }
194
taskEventTimerFired(Timer<IDBTransactionBackendImpl> *)195 void IDBTransactionBackendImpl::taskEventTimerFired(Timer<IDBTransactionBackendImpl>*)
196 {
197 ASSERT(m_state == Running);
198
199 if (!m_pendingEvents && m_taskQueue.isEmpty()) {
200 // The last task event has completed and the task
201 // queue is empty. Commit the transaction.
202 commit();
203 return;
204 }
205
206 // We are still waiting for other events to complete. However,
207 // the task queue is non-empty and the timer is inactive.
208 // We can therfore schedule the timer again.
209 if (!m_taskQueue.isEmpty() && !m_taskTimer.isActive())
210 m_taskTimer.startOneShot(0);
211 }
212
213 };
214
215 #endif // ENABLE(INDEXED_DATABASE)
216