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