~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/Modules/indexeddb/IDBTransactionBackendImpl.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "IDBCursorBackendImpl.h"
 
33
#include "IDBDatabaseBackendImpl.h"
 
34
#include "IDBDatabaseException.h"
 
35
#include "IDBObjectStoreBackendImpl.h"
 
36
#include "IDBTracing.h"
 
37
#include "IDBTransactionCoordinator.h"
 
38
#include "ScriptExecutionContext.h"
 
39
 
 
40
namespace WebCore {
 
41
 
 
42
PassRefPtr<IDBTransactionBackendImpl> IDBTransactionBackendImpl::create(int64_t id, const Vector<int64_t>& objectStoreIds, unsigned short mode, IDBDatabaseBackendImpl* database)
 
43
{
 
44
    return adoptRef(new IDBTransactionBackendImpl(id, objectStoreIds, mode, database));
 
45
}
 
46
 
 
47
IDBTransactionBackendImpl::IDBTransactionBackendImpl(int64_t id, const Vector<int64_t>& objectStoreIds, unsigned short mode, IDBDatabaseBackendImpl* database)
 
48
    : m_id(id)
 
49
    , m_objectStoreIds(objectStoreIds)
 
50
    , m_mode(mode)
 
51
    , m_state(Unused)
 
52
    , m_commitPending(false)
 
53
    , m_database(database)
 
54
    , m_transaction(database->backingStore().get())
 
55
    , m_taskTimer(this, &IDBTransactionBackendImpl::taskTimerFired)
 
56
    , m_pendingPreemptiveEvents(0)
 
57
{
 
58
    m_database->transactionCoordinator()->didCreateTransaction(this);
 
59
}
 
60
 
 
61
IDBTransactionBackendImpl::~IDBTransactionBackendImpl()
 
62
{
 
63
    // It shouldn't be possible for this object to get deleted until it's either complete or aborted.
 
64
    ASSERT(m_state == Finished);
 
65
}
 
66
 
 
67
PassRefPtr<IDBObjectStoreBackendInterface> IDBTransactionBackendImpl::objectStore(int64_t id, ExceptionCode& ec)
 
68
{
 
69
    if (m_state == Finished) {
 
70
        ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
 
71
        return 0;
 
72
    }
 
73
 
 
74
    RefPtr<IDBObjectStoreBackendImpl> objectStore = m_database->objectStore(id);
 
75
    ASSERT(objectStore);
 
76
    return objectStore.release();
 
77
}
 
78
 
 
79
bool IDBTransactionBackendImpl::scheduleTask(TaskType type, PassOwnPtr<ScriptExecutionContext::Task> task, PassOwnPtr<ScriptExecutionContext::Task> abortTask)
 
80
{
 
81
    if (m_state == Finished)
 
82
        return false;
 
83
 
 
84
    if (type == NormalTask)
 
85
        m_taskQueue.append(task);
 
86
    else
 
87
        m_preemptiveTaskQueue.append(task);
 
88
 
 
89
    if (abortTask)
 
90
        m_abortTaskQueue.prepend(abortTask);
 
91
 
 
92
    if (m_state == Unused)
 
93
        start();
 
94
    else if (m_state == Running && !m_taskTimer.isActive())
 
95
        m_taskTimer.startOneShot(0);
 
96
 
 
97
    return true;
 
98
}
 
99
 
 
100
void IDBTransactionBackendImpl::abort()
 
101
{
 
102
    abort(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
 
103
}
 
104
 
 
105
void IDBTransactionBackendImpl::abort(PassRefPtr<IDBDatabaseError> error)
 
106
{
 
107
    IDB_TRACE("IDBTransactionBackendImpl::abort");
 
108
    if (m_state == Finished)
 
109
        return;
 
110
 
 
111
    bool wasRunning = m_state == Running;
 
112
 
 
113
    // The last reference to this object may be released while performing the
 
114
    // abort steps below. We therefore take a self reference to keep ourselves
 
115
    // alive while executing this method.
 
116
    RefPtr<IDBTransactionBackendImpl> protect(this);
 
117
 
 
118
    m_state = Finished;
 
119
    m_taskTimer.stop();
 
120
 
 
121
    if (wasRunning)
 
122
        m_transaction.rollback();
 
123
 
 
124
    // Run the abort tasks, if any.
 
125
    while (!m_abortTaskQueue.isEmpty()) {
 
126
        OwnPtr<ScriptExecutionContext::Task> task(m_abortTaskQueue.takeFirst());
 
127
        task->performTask(0);
 
128
    }
 
129
 
 
130
    // Backing store resources (held via cursors) must be released before script callbacks
 
131
    // are fired, as the script callbacks may release references and allow the backing store
 
132
    // itself to be released, and order is critical.
 
133
    closeOpenCursors();
 
134
    m_transaction.reset();
 
135
 
 
136
    // Transactions must also be marked as completed before the front-end is notified, as
 
137
    // the transaction completion unblocks operations like closing connections.
 
138
    m_database->transactionCoordinator()->didFinishTransaction(this);
 
139
    ASSERT(!m_database->transactionCoordinator()->isActive(this));
 
140
    m_database->transactionFinished(this);
 
141
 
 
142
    if (m_callbacks)
 
143
        m_callbacks->onAbort(error);
 
144
 
 
145
    m_database->transactionFinishedAndAbortFired(this);
 
146
 
 
147
    m_database = 0;
 
148
}
 
149
 
 
150
bool IDBTransactionBackendImpl::isTaskQueueEmpty() const
 
151
{
 
152
    return m_preemptiveTaskQueue.isEmpty() && m_taskQueue.isEmpty();
 
153
}
 
154
 
 
155
bool IDBTransactionBackendImpl::hasPendingTasks() const
 
156
{
 
157
    return m_pendingPreemptiveEvents || !isTaskQueueEmpty();
 
158
}
 
159
 
 
160
void IDBTransactionBackendImpl::registerOpenCursor(IDBCursorBackendImpl* cursor)
 
161
{
 
162
    m_openCursors.add(cursor);
 
163
}
 
164
 
 
165
void IDBTransactionBackendImpl::unregisterOpenCursor(IDBCursorBackendImpl* cursor)
 
166
{
 
167
    m_openCursors.remove(cursor);
 
168
}
 
169
 
 
170
void IDBTransactionBackendImpl::run()
 
171
{
 
172
    // TransactionCoordinator has started this transaction. Schedule a timer
 
173
    // to process the first task.
 
174
    ASSERT(m_state == StartPending || m_state == Running);
 
175
    ASSERT(!m_taskTimer.isActive());
 
176
 
 
177
    m_taskTimer.startOneShot(0);
 
178
}
 
179
 
 
180
void IDBTransactionBackendImpl::start()
 
181
{
 
182
    ASSERT(m_state == Unused);
 
183
 
 
184
    m_state = StartPending;
 
185
    m_database->transactionCoordinator()->didStartTransaction(this);
 
186
    m_database->transactionStarted(this);
 
187
}
 
188
 
 
189
void IDBTransactionBackendImpl::commit()
 
190
{
 
191
    IDB_TRACE("IDBTransactionBackendImpl::commit");
 
192
 
 
193
    // In multiprocess ports, front-end may have requested a commit but an abort has already
 
194
    // been initiated asynchronously by the back-end.
 
195
    if (m_state == Finished)
 
196
        return;
 
197
 
 
198
    ASSERT(m_state == Unused || m_state == Running);
 
199
    m_commitPending = true;
 
200
 
 
201
    // Front-end has requested a commit, but there may be tasks like createIndex which
 
202
    // are considered synchronous by the front-end but are processed asynchronously.
 
203
    if (hasPendingTasks())
 
204
        return;
 
205
 
 
206
    // The last reference to this object may be released while performing the
 
207
    // commit steps below. We therefore take a self reference to keep ourselves
 
208
    // alive while executing this method.
 
209
    RefPtr<IDBTransactionBackendImpl> protect(this);
 
210
 
 
211
    bool unused = m_state == Unused;
 
212
    m_state = Finished;
 
213
 
 
214
    bool committed = unused || m_transaction.commit();
 
215
 
 
216
    // Backing store resources (held via cursors) must be released before script callbacks
 
217
    // are fired, as the script callbacks may release references and allow the backing store
 
218
    // itself to be released, and order is critical.
 
219
    closeOpenCursors();
 
220
    m_transaction.reset();
 
221
 
 
222
    // Transactions must also be marked as completed before the front-end is notified, as
 
223
    // the transaction completion unblocks operations like closing connections.
 
224
    if (!unused)
 
225
        m_database->transactionCoordinator()->didFinishTransaction(this);
 
226
    m_database->transactionFinished(this);
 
227
 
 
228
    if (committed) {
 
229
        m_callbacks->onComplete();
 
230
        m_database->transactionFinishedAndCompleteFired(this);
 
231
    } else {
 
232
        m_callbacks->onAbort(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
 
233
        m_database->transactionFinishedAndAbortFired(this);
 
234
    }
 
235
 
 
236
    m_database = 0;
 
237
}
 
238
 
 
239
void IDBTransactionBackendImpl::taskTimerFired(Timer<IDBTransactionBackendImpl>*)
 
240
{
 
241
    IDB_TRACE("IDBTransactionBackendImpl::taskTimerFired");
 
242
    ASSERT(!isTaskQueueEmpty());
 
243
 
 
244
    if (m_state == StartPending) {
 
245
        m_transaction.begin();
 
246
        m_state = Running;
 
247
    }
 
248
 
 
249
    // The last reference to this object may be released while performing the
 
250
    // tasks. Take take a self reference to keep this object alive so that
 
251
    // the loop termination conditions can be checked.
 
252
    RefPtr<IDBTransactionBackendImpl> protect(this);
 
253
 
 
254
    TaskQueue* taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
 
255
    while (!taskQueue->isEmpty() && m_state != Finished) {
 
256
        ASSERT(m_state == Running);
 
257
        OwnPtr<ScriptExecutionContext::Task> task(taskQueue->takeFirst());
 
258
        task->performTask(0);
 
259
 
 
260
        // Event itself may change which queue should be processed next.
 
261
        taskQueue = m_pendingPreemptiveEvents ? &m_preemptiveTaskQueue : &m_taskQueue;
 
262
    }
 
263
 
 
264
    // If there are no pending tasks, we haven't already committed/aborted,
 
265
    // and the front-end requested a commit, it is now safe to do so.
 
266
    if (!hasPendingTasks() && m_state != Finished && m_commitPending)
 
267
        commit();
 
268
}
 
269
 
 
270
void IDBTransactionBackendImpl::closeOpenCursors()
 
271
{
 
272
    for (HashSet<IDBCursorBackendImpl*>::iterator i = m_openCursors.begin(); i != m_openCursors.end(); ++i)
 
273
        (*i)->close();
 
274
    m_openCursors.clear();
 
275
}
 
276
 
 
277
};
 
278
 
 
279
#endif // ENABLE(INDEXED_DATABASE)