85d805beac97e1088be5f06b2b8df546b4961774
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBDatabaseBackend.cpp
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 "IDBDatabaseBackend.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "IDBBackingStoreInterface.h"
32 #include "IDBCursorBackend.h"
33 #include "IDBDatabaseCallbacks.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBFactoryBackendInterface.h"
36 #include "IDBIndexWriter.h"
37 #include "IDBKeyRange.h"
38 #include "IDBRecordIdentifier.h"
39 #include "IDBTransactionBackend.h"
40 #include "IDBTransactionCoordinator.h"
41 #include "Logging.h"
42 #include "SharedBuffer.h"
43 #include <wtf/TemporaryChange.h>
44
45 namespace WebCore {
46
47 PassRefPtr<IDBDatabaseBackend> IDBDatabaseBackend::create(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection)
48 {
49     RefPtr<IDBDatabaseBackend> backend = adoptRef(new IDBDatabaseBackend(name, uniqueIdentifier, factory, serverConnection));
50     backend->openInternalAsync();
51     
52     return backend.release();
53 }
54
55 IDBDatabaseBackend::IDBDatabaseBackend(const String& name, const String& uniqueIdentifier, IDBFactoryBackendInterface* factory, IDBServerConnection& serverConnection)
56     : m_metadata(name, InvalidId, 0, InvalidId)
57     , m_identifier(uniqueIdentifier)
58     , m_factory(factory)
59     , m_serverConnection(serverConnection)
60     , m_transactionCoordinator(IDBTransactionCoordinator::create())
61     , m_closingConnection(false)
62 {
63     ASSERT(!m_metadata.name.isNull());
64 }
65
66 void IDBDatabaseBackend::addObjectStore(const IDBObjectStoreMetadata& objectStore, int64_t newMaxObjectStoreId)
67 {
68     ASSERT(!m_metadata.objectStores.contains(objectStore.id));
69     if (newMaxObjectStoreId != IDBObjectStoreMetadata::InvalidId) {
70         ASSERT(m_metadata.maxObjectStoreId < newMaxObjectStoreId);
71         m_metadata.maxObjectStoreId = newMaxObjectStoreId;
72     }
73     m_metadata.objectStores.set(objectStore.id, objectStore);
74 }
75
76 void IDBDatabaseBackend::removeObjectStore(int64_t objectStoreId)
77 {
78     ASSERT(m_metadata.objectStores.contains(objectStoreId));
79     m_metadata.objectStores.remove(objectStoreId);
80 }
81
82 void IDBDatabaseBackend::addIndex(int64_t objectStoreId, const IDBIndexMetadata& index, int64_t newMaxIndexId)
83 {
84     ASSERT(m_metadata.objectStores.contains(objectStoreId));
85     IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
86
87     ASSERT(!objectStore.indexes.contains(index.id));
88     objectStore.indexes.set(index.id, index);
89     if (newMaxIndexId != IDBIndexMetadata::InvalidId) {
90         ASSERT(objectStore.maxIndexId < newMaxIndexId);
91         objectStore.maxIndexId = newMaxIndexId;
92     }
93     m_metadata.objectStores.set(objectStoreId, objectStore);
94 }
95
96 void IDBDatabaseBackend::removeIndex(int64_t objectStoreId, int64_t indexId)
97 {
98     ASSERT(m_metadata.objectStores.contains(objectStoreId));
99     IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
100
101     ASSERT(objectStore.indexes.contains(indexId));
102     objectStore.indexes.remove(indexId);
103     m_metadata.objectStores.set(objectStoreId, objectStore);
104 }
105
106 void IDBDatabaseBackend::openInternalAsync()
107 {
108     RefPtr<IDBDatabaseBackend> self = this;
109     m_serverConnection->getOrEstablishIDBDatabaseMetadata(m_metadata.name, [self](const IDBDatabaseMetadata& metadata, bool success) {
110         self->didOpenInternalAsync(metadata, success);
111     });
112 }
113
114 void IDBDatabaseBackend::didOpenInternalAsync(const IDBDatabaseMetadata& metadata, bool success)
115 {
116     if (!success) {
117         processPendingOpenCalls(false);
118         return;
119     }
120
121     m_metadata = metadata;
122
123     processPendingCalls();
124 }
125
126 IDBDatabaseBackend::~IDBDatabaseBackend()
127 {
128 }
129
130 void IDBDatabaseBackend::createObjectStore(int64_t transactionId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement)
131 {
132     LOG(StorageAPI, "IDBDatabaseBackend::createObjectStore");
133     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
134     if (!transaction)
135         return;
136     ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
137
138     ASSERT(!m_metadata.objectStores.contains(objectStoreId));
139     IDBObjectStoreMetadata objectStoreMetadata(name, objectStoreId, keyPath, autoIncrement, IDBDatabaseBackend::MinimumIndexId);
140
141     transaction->scheduleCreateObjectStoreOperation(objectStoreMetadata);
142     addObjectStore(objectStoreMetadata, objectStoreId);
143 }
144
145 void IDBDatabaseBackend::deleteObjectStore(int64_t transactionId, int64_t objectStoreId)
146 {
147     LOG(StorageAPI, "IDBDatabaseBackend::deleteObjectStore");
148     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
149     if (!transaction)
150         return;
151     ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
152
153     ASSERT(m_metadata.objectStores.contains(objectStoreId));
154     const IDBObjectStoreMetadata& objectStoreMetadata = m_metadata.objectStores.get(objectStoreId);
155
156     transaction->scheduleDeleteObjectStoreOperation(objectStoreMetadata);
157     removeObjectStore(objectStoreId);
158 }
159
160 void IDBDatabaseBackend::createIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry)
161 {
162     LOG(StorageAPI, "IDBDatabaseBackend::createIndex");
163     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
164     if (!transaction)
165         return;
166     ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
167
168     ASSERT(m_metadata.objectStores.contains(objectStoreId));
169     const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
170
171     ASSERT(!objectStore.indexes.contains(indexId));
172     const IDBIndexMetadata indexMetadata(name, indexId, keyPath, unique, multiEntry);
173
174     transaction->scheduleCreateIndexOperation(objectStoreId, indexMetadata);
175
176     addIndex(objectStoreId, indexMetadata, indexId);
177 }
178
179 void IDBDatabaseBackend::deleteIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId)
180 {
181     LOG(StorageAPI, "IDBDatabaseBackend::deleteIndex");
182     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
183     if (!transaction)
184         return;
185     ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
186
187     ASSERT(m_metadata.objectStores.contains(objectStoreId));
188     const IDBObjectStoreMetadata objectStore = m_metadata.objectStores.get(objectStoreId);
189
190     ASSERT(objectStore.indexes.contains(indexId));
191     const IDBIndexMetadata& indexMetadata = objectStore.indexes.get(indexId);
192
193     transaction->scheduleDeleteIndexOperation(objectStoreId, indexMetadata);
194
195     removeIndex(objectStoreId, indexId);
196 }
197
198 void IDBDatabaseBackend::commit(int64_t transactionId)
199 {
200     // The frontend suggests that we commit, but we may have previously initiated an abort, and so have disposed of the transaction. onAbort has already been dispatched to the frontend, so it will find out about that asynchronously.
201     if (m_transactions.contains(transactionId))
202         m_transactions.get(transactionId)->commit();
203 }
204
205 void IDBDatabaseBackend::abort(int64_t transactionId)
206 {
207     // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it.
208     if (m_transactions.contains(transactionId))
209         m_transactions.get(transactionId)->abort();
210 }
211
212 void IDBDatabaseBackend::abort(int64_t transactionId, PassRefPtr<IDBDatabaseError> error)
213 {
214     // If the transaction is unknown, then it has already been aborted by the backend before this call so it is safe to ignore it.
215     if (m_transactions.contains(transactionId))
216         m_transactions.get(transactionId)->abort(error);
217 }
218
219 void IDBDatabaseBackend::get(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, bool keyOnly, PassRefPtr<IDBCallbacks> callbacks)
220 {
221     LOG(StorageAPI, "IDBDatabaseBackend::get");
222     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
223     if (!transaction)
224         return;
225
226     transaction->scheduleGetOperation(m_metadata, objectStoreId, indexId, keyRange, keyOnly ? IndexedDB::CursorKeyOnly : IndexedDB::CursorKeyAndValue, callbacks);
227 }
228
229 void IDBDatabaseBackend::put(int64_t transactionId, int64_t objectStoreId, PassRefPtr<SharedBuffer> value, PassRefPtr<IDBKey> key, PutMode putMode, PassRefPtr<IDBCallbacks> callbacks, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
230 {
231     LOG(StorageAPI, "IDBDatabaseBackend::put");
232     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
233     if (!transaction)
234         return;
235     ASSERT(transaction->mode() != IndexedDB::TransactionReadOnly);
236
237     const IDBObjectStoreMetadata objectStoreMetadata = m_metadata.objectStores.get(objectStoreId);
238
239     ASSERT(objectStoreMetadata.autoIncrement || key.get());
240
241     transaction->schedulePutOperation(objectStoreMetadata, value, key, putMode, callbacks, indexIds, indexKeys);
242 }
243
244 void IDBDatabaseBackend::setIndexKeys(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKey> prpPrimaryKey, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
245 {
246     LOG(StorageAPI, "IDBDatabaseBackend::setIndexKeys");
247     ASSERT(prpPrimaryKey);
248
249     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
250     if (!transaction)
251         return;
252     ASSERT(transaction->mode() == IndexedDB::TransactionVersionChange);
253
254     RefPtr<IDBKey> primaryKey = prpPrimaryKey;
255     RefPtr<IDBBackingStoreInterface> store = m_serverConnection->deprecatedBackingStore();
256     // FIXME: This method could be asynchronous, but we need to evaluate if it's worth the extra complexity.
257     RefPtr<IDBRecordIdentifier> recordIdentifier;
258     bool ok = store->keyExistsInObjectStore(transaction->backingStoreTransaction(), m_metadata.id, objectStoreId, *primaryKey, recordIdentifier);
259     if (!ok) {
260         transaction->abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error setting index keys."));
261         return;
262     }
263     if (!recordIdentifier) {
264         RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, String::format("Internal error setting index keys for object store."));
265         transaction->abort(error.release());
266         return;
267     }
268
269     Vector<RefPtr<IDBIndexWriter>> indexWriters;
270     String errorMessage;
271     bool obeysConstraints = false;
272     ASSERT(m_metadata.objectStores.contains(objectStoreId));
273     const IDBObjectStoreMetadata& objectStoreMetadata = m_metadata.objectStores.get(objectStoreId);
274     bool backingStoreSuccess = store->makeIndexWriters(*transaction, id(), objectStoreMetadata, *primaryKey, false, indexIds, indexKeys, indexWriters, &errorMessage, obeysConstraints);
275     if (!backingStoreSuccess) {
276         transaction->abort(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error: backing store error updating index keys."));
277         return;
278     }
279     if (!obeysConstraints) {
280         transaction->abort(IDBDatabaseError::create(IDBDatabaseException::ConstraintError, errorMessage));
281         return;
282     }
283
284     for (size_t i = 0; i < indexWriters.size(); ++i) {
285         IDBIndexWriter* indexWriter = indexWriters[i].get();
286         indexWriter->writeIndexKeys(recordIdentifier.get(), *store.get(), transaction->backingStoreTransaction(), id(), objectStoreId);
287     }
288 }
289
290 void IDBDatabaseBackend::setIndexesReady(int64_t transactionId, int64_t, const Vector<int64_t>& indexIds)
291 {
292     LOG(StorageAPI, "IDBDatabaseBackend::setIndexesReady");
293
294     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
295     if (!transaction)
296         return;
297
298     transaction->scheduleSetIndexesReadyOperation(indexIds.size());
299 }
300
301 void IDBDatabaseBackend::openCursor(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, IndexedDB::CursorDirection direction, bool keyOnly, TaskType taskType, PassRefPtr<IDBCallbacks> callbacks)
302 {
303     LOG(StorageAPI, "IDBDatabaseBackend::openCursor");
304     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
305     if (!transaction)
306         return;
307
308     transaction->scheduleOpenCursorOperation(objectStoreId, indexId, keyRange, direction, keyOnly ? IndexedDB::CursorKeyOnly : IndexedDB::CursorKeyAndValue, taskType, callbacks);
309 }
310
311 void IDBDatabaseBackend::count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
312 {
313     LOG(StorageAPI, "IDBDatabaseBackend::count");
314     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
315     if (!transaction)
316         return;
317
318     ASSERT(m_metadata.objectStores.contains(objectStoreId));
319     transaction->scheduleCountOperation(objectStoreId, indexId, keyRange, callbacks);
320 }
321
322
323 void IDBDatabaseBackend::deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
324 {
325     LOG(StorageAPI, "IDBDatabaseBackend::deleteRange");
326     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
327     if (!transaction)
328         return;
329     ASSERT(transaction->mode() != IndexedDB::TransactionReadOnly);
330
331     transaction->scheduleDeleteRangeOperation(objectStoreId, keyRange, callbacks);
332 }
333
334 void IDBDatabaseBackend::clear(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
335 {
336     LOG(StorageAPI, "IDBDatabaseBackend::clear");
337     IDBTransactionBackend* transaction = m_transactions.get(transactionId);
338     if (!transaction)
339         return;
340     ASSERT(transaction->mode() != IndexedDB::TransactionReadOnly);
341
342     transaction->scheduleClearOperation(objectStoreId, callbacks);
343 }
344
345 void IDBDatabaseBackend::transactionStarted(IDBTransactionBackend* transaction)
346 {
347     if (transaction->mode() == IndexedDB::TransactionVersionChange) {
348         ASSERT(!m_runningVersionChangeTransaction);
349         m_runningVersionChangeTransaction = transaction;
350     }
351 }
352
353 void IDBDatabaseBackend::transactionFinished(IDBTransactionBackend* rawTransaction)
354 {
355     RefPtr<IDBTransactionBackend> transaction = rawTransaction;
356     ASSERT(m_transactions.contains(transaction->id()));
357     ASSERT(m_transactions.get(transaction->id()) == transaction.get());
358     m_transactions.remove(transaction->id());
359     if (transaction->mode() == IndexedDB::TransactionVersionChange) {
360         ASSERT(transaction.get() == m_runningVersionChangeTransaction.get());
361         m_runningVersionChangeTransaction.clear();
362     }
363 }
364
365 void IDBDatabaseBackend::transactionFinishedAndAbortFired(IDBTransactionBackend* rawTransaction)
366 {
367     RefPtr<IDBTransactionBackend> transaction = rawTransaction;
368     if (transaction->mode() == IndexedDB::TransactionVersionChange) {
369         // If this was an open-with-version call, there will be a "second
370         // half" open call waiting for us in processPendingCalls.
371         // FIXME: When we no longer support setVersion, assert such a thing.
372         if (m_pendingSecondHalfOpen) {
373             m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "Version change transaction was aborted in upgradeneeded event handler."));
374             m_pendingSecondHalfOpen.release();
375         }
376         processPendingCalls();
377     }
378 }
379
380 void IDBDatabaseBackend::transactionFinishedAndCompleteFired(IDBTransactionBackend* rawTransaction)
381 {
382     RefPtr<IDBTransactionBackend> transaction = rawTransaction;
383     if (transaction->mode() == IndexedDB::TransactionVersionChange)
384         processPendingCalls();
385 }
386
387 size_t IDBDatabaseBackend::connectionCount()
388 {
389     // This does not include pending open calls, as those should not block version changes and deletes.
390     return m_databaseCallbacksSet.size();
391 }
392
393 void IDBDatabaseBackend::processPendingCalls()
394 {
395     if (m_pendingSecondHalfOpen) {
396         ASSERT(m_pendingSecondHalfOpen->version() == m_metadata.version);
397         ASSERT(m_metadata.id != InvalidId);
398         m_pendingSecondHalfOpen->callbacks()->onSuccess(this, this->metadata());
399         m_pendingSecondHalfOpen.release();
400         // Fall through when complete, as pending deletes may be (partially) unblocked.
401     }
402
403     // Note that this check is only an optimization to reduce queue-churn and
404     // not necessary for correctness; deleteDatabase and openConnection will
405     // requeue their calls if this condition is true.
406     if (m_runningVersionChangeTransaction)
407         return;
408
409     if (!m_pendingDeleteCalls.isEmpty() && isDeleteDatabaseBlocked())
410         return;
411     while (!m_pendingDeleteCalls.isEmpty()) {
412         OwnPtr<IDBPendingDeleteCall> pendingDeleteCall = m_pendingDeleteCalls.takeFirst();
413         m_deleteCallbacksWaitingCompletion.add(pendingDeleteCall->callbacks());
414         deleteDatabaseAsync(pendingDeleteCall->callbacks());
415     }
416
417     // deleteDatabaseAsync should never re-queue calls.
418     ASSERT(m_pendingDeleteCalls.isEmpty());
419
420     // If there are any database deletions waiting for completion, we're done for now.
421     // Further callbacks will be handled in a future call to processPendingCalls().
422     if (m_deleteCallbacksWaitingCompletion.isEmpty())
423         return;
424
425     if (m_runningVersionChangeTransaction)
426         return;
427
428     processPendingOpenCalls(true);
429 }
430
431 void IDBDatabaseBackend::processPendingOpenCalls(bool success)
432 {
433     // Open calls can be requeued if an open call started a version change transaction or deletes the database.
434     Deque<OwnPtr<IDBPendingOpenCall>> pendingOpenCalls;
435     m_pendingOpenCalls.swap(pendingOpenCalls);
436
437     while (!pendingOpenCalls.isEmpty()) {
438         OwnPtr<IDBPendingOpenCall> pendingOpenCall = pendingOpenCalls.takeFirst();
439         if (success) {
440             if (m_metadata.id == InvalidId) {
441                 // This database was deleted then quickly re-opened.
442                 // openInternalAsync() will recreate it in the backing store and then resume processing pending callbacks.
443                 pendingOpenCalls.prepend(pendingOpenCall.release());
444                 pendingOpenCalls.swap(m_pendingOpenCalls);
445
446                 openInternalAsync();
447                 return;
448             }
449             openConnectionInternal(pendingOpenCall->callbacks(), pendingOpenCall->databaseCallbacks(), pendingOpenCall->transactionId(), pendingOpenCall->version());
450         } else {
451             String message;
452             if (pendingOpenCall->version() == IDBDatabaseMetadata::NoIntVersion)
453                 message = "Internal error opening database with no version specified.";
454             else
455                 message = String::format("Internal error opening database with version %llu", static_cast<unsigned long long>(pendingOpenCall->version()));
456             pendingOpenCall->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message));
457         }
458     }
459 }
460
461 void IDBDatabaseBackend::createTransaction(int64_t transactionId, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, unsigned short mode)
462 {
463     RefPtr<IDBTransactionBackend> transaction = m_factory->maybeCreateTransactionBackend(this, transactionId, callbacks, objectStoreIds, static_cast<IndexedDB::TransactionMode>(mode));
464
465     if (!transaction)
466         return;
467
468     ASSERT(!m_transactions.contains(transactionId));
469     m_transactions.add(transactionId, transaction.get());
470 }
471
472 void IDBDatabaseBackend::openConnection(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
473 {
474     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
475     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
476
477     m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, version));
478
479     processPendingCalls();
480 }
481
482
483 void IDBDatabaseBackend::openConnectionInternal(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, uint64_t version)
484 {
485     ASSERT(m_pendingDeleteCalls.isEmpty());
486     ASSERT(!m_runningVersionChangeTransaction);
487
488     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
489     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
490
491     // We infer that the database didn't exist from its lack of either type of version.
492     bool isNewDatabase = m_metadata.version == IDBDatabaseMetadata::NoIntVersion;
493
494     if (version == IDBDatabaseMetadata::DefaultIntVersion) {
495         // FIXME: this comments was related to Chromium code. It may be incorrect
496         // For unit tests only - skip upgrade steps. Calling from script with DefaultIntVersion throws exception.
497         ASSERT(isNewDatabase);
498         m_databaseCallbacksSet.add(databaseCallbacks);
499         callbacks->onSuccess(this, this->metadata());
500         return;
501     }
502
503     if (version == IDBDatabaseMetadata::NoIntVersion) {
504         if (!isNewDatabase) {
505             m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(databaseCallbacks));
506             callbacks->onSuccess(this, this->metadata());
507             return;
508         }
509         // Spec says: If no version is specified and no database exists, set database version to 1.
510         version = 1;
511     }
512
513     if (version > m_metadata.version) {
514         runIntVersionChangeTransaction(callbacks, databaseCallbacks, transactionId, version);
515         return;
516     }
517
518     if (version < m_metadata.version) {
519         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::VersionError, String::format("The requested version (%llu) is less than the existing version (%llu).", static_cast<unsigned long long>(version), static_cast<unsigned long long>(m_metadata.version))));
520         return;
521     }
522
523     ASSERT(version == m_metadata.version);
524     m_databaseCallbacksSet.add(databaseCallbacks);
525     callbacks->onSuccess(this, this->metadata());
526 }
527
528 void IDBDatabaseBackend::runIntVersionChangeTransaction(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, int64_t requestedVersion)
529 {
530     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
531     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
532     ASSERT(callbacks);
533     for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
534         // Front end ensures the event is not fired at connections that have closePending set.
535         if (*it != databaseCallbacks)
536             (*it)->onVersionChange(m_metadata.version, requestedVersion, IndexedDB::NullVersion);
537     }
538     // The spec dictates we wait until all the version change events are
539     // delivered and then check m_databaseCallbacks.empty() before proceeding
540     // or firing a blocked event, but instead we should be consistent with how
541     // the old setVersion (incorrectly) did it.
542     // FIXME: Remove the call to onBlocked and instead wait until the frontend
543     // tells us that all the blocked events have been delivered. See
544     // https://bugs.webkit.org/show_bug.cgi?id=71130
545     if (connectionCount())
546         callbacks->onBlocked(m_metadata.version);
547     // FIXME: Add test for m_runningVersionChangeTransaction.
548     if (m_runningVersionChangeTransaction || connectionCount()) {
549         m_pendingOpenCalls.append(IDBPendingOpenCall::create(*callbacks, *databaseCallbacks, transactionId, requestedVersion));
550         return;
551     }
552
553     Vector<int64_t> objectStoreIds;
554     createTransaction(transactionId, databaseCallbacks, objectStoreIds, IndexedDB::TransactionVersionChange);
555     RefPtr<IDBTransactionBackend> transaction = m_transactions.get(transactionId);
556
557     transaction->scheduleVersionChangeOperation(transactionId, requestedVersion, callbacks, databaseCallbacks, m_metadata);
558
559     ASSERT(!m_pendingSecondHalfOpen);
560     m_databaseCallbacksSet.add(databaseCallbacks);
561 }
562
563 void IDBDatabaseBackend::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
564 {
565     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
566     if (isDeleteDatabaseBlocked()) {
567         for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
568             // Front end ensures the event is not fired at connections that have closePending set.
569             (*it)->onVersionChange(m_metadata.version, 0, IndexedDB::NullVersion);
570         }
571         // FIXME: Only fire onBlocked if there are open connections after the
572         // VersionChangeEvents are received, not just set up to fire.
573         // https://bugs.webkit.org/show_bug.cgi?id=71130
574         callbacks->onBlocked(m_metadata.version);
575         m_pendingDeleteCalls.append(IDBPendingDeleteCall::create(callbacks.release()));
576         return;
577     }
578     deleteDatabaseAsync(callbacks.release());
579 }
580
581 bool IDBDatabaseBackend::isDeleteDatabaseBlocked()
582 {
583     return connectionCount();
584 }
585
586 void IDBDatabaseBackend::deleteDatabaseAsync(PassRefPtr<IDBCallbacks> callbacks)
587 {
588     ASSERT(!isDeleteDatabaseBlocked());
589
590     RefPtr<IDBDatabaseBackend> self(this);
591     m_serverConnection->deleteDatabase(m_metadata.name, [self, callbacks](bool success) {
592         ASSERT(self->m_deleteCallbacksWaitingCompletion.contains(callbacks));
593         self->m_deleteCallbacksWaitingCompletion.remove(callbacks);
594
595         // If this IDBDatabaseBackend was closed while waiting for deleteDatabase to complete, no point in performing any callbacks.
596         if (!self->m_serverConnection->isClosed())
597             return;
598
599         if (success) {
600             self->m_metadata.id = InvalidId;
601             self->m_metadata.version = IDBDatabaseMetadata::NoIntVersion;
602             self->m_metadata.objectStores.clear();
603             callbacks->onSuccess();
604         } else
605             callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error deleting database."));
606
607         self->processPendingCalls();
608     });
609 }
610
611 void IDBDatabaseBackend::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
612 {
613     RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
614     ASSERT(m_databaseCallbacksSet.contains(callbacks));
615
616     m_databaseCallbacksSet.remove(callbacks);
617     if (m_pendingSecondHalfOpen && m_pendingSecondHalfOpen->databaseCallbacks() == callbacks) {
618         m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed."));
619         m_pendingSecondHalfOpen.release();
620     }
621
622     if (connectionCount() > 1)
623         return;
624
625     // processPendingCalls allows the inspector to process a pending open call
626     // and call close, reentering IDBDatabaseBackend::close. Then the
627     // backend would be removed both by the inspector closing its connection, and
628     // by the connection that first called close.
629     // To avoid that situation, don't proceed in case of reentrancy.
630     if (m_closingConnection)
631         return;
632     TemporaryChange<bool> closingConnection(m_closingConnection, true);
633     processPendingCalls();
634
635     // FIXME: Add a test for the m_pendingOpenCalls cases below.
636     if (!connectionCount() && !m_pendingOpenCalls.size() && !m_pendingDeleteCalls.size()) {
637         TransactionMap transactions(m_transactions);
638         RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Connection is closing.");
639         for (TransactionMap::const_iterator::Values it = transactions.values().begin(), end = transactions.values().end(); it != end; ++it)
640             (*it)->abort(error);
641
642         ASSERT(m_transactions.isEmpty());
643
644         m_serverConnection->close();
645
646         // This check should only be false in unit tests.
647         ASSERT(m_factory);
648         if (m_factory)
649             m_factory->removeIDBDatabaseBackend(m_identifier);
650     }
651 }
652
653 } // namespace WebCore
654
655 #endif // ENABLE(INDEXED_DATABASE)