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

« back to all changes in this revision

Viewing changes to Source/WebCore/Modules/indexeddb/IDBDatabaseBackendImpl.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) 2011 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 "IDBDatabaseBackendImpl.h"
 
28
 
 
29
#if ENABLE(INDEXED_DATABASE)
 
30
 
 
31
#include "CrossThreadTask.h"
 
32
#include "IDBBackingStore.h"
 
33
#include "IDBDatabaseException.h"
 
34
#include "IDBFactoryBackendImpl.h"
 
35
#include "IDBObjectStoreBackendImpl.h"
 
36
#include "IDBTransactionBackendImpl.h"
 
37
#include "IDBTransactionCoordinator.h"
 
38
 
 
39
namespace WebCore {
 
40
 
 
41
class IDBDatabaseBackendImpl::PendingOpenCall {
 
42
public:
 
43
    static PassOwnPtr<PendingOpenCall> create(PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
 
44
    {
 
45
        return adoptPtr(new PendingOpenCall(callbacks, databaseCallbacks));
 
46
    }
 
47
    PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
 
48
    PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks() { return m_databaseCallbacks; }
 
49
 
 
50
private:
 
51
    PendingOpenCall(PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
 
52
        : m_callbacks(callbacks)
 
53
        , m_databaseCallbacks(databaseCallbacks)
 
54
    {
 
55
    }
 
56
 
 
57
    RefPtr<IDBCallbacks> m_callbacks;
 
58
    RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
 
59
};
 
60
 
 
61
class IDBDatabaseBackendImpl::PendingOpenWithVersionCall {
 
62
public:
 
63
    static PassOwnPtr<PendingOpenWithVersionCall> create(PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, int64_t version)
 
64
    {
 
65
        return adoptPtr(new PendingOpenWithVersionCall(callbacks, databaseCallbacks, version));
 
66
    }
 
67
    PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
 
68
    PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks() { return m_databaseCallbacks; }
 
69
    int64_t version() { return m_version; }
 
70
 
 
71
private:
 
72
    PendingOpenWithVersionCall(PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, int64_t version)
 
73
        : m_callbacks(callbacks)
 
74
        , m_databaseCallbacks(databaseCallbacks)
 
75
        , m_version(version)
 
76
    {
 
77
    }
 
78
    RefPtr<IDBCallbacks> m_callbacks;
 
79
    RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
 
80
    int64_t m_version;
 
81
};
 
82
 
 
83
class IDBDatabaseBackendImpl::PendingDeleteCall {
 
84
public:
 
85
    static PassOwnPtr<PendingDeleteCall> create(PassRefPtr<IDBCallbacks> callbacks)
 
86
    {
 
87
        return adoptPtr(new PendingDeleteCall(callbacks));
 
88
    }
 
89
    PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
 
90
 
 
91
private:
 
92
    PendingDeleteCall(PassRefPtr<IDBCallbacks> callbacks)
 
93
        : m_callbacks(callbacks)
 
94
    {
 
95
    }
 
96
    RefPtr<IDBCallbacks> m_callbacks;
 
97
};
 
98
 
 
99
PassRefPtr<IDBDatabaseBackendImpl> IDBDatabaseBackendImpl::create(const String& name, IDBBackingStore* database, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
 
100
{
 
101
    RefPtr<IDBDatabaseBackendImpl> backend = adoptRef(new IDBDatabaseBackendImpl(name, database, factory, uniqueIdentifier));
 
102
    if (!backend->openInternal())
 
103
        return 0;
 
104
    return backend.release();
 
105
}
 
106
 
 
107
namespace {
 
108
const char* NoStringVersion = "";
 
109
}
 
110
 
 
111
IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBBackingStore* backingStore, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
 
112
    : m_backingStore(backingStore)
 
113
    , m_metadata(name, InvalidId, NoStringVersion, IDBDatabaseMetadata::NoIntVersion, InvalidId)
 
114
    , m_identifier(uniqueIdentifier)
 
115
    , m_factory(factory)
 
116
    , m_transactionCoordinator(IDBTransactionCoordinator::create())
 
117
    , m_closingConnection(false)
 
118
{
 
119
    ASSERT(!m_metadata.name.isNull());
 
120
}
 
121
 
 
122
bool IDBDatabaseBackendImpl::openInternal()
 
123
{
 
124
    bool success = m_backingStore->getIDBDatabaseMetaData(m_metadata.name, &m_metadata);
 
125
    ASSERT_WITH_MESSAGE(success == (m_metadata.id != InvalidId), "success = %s, m_id = %lld", success ? "true" : "false", static_cast<long long>(m_metadata.id));
 
126
    if (success) {
 
127
        loadObjectStores();
 
128
        return true;
 
129
    }
 
130
    return m_backingStore->createIDBDatabaseMetaData(m_metadata.name, m_metadata.version, m_metadata.intVersion, m_metadata.id);
 
131
}
 
132
 
 
133
IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
 
134
{
 
135
}
 
136
 
 
137
PassRefPtr<IDBBackingStore> IDBDatabaseBackendImpl::backingStore() const
 
138
{
 
139
    return m_backingStore;
 
140
}
 
141
 
 
142
IDBDatabaseMetadata IDBDatabaseBackendImpl::metadata() const
 
143
{
 
144
    // FIXME: Figure out a way to keep m_metadata.objectStores.get(N).indexes up to date rather than regenerating this every time.
 
145
    IDBDatabaseMetadata metadata(m_metadata);
 
146
    for (ObjectStoreMap::const_iterator it = m_objectStores.begin(); it != m_objectStores.end(); ++it)
 
147
        metadata.objectStores.set(it->value->id(), it->value->metadata());
 
148
    return metadata;
 
149
}
 
150
 
 
151
PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(int64_t id, const String& name, const IDBKeyPath& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
 
152
{
 
153
    ASSERT(!m_objectStores.contains(id));
 
154
 
 
155
    RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(this, IDBObjectStoreMetadata(name, id, keyPath, autoIncrement, IDBObjectStoreBackendInterface::MinimumIndexId));
 
156
    ASSERT(objectStore->name() == name);
 
157
 
 
158
    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
 
159
    ASSERT(transaction->mode() == IDBTransaction::VERSION_CHANGE);
 
160
 
 
161
    // FIXME: Fix edge cases around transaction aborts that prevent this from just being ASSERT(id == m_metadata.maxObjectStoreId + 1)
 
162
    ASSERT(id > m_metadata.maxObjectStoreId);
 
163
    m_metadata.maxObjectStoreId = id;
 
164
 
 
165
    RefPtr<IDBDatabaseBackendImpl> database = this;
 
166
    if (!transaction->scheduleTask(
 
167
            createCallbackTask(&IDBDatabaseBackendImpl::createObjectStoreInternal, database, objectStore, transaction),
 
168
            createCallbackTask(&IDBDatabaseBackendImpl::removeObjectStoreFromMap, database, objectStore))) {
 
169
        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
 
170
        return 0;
 
171
    }
 
172
 
 
173
    m_objectStores.set(id, objectStore);
 
174
    return objectStore.release();
 
175
}
 
176
 
 
177
void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendImpl> transaction)
 
178
{
 
179
    if (!database->m_backingStore->createObjectStore(transaction->backingStoreTransaction(), database->id(), objectStore->id(), objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement())) {
 
180
        transaction->abort();
 
181
        return;
 
182
    }
 
183
}
 
184
 
 
185
PassRefPtr<IDBObjectStoreBackendImpl> IDBDatabaseBackendImpl::objectStore(int64_t id)
 
186
{
 
187
    return m_objectStores.get(id);
 
188
}
 
189
 
 
190
void IDBDatabaseBackendImpl::deleteObjectStore(int64_t id, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
 
191
{
 
192
    ASSERT(m_objectStores.contains(id));
 
193
 
 
194
    RefPtr<IDBDatabaseBackendImpl> database = this;
 
195
    RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(id);
 
196
    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
 
197
    ASSERT(transaction->mode() == IDBTransaction::VERSION_CHANGE);
 
198
 
 
199
    if (!transaction->scheduleTask(
 
200
            createCallbackTask(&IDBDatabaseBackendImpl::deleteObjectStoreInternal, database, objectStore, transaction),
 
201
            createCallbackTask(&IDBDatabaseBackendImpl::addObjectStoreToMap, database, objectStore))) {
 
202
        ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
 
203
        return;
 
204
    }
 
205
    m_objectStores.remove(id);
 
206
}
 
207
 
 
208
void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendImpl> transaction)
 
209
{
 
210
    database->m_backingStore->deleteObjectStore(transaction->backingStoreTransaction(), database->id(), objectStore->id());
 
211
}
 
212
 
 
213
void IDBDatabaseBackendImpl::setIntVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, int64_t version, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks, PassRefPtr<IDBTransactionBackendImpl> transaction)
 
214
{
 
215
    RefPtr<IDBCallbacks> callbacks(prpCallbacks);
 
216
    int64_t databaseId = database->id();
 
217
    int64_t oldVersion = database->m_metadata.intVersion;
 
218
    ASSERT(version > oldVersion);
 
219
    database->m_metadata.intVersion = version;
 
220
    if (!database->m_backingStore->updateIDBDatabaseIntVersion(transaction->backingStoreTransaction(), databaseId, database->m_metadata.intVersion)) {
 
221
        RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage.");
 
222
        callbacks->onError(error);
 
223
        transaction->abort(error);
 
224
        return;
 
225
    }
 
226
    ASSERT(!database->m_pendingSecondHalfOpenWithVersion);
 
227
    database->m_pendingSecondHalfOpenWithVersion = PendingOpenWithVersionCall::create(callbacks, databaseCallbacks, version);
 
228
    callbacks->onUpgradeNeeded(oldVersion, transaction, database);
 
229
}
 
230
 
 
231
void IDBDatabaseBackendImpl::transactionStarted(PassRefPtr<IDBTransactionBackendImpl> prpTransaction)
 
232
{
 
233
    RefPtr<IDBTransactionBackendImpl> transaction = prpTransaction;
 
234
    if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
 
235
        ASSERT(!m_runningVersionChangeTransaction);
 
236
        m_runningVersionChangeTransaction = transaction;
 
237
    }
 
238
}
 
239
 
 
240
void IDBDatabaseBackendImpl::transactionFinished(PassRefPtr<IDBTransactionBackendImpl> prpTransaction)
 
241
{
 
242
    RefPtr<IDBTransactionBackendImpl> transaction = prpTransaction;
 
243
    ASSERT(m_transactions.contains(transaction.get()));
 
244
    m_transactions.remove(transaction.get());
 
245
    if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
 
246
        ASSERT(transaction.get() == m_runningVersionChangeTransaction.get());
 
247
        m_runningVersionChangeTransaction.clear();
 
248
    }
 
249
}
 
250
 
 
251
void IDBDatabaseBackendImpl::transactionFinishedAndAbortFired(PassRefPtr<IDBTransactionBackendImpl> prpTransaction)
 
252
{
 
253
    RefPtr<IDBTransactionBackendImpl> transaction = prpTransaction;
 
254
    if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
 
255
        // If this was an open-with-version call, there will be a "second
 
256
        // half" open call waiting for us in processPendingCalls.
 
257
        // FIXME: When we no longer support setVersion, assert such a thing.
 
258
        if (m_pendingSecondHalfOpenWithVersion) {
 
259
            m_pendingSecondHalfOpenWithVersion->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::IDB_ABORT_ERR, "Version change transaction was aborted in upgradeneeded event handler."));
 
260
            m_pendingSecondHalfOpenWithVersion.release();
 
261
        }
 
262
        processPendingCalls();
 
263
    }
 
264
}
 
265
 
 
266
void IDBDatabaseBackendImpl::transactionFinishedAndCompleteFired(PassRefPtr<IDBTransactionBackendImpl> prpTransaction)
 
267
{
 
268
    RefPtr<IDBTransactionBackendImpl> transaction = prpTransaction;
 
269
    if (transaction->mode() == IDBTransaction::VERSION_CHANGE)
 
270
        processPendingCalls();
 
271
}
 
272
 
 
273
size_t IDBDatabaseBackendImpl::connectionCount()
 
274
{
 
275
    // This does not include pending open calls, as those should not block version changes and deletes.
 
276
    return m_databaseCallbacksSet.size();
 
277
}
 
278
 
 
279
void IDBDatabaseBackendImpl::processPendingCalls()
 
280
{
 
281
    if (m_pendingSecondHalfOpenWithVersion) {
 
282
        ASSERT(m_pendingSecondHalfOpenWithVersion->version() == m_metadata.intVersion);
 
283
        ASSERT(m_metadata.id != InvalidId);
 
284
        m_pendingSecondHalfOpenWithVersion->callbacks()->onSuccess(this);
 
285
        m_pendingSecondHalfOpenWithVersion.release();
 
286
        // Fall through when complete, as pending deletes may be (partially) unblocked.
 
287
    }
 
288
 
 
289
    // Note that this check is only an optimization to reduce queue-churn and
 
290
    // not necessary for correctness; deleteDatabase and openConnection will
 
291
    // requeue their calls if this condition is true.
 
292
    if (m_runningVersionChangeTransaction)
 
293
        return;
 
294
 
 
295
    // Pending calls may be requeued.
 
296
    Deque<OwnPtr<PendingDeleteCall> > pendingDeleteCalls;
 
297
    m_pendingDeleteCalls.swap(pendingDeleteCalls);
 
298
    while (!pendingDeleteCalls.isEmpty()) {
 
299
        OwnPtr<PendingDeleteCall> pendingDeleteCall = pendingDeleteCalls.takeFirst();
 
300
        deleteDatabase(pendingDeleteCall->callbacks());
 
301
    }
 
302
 
 
303
    // This check is also not really needed, openConnection would just requeue its calls.
 
304
    if (m_runningVersionChangeTransaction || !m_pendingDeleteCalls.isEmpty())
 
305
        return;
 
306
 
 
307
    Deque<OwnPtr<PendingOpenWithVersionCall> > pendingOpenWithVersionCalls;
 
308
    m_pendingOpenWithVersionCalls.swap(pendingOpenWithVersionCalls);
 
309
    while (!pendingOpenWithVersionCalls.isEmpty()) {
 
310
        OwnPtr<PendingOpenWithVersionCall> pendingOpenWithVersionCall = pendingOpenWithVersionCalls.takeFirst();
 
311
        openConnectionWithVersion(pendingOpenWithVersionCall->callbacks(), pendingOpenWithVersionCall->databaseCallbacks(), pendingOpenWithVersionCall->version());
 
312
    }
 
313
 
 
314
    // Open calls can be requeued if an openWithVersion call started a version
 
315
    // change transaction.
 
316
    Deque<OwnPtr<PendingOpenCall> > pendingOpenCalls;
 
317
    m_pendingOpenCalls.swap(pendingOpenCalls);
 
318
    while (!pendingOpenCalls.isEmpty()) {
 
319
        OwnPtr<PendingOpenCall> pendingOpenCall = pendingOpenCalls.takeFirst();
 
320
        openConnection(pendingOpenCall->callbacks(), pendingOpenCall->databaseCallbacks());
 
321
    }
 
322
}
 
323
 
 
324
// FIXME: Remove this as part of https://bugs.webkit.org/show_bug.cgi?id=102733.
 
325
PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(const Vector<int64_t>& objectStoreIds, unsigned short mode)
 
326
{
 
327
    return createTransaction(0, objectStoreIds, mode);
 
328
}
 
329
 
 
330
PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::createTransaction(int64_t transactionId, const Vector<int64_t>& objectStoreIds, unsigned short mode)
 
331
{
 
332
    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::create(transactionId, objectStoreIds, mode, this);
 
333
    m_transactions.add(transaction.get());
 
334
    return transaction.release();
 
335
}
 
336
 
 
337
void IDBDatabaseBackendImpl::openConnection(PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
 
338
{
 
339
    ASSERT(m_backingStore.get());
 
340
    if (!m_pendingDeleteCalls.isEmpty() || m_runningVersionChangeTransaction) {
 
341
        m_pendingOpenCalls.append(PendingOpenCall::create(callbacks, databaseCallbacks));
 
342
        return;
 
343
    }
 
344
    if (m_metadata.id == InvalidId && !openInternal()) {
 
345
        callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
 
346
        return;
 
347
    }
 
348
    if (m_metadata.version == NoStringVersion && m_metadata.intVersion == IDBDatabaseMetadata::NoIntVersion) {
 
349
        // Spec says: If no version is specified and no database exists, set
 
350
        // database version to 1. We infer that the database didn't exist from
 
351
        // its lack of either type of version.
 
352
        openConnectionWithVersion(callbacks, databaseCallbacks, 1);
 
353
        return;
 
354
    }
 
355
    m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(databaseCallbacks));
 
356
    callbacks->onSuccess(this);
 
357
}
 
358
 
 
359
void IDBDatabaseBackendImpl::runIntVersionChangeTransaction(int64_t requestedVersion, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks)
 
360
{
 
361
    RefPtr<IDBCallbacks> callbacks = prpCallbacks;
 
362
    RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
 
363
    ASSERT(callbacks);
 
364
    for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
 
365
        // Front end ensures the event is not fired at connections that have closePending set.
 
366
        if (*it != databaseCallbacks)
 
367
            (*it)->onVersionChange(m_metadata.intVersion, requestedVersion);
 
368
    }
 
369
    // The spec dictates we wait until all the version change events are
 
370
    // delivered and then check m_databaseCallbacks.empty() before proceeding
 
371
    // or firing a blocked event, but instead we should be consistent with how
 
372
    // the old setVersion (incorrectly) did it.
 
373
    // FIXME: Remove the call to onBlocked and instead wait until the frontend
 
374
    // tells us that all the blocked events have been delivered. See
 
375
    // https://bugs.webkit.org/show_bug.cgi?id=71130
 
376
    if (connectionCount())
 
377
        callbacks->onBlocked(m_metadata.intVersion);
 
378
    // FIXME: Add test for m_runningVersionChangeTransaction.
 
379
    if (m_runningVersionChangeTransaction || connectionCount()) {
 
380
        m_pendingOpenWithVersionCalls.append(PendingOpenWithVersionCall::create(callbacks, databaseCallbacks, requestedVersion));
 
381
        return;
 
382
    }
 
383
 
 
384
    Vector<int64_t> objectStoreIds;
 
385
    RefPtr<IDBTransactionBackendInterface> transactionInterface = transaction(objectStoreIds, IDBTransaction::VERSION_CHANGE);
 
386
    RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionInterface.get());
 
387
 
 
388
    RefPtr<IDBDatabaseBackendImpl> database = this;
 
389
    OwnPtr<ScriptExecutionContext::Task> intVersionTask = createCallbackTask(&IDBDatabaseBackendImpl::setIntVersionInternal, database, requestedVersion, callbacks, databaseCallbacks, transaction);
 
390
    OwnPtr<ScriptExecutionContext::Task> resetVersionOnAbortTask = createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_metadata.version, m_metadata.intVersion);
 
391
    if (!transaction->scheduleTask(intVersionTask.release(), resetVersionOnAbortTask.release())) {
 
392
        ASSERT_NOT_REACHED();
 
393
    }
 
394
    ASSERT(!m_pendingSecondHalfOpenWithVersion);
 
395
    m_databaseCallbacksSet.add(databaseCallbacks);
 
396
}
 
397
 
 
398
void IDBDatabaseBackendImpl::openConnectionWithVersion(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t version)
 
399
{
 
400
    RefPtr<IDBCallbacks> callbacks = prpCallbacks;
 
401
    RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
 
402
    if (!m_pendingDeleteCalls.isEmpty() || m_runningVersionChangeTransaction) {
 
403
        m_pendingOpenWithVersionCalls.append(PendingOpenWithVersionCall::create(callbacks, databaseCallbacks, version));
 
404
        return;
 
405
    }
 
406
    if (m_metadata.id == InvalidId) {
 
407
        if (openInternal())
 
408
            ASSERT(m_metadata.intVersion == IDBDatabaseMetadata::NoIntVersion);
 
409
        else {
 
410
            callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
 
411
            return;
 
412
        }
 
413
    }
 
414
    if (version > m_metadata.intVersion) {
 
415
        runIntVersionChangeTransaction(version, callbacks, databaseCallbacks);
 
416
        return;
 
417
    }
 
418
    if (version < m_metadata.intVersion) {
 
419
        callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::VER_ERR, String::format("The requested version (%lld) is less than the existing version (%lld).", static_cast<long long>(version), static_cast<long long>(m_metadata.intVersion))));
 
420
        return;
 
421
    }
 
422
    ASSERT(version == m_metadata.intVersion);
 
423
    m_databaseCallbacksSet.add(databaseCallbacks);
 
424
    callbacks->onSuccess(this);
 
425
}
 
426
 
 
427
void IDBDatabaseBackendImpl::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
 
428
{
 
429
    if (m_runningVersionChangeTransaction) {
 
430
        m_pendingDeleteCalls.append(PendingDeleteCall::create(prpCallbacks));
 
431
        return;
 
432
    }
 
433
    RefPtr<IDBCallbacks> callbacks = prpCallbacks;
 
434
    for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
 
435
        // Front end ensures the event is not fired at connections that have closePending set.
 
436
        (*it)->onVersionChange(NoStringVersion);
 
437
    }
 
438
    // FIXME: Only fire onBlocked if there are open connections after the
 
439
    // VersionChangeEvents are received, not just set up to fire.
 
440
    // https://bugs.webkit.org/show_bug.cgi?id=71130
 
441
    if (connectionCount()) {
 
442
        m_pendingDeleteCalls.append(PendingDeleteCall::create(callbacks));
 
443
        callbacks->onBlocked();
 
444
        return;
 
445
    }
 
446
    ASSERT(m_backingStore);
 
447
    if (!m_backingStore->deleteDatabase(m_metadata.name)) {
 
448
        callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
 
449
        return;
 
450
    }
 
451
    m_metadata.version = NoStringVersion;
 
452
    m_metadata.id = InvalidId;
 
453
    m_metadata.intVersion = IDBDatabaseMetadata::NoIntVersion;
 
454
    m_objectStores.clear();
 
455
    callbacks->onSuccess();
 
456
}
 
457
 
 
458
void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
 
459
{
 
460
    RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
 
461
    ASSERT(m_databaseCallbacksSet.contains(callbacks));
 
462
 
 
463
    m_databaseCallbacksSet.remove(callbacks);
 
464
    if (m_pendingSecondHalfOpenWithVersion && m_pendingSecondHalfOpenWithVersion->databaseCallbacks() == callbacks) {
 
465
        m_pendingSecondHalfOpenWithVersion->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::IDB_ABORT_ERR, "The connection was closed."));
 
466
        m_pendingSecondHalfOpenWithVersion.release();
 
467
    }
 
468
 
 
469
    if (connectionCount() > 1)
 
470
        return;
 
471
 
 
472
    // processPendingCalls allows the inspector to process a pending open call
 
473
    // and call close, reentering IDBDatabaseBackendImpl::close. Then the
 
474
    // backend would be removed both by the inspector closing its connection, and
 
475
    // by the connection that first called close.
 
476
    // To avoid that situation, don't proceed in case of reentrancy.
 
477
    if (m_closingConnection)
 
478
        return;
 
479
    m_closingConnection = true;
 
480
    processPendingCalls();
 
481
 
 
482
    // FIXME: Add a test for the m_pendingOpenCalls and m_pendingOpenWithVersionCalls cases below.
 
483
    if (!connectionCount() && !m_pendingOpenCalls.size() && !m_pendingOpenWithVersionCalls.size() && !m_pendingDeleteCalls.size()) {
 
484
        TransactionSet transactions(m_transactions);
 
485
        for (TransactionSet::const_iterator it = transactions.begin(); it != transactions.end(); ++it)
 
486
            (*it)->abort();
 
487
 
 
488
        ASSERT(m_transactions.isEmpty());
 
489
 
 
490
        m_backingStore.clear();
 
491
        // This check should only be false in tests.
 
492
        if (m_factory)
 
493
            m_factory->removeIDBDatabaseBackend(m_identifier);
 
494
    }
 
495
    m_closingConnection = false;
 
496
}
 
497
 
 
498
void IDBDatabaseBackendImpl::loadObjectStores()
 
499
{
 
500
    Vector<int64_t> ids;
 
501
    Vector<String> names;
 
502
    Vector<IDBKeyPath> keyPaths;
 
503
    Vector<bool> autoIncrementFlags;
 
504
    Vector<int64_t> maxIndexIds;
 
505
    Vector<IDBObjectStoreMetadata> objectStores = m_backingStore->getObjectStores(m_metadata.id);
 
506
 
 
507
    for (size_t i = 0; i < objectStores.size(); i++)
 
508
        m_objectStores.set(objectStores[i].id, IDBObjectStoreBackendImpl::create(this, objectStores[i]));
 
509
}
 
510
 
 
511
void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> prpObjectStore)
 
512
{
 
513
    RefPtr<IDBObjectStoreBackendImpl> objectStore = prpObjectStore;
 
514
    ASSERT(database->m_objectStores.contains(objectStore->id()));
 
515
    database->m_objectStores.remove(objectStore->id());
 
516
}
 
517
 
 
518
void IDBDatabaseBackendImpl::addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
 
519
{
 
520
    RefPtr<IDBObjectStoreBackendImpl> objectStorePtr = objectStore;
 
521
    ASSERT(!database->m_objectStores.contains(objectStorePtr->id()));
 
522
    database->m_objectStores.set(objectStorePtr->id(), objectStorePtr);
 
523
}
 
524
 
 
525
void IDBDatabaseBackendImpl::resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& previousVersion, int64_t previousIntVersion)
 
526
{
 
527
    database->m_metadata.version = previousVersion;
 
528
    database->m_metadata.intVersion = previousIntVersion;
 
529
}
 
530
 
 
531
} // namespace WebCore
 
532
 
 
533
#endif // ENABLE(INDEXED_DATABASE)