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