• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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