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 36namespace WebCore { 37 38PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(DOMStringList* objectStores, unsigned short mode, IDBDatabaseBackendImpl* database) 39{ 40 return adoptRef(new IDBTransactionBackendImpl(objectStores, mode, database)); 41} 42 43IDBTransactionBackendImpl::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 57IDBTransactionBackendImpl::~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 63PassRefPtr<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 88bool 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 103void 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 131void 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 144void IDBTransactionBackendImpl::run() 145{ 146 ASSERT(m_state == StartPending || m_state == Running); 147 ASSERT(!m_taskTimer.isActive()); 148 149 m_taskTimer.startOneShot(0); 150} 151 152void IDBTransactionBackendImpl::start() 153{ 154 ASSERT(m_state == Unused); 155 156 m_state = StartPending; 157 m_database->transactionCoordinator()->didStartTransaction(this); 158} 159 160void 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 175void 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 195void 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