f94458f43b288a0ffe5b59b0627af6f140eb7f1d
[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)
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<IDBDatabaseBackendLevelDB> IDBDatabaseBackendLevelDB::create(const String& name, IDBBackingStoreInterface* backingStore, IDBFactoryBackendInterface* factory, const String& uniqueIdentifier)
48 {
49     RefPtr<IDBDatabaseBackendLevelDB> backend = adoptRef(new IDBDatabaseBackendLevelDB(name, backingStore, factory, uniqueIdentifier));
50     if (!backend->openInternal())
51         return 0;
52     return backend.release();
53 }
54
55 IDBDatabaseBackendLevelDB::IDBDatabaseBackendLevelDB(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 IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::removeObjectStore(int64_t objectStoreId)
77 {
78     ASSERT(m_metadata.objectStores.contains(objectStoreId));
79     m_metadata.objectStores.remove(objectStoreId);
80 }
81
82 void IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::~IDBDatabaseBackendLevelDB()
120 {
121 }
122
123 IDBBackingStoreInterface* IDBDatabaseBackendLevelDB::backingStore() const
124 {
125     return m_backingStore.get();
126 }
127
128 void IDBDatabaseBackendLevelDB::createObjectStore(int64_t transactionId, int64_t objectStoreId, const String& name, const IDBKeyPath& keyPath, bool autoIncrement)
129 {
130     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::deleteObjectStore(int64_t transactionId, int64_t objectStoreId)
144 {
145     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::createIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId, const String& name, const IDBKeyPath& keyPath, bool unique, bool multiEntry)
159 {
160     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::deleteIndex(int64_t transactionId, int64_t objectStoreId, int64_t indexId)
178 {
179     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::get(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, bool keyOnly, PassRefPtr<IDBCallbacks> callbacks)
218 {
219     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 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)
228 {
229     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::setIndexKeys(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKey> prpPrimaryKey, const Vector<int64_t>& indexIds, const Vector<IndexKeys>& indexKeys)
243 {
244     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::setIndexesReady(int64_t transactionId, int64_t, const Vector<int64_t>& indexIds)
289 {
290     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::setIndexesReady");
291
292     IDBTransactionBackendInterface* transaction = m_transactions.get(transactionId);
293     if (!transaction)
294         return;
295
296     transaction->scheduleSetIndexesReadyOperation(indexIds.size());
297 }
298
299 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)
300 {
301     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::count(int64_t transactionId, int64_t objectStoreId, int64_t indexId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
310 {
311     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::deleteRange(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBKeyRange> keyRange, PassRefPtr<IDBCallbacks> callbacks)
322 {
323     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::clear(int64_t transactionId, int64_t objectStoreId, PassRefPtr<IDBCallbacks> callbacks)
333 {
334     LOG(StorageAPI, "IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::transactionStarted(IDBTransactionBackendInterface* transaction)
344 {
345     if (transaction->mode() == IndexedDB::TransactionVersionChange) {
346         ASSERT(!m_runningVersionChangeTransaction);
347         m_runningVersionChangeTransaction = transaction;
348     }
349 }
350
351 void IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::transactionFinishedAndCompleteFired(IDBTransactionBackendInterface* rawTransaction)
379 {
380     RefPtr<IDBTransactionBackendInterface> transaction = rawTransaction;
381     if (transaction->mode() == IndexedDB::TransactionVersionChange)
382         processPendingCalls();
383 }
384
385 size_t IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::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 IDBDatabaseBackendLevelDB::createTransaction(int64_t transactionId, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, unsigned short mode)
431 {
432     RefPtr<IDBTransactionBackendInterface> transaction = m_factory->createTransactionBackend(this, transactionId, callbacks, objectStoreIds, static_cast<IndexedDB::TransactionMode>(mode));
433
434     ASSERT(!m_transactions.contains(transactionId));
435     m_transactions.add(transactionId, transaction.get());
436 }
437
438 void IDBDatabaseBackendLevelDB::openConnection(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, int64_t version)
439 {
440     ASSERT(m_backingStore.get());
441     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
442     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
443
444     if (!m_pendingDeleteCalls.isEmpty() || m_runningVersionChangeTransaction) {
445         m_pendingOpenCalls.append(PendingOpenCall::create(callbacks, databaseCallbacks, transactionId, version));
446         return;
447     }
448
449     if (m_metadata.id == InvalidId) {
450         // The database was deleted then immediately re-opened; openInternal() recreates it in the backing store.
451         if (openInternal())
452             ASSERT(m_metadata.version == IDBDatabaseMetadata::NoIntVersion);
453         else {
454             String message;
455             RefPtr<IDBDatabaseError> error;
456             if (version == IDBDatabaseMetadata::NoIntVersion)
457                 message = "Internal error opening database with no version specified.";
458             else
459                 message = String::format("Internal error opening database with version %lld", static_cast<long long>(version));
460             callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, message));
461             return;
462         }
463     }
464
465     // We infer that the database didn't exist from its lack of either type of version.
466     bool isNewDatabase = m_metadata.version == IDBDatabaseMetadata::NoIntVersion;
467
468     if (version == IDBDatabaseMetadata::DefaultIntVersion) {
469         // FIXME: this comments was related to Chromium code. It may be incorrect
470         // For unit tests only - skip upgrade steps. Calling from script with DefaultIntVersion throws exception.
471         ASSERT(isNewDatabase);
472         m_databaseCallbacksSet.add(databaseCallbacks);
473         callbacks->onSuccess(this, this->metadata());
474         return;
475     }
476
477     if (version == IDBDatabaseMetadata::NoIntVersion) {
478         if (!isNewDatabase) {
479             m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(databaseCallbacks));
480             callbacks->onSuccess(this, this->metadata());
481             return;
482         }
483         // Spec says: If no version is specified and no database exists, set database version to 1.
484         version = 1;
485     }
486
487     // FIXME: Database versions are now of type uint64_t, but this code expected int64_t.
488     if (version > (int64_t)m_metadata.version) {
489         runIntVersionChangeTransaction(callbacks, databaseCallbacks, transactionId, version);
490         return;
491     }
492
493     // FIXME: Database versions are now of type uint64_t, but this code expected int64_t.
494     if (version < (int64_t)m_metadata.version) {
495         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))));
496         return;
497     }
498
499     // FIXME: Database versions are now of type uint64_t, but this code expected int64_t.
500     ASSERT(version == (int64_t)m_metadata.version);
501     m_databaseCallbacksSet.add(databaseCallbacks);
502     callbacks->onSuccess(this, this->metadata());
503 }
504
505 void IDBDatabaseBackendLevelDB::runIntVersionChangeTransaction(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t transactionId, int64_t requestedVersion)
506 {
507     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
508     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
509     ASSERT(callbacks);
510     for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
511         // Front end ensures the event is not fired at connections that have closePending set.
512         if (*it != databaseCallbacks)
513             (*it)->onVersionChange(m_metadata.version, requestedVersion, IndexedDB::NullVersion);
514     }
515     // The spec dictates we wait until all the version change events are
516     // delivered and then check m_databaseCallbacks.empty() before proceeding
517     // or firing a blocked event, but instead we should be consistent with how
518     // the old setVersion (incorrectly) did it.
519     // FIXME: Remove the call to onBlocked and instead wait until the frontend
520     // tells us that all the blocked events have been delivered. See
521     // https://bugs.webkit.org/show_bug.cgi?id=71130
522     if (connectionCount())
523         callbacks->onBlocked(m_metadata.version);
524     // FIXME: Add test for m_runningVersionChangeTransaction.
525     if (m_runningVersionChangeTransaction || connectionCount()) {
526         m_pendingOpenCalls.append(PendingOpenCall::create(callbacks, databaseCallbacks, transactionId, requestedVersion));
527         return;
528     }
529
530     Vector<int64_t> objectStoreIds;
531     createTransaction(transactionId, databaseCallbacks, objectStoreIds, IndexedDB::TransactionVersionChange);
532     RefPtr<IDBTransactionBackendInterface> transaction = m_transactions.get(transactionId);
533
534     transaction->scheduleVersionChangeOperation(transactionId, requestedVersion, callbacks, databaseCallbacks, m_metadata);
535
536     ASSERT(!m_pendingSecondHalfOpen);
537     m_databaseCallbacksSet.add(databaseCallbacks);
538 }
539
540 void IDBDatabaseBackendLevelDB::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
541 {
542     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
543     if (isDeleteDatabaseBlocked()) {
544         for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
545             // Front end ensures the event is not fired at connections that have closePending set.
546             (*it)->onVersionChange(m_metadata.version, 0, IndexedDB::NullVersion);
547         }
548         // FIXME: Only fire onBlocked if there are open connections after the
549         // VersionChangeEvents are received, not just set up to fire.
550         // https://bugs.webkit.org/show_bug.cgi?id=71130
551         callbacks->onBlocked(m_metadata.version);
552         m_pendingDeleteCalls.append(PendingDeleteCall::create(callbacks.release()));
553         return;
554     }
555     deleteDatabaseFinal(callbacks.release());
556 }
557
558 bool IDBDatabaseBackendLevelDB::isDeleteDatabaseBlocked()
559 {
560     return connectionCount();
561 }
562
563 void IDBDatabaseBackendLevelDB::deleteDatabaseFinal(PassRefPtr<IDBCallbacks> callbacks)
564 {
565     ASSERT(!isDeleteDatabaseBlocked());
566     ASSERT(m_backingStore);
567     if (!m_backingStore->deleteDatabase(m_metadata.name)) {
568         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error deleting database."));
569         return;
570     }
571     m_metadata.id = InvalidId;
572     m_metadata.version = IDBDatabaseMetadata::NoIntVersion;
573     m_metadata.objectStores.clear();
574     callbacks->onSuccess();
575 }
576
577 void IDBDatabaseBackendLevelDB::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
578 {
579     RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
580     ASSERT(m_databaseCallbacksSet.contains(callbacks));
581
582     m_databaseCallbacksSet.remove(callbacks);
583     if (m_pendingSecondHalfOpen && m_pendingSecondHalfOpen->databaseCallbacks() == callbacks) {
584         m_pendingSecondHalfOpen->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed."));
585         m_pendingSecondHalfOpen.release();
586     }
587
588     if (connectionCount() > 1)
589         return;
590
591     // processPendingCalls allows the inspector to process a pending open call
592     // and call close, reentering IDBDatabaseBackendLevelDB::close. Then the
593     // backend would be removed both by the inspector closing its connection, and
594     // by the connection that first called close.
595     // To avoid that situation, don't proceed in case of reentrancy.
596     if (m_closingConnection)
597         return;
598     TemporaryChange<bool> closingConnection(m_closingConnection, true);
599     processPendingCalls();
600
601     // FIXME: Add a test for the m_pendingOpenCalls cases below.
602     if (!connectionCount() && !m_pendingOpenCalls.size() && !m_pendingDeleteCalls.size()) {
603         TransactionMap transactions(m_transactions);
604         RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Connection is closing.");
605         for (TransactionMap::const_iterator::Values it = transactions.values().begin(), end = transactions.values().end(); it != end; ++it)
606             (*it)->abort(error);
607
608         ASSERT(m_transactions.isEmpty());
609
610         m_backingStore.clear();
611
612         // This check should only be false in unit tests.
613         ASSERT(m_factory);
614         if (m_factory)
615             m_factory->removeIDBDatabaseBackend(m_identifier);
616     }
617 }
618
619 } // namespace WebCore
620
621 #endif // ENABLE(INDEXED_DATABASE)