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