Home | History | Annotate | Download | only in storage
      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 
     38 PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(DOMStringList* objectStores, unsigned short mode, IDBDatabaseBackendImpl* database)
     39 {
     40     return adoptRef(new IDBTransactionBackendImpl(objectStores, mode, database));
     41 }
     42 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    152 void IDBTransactionBackendImpl::start()
    153 {
    154     ASSERT(m_state == Unused);
    155 
    156     m_state = StartPending;
    157     m_database->transactionCoordinator()->didStartTransaction(this);
    158 }
    159 
    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 
    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 
    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