ae7af0eb48e730961d8a5fae1d05a276be466b0b
[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 = m_backingStore->getIDBDatabaseMetaData(m_metadata.name, &m_metadata);
240     ASSERT_WITH_MESSAGE(success == (m_metadata.id != InvalidId), "success = %s, m_id = %lld", success ? "true" : "false", static_cast<long long>(m_metadata.id));
241     if (success) {
242         loadObjectStores();
243         return true;
244     }
245     return m_backingStore->createIDBDatabaseMetaData(m_metadata.name, m_metadata.version, m_metadata.intVersion, m_metadata.id);
246 }
247
248 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
249 {
250 }
251
252 PassRefPtr<IDBBackingStore> IDBDatabaseBackendImpl::backingStore() const
253 {
254     return m_backingStore;
255 }
256
257 IDBDatabaseMetadata IDBDatabaseBackendImpl::metadata() const
258 {
259     // FIXME: Figure out a way to keep m_metadata.objectStores.get(N).indexes up to date rather than regenerating this every time.
260     IDBDatabaseMetadata metadata(m_metadata);
261     for (ObjectStoreMap::const_iterator it = m_objectStores.begin(); it != m_objectStores.end(); ++it)
262         metadata.objectStores.set(it->value->id(), it->value->metadata());
263     return metadata;
264 }
265
266 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(int64_t id, const String& name, const IDBKeyPath& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
267 {
268     ASSERT(!m_objectStores.contains(id));
269
270     RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(this, IDBObjectStoreMetadata(name, id, keyPath, autoIncrement, IDBObjectStoreBackendInterface::MinimumIndexId));
271     ASSERT(objectStore->name() == name);
272
273     RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
274     ASSERT(transaction->mode() == IDBTransaction::VERSION_CHANGE);
275
276     // FIXME: Fix edge cases around transaction aborts that prevent this from just being ASSERT(id == m_metadata.maxObjectStoreId + 1)
277     ASSERT(id > m_metadata.maxObjectStoreId);
278     m_metadata.maxObjectStoreId = id;
279
280     if (!transaction->scheduleTask(CreateObjectStoreOperation::create(this, objectStore), CreateObjectStoreAbortOperation::create(this, objectStore))) {
281         ec = IDBDatabaseException::TransactionInactiveError;
282         return 0;
283     }
284
285     m_objectStores.set(id, objectStore);
286     return objectStore.release();
287 }
288
289 void IDBDatabaseBackendImpl::CreateObjectStoreOperation::perform(IDBTransactionBackendImpl* transaction)
290 {
291     IDB_TRACE("CreateObjectStoreOperation");
292     if (!m_database->m_backingStore->createObjectStore(transaction->backingStoreTransaction(), m_database->id(), m_objectStore->id(), m_objectStore->name(), m_objectStore->keyPath(), m_objectStore->autoIncrement())) {
293         transaction->abort();
294         return;
295     }
296 }
297
298 PassRefPtr<IDBObjectStoreBackendImpl> IDBDatabaseBackendImpl::objectStore(int64_t id)
299 {
300     return m_objectStores.get(id);
301 }
302
303 void IDBDatabaseBackendImpl::deleteObjectStore(int64_t id, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
304 {
305     ASSERT(m_objectStores.contains(id));
306
307     RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(id);
308     RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionPtr);
309     ASSERT(transaction->mode() == IDBTransaction::VERSION_CHANGE);
310
311     if (!transaction->scheduleTask(DeleteObjectStoreOperation::create(this, objectStore), DeleteObjectStoreAbortOperation::create(this, objectStore))) {
312         ec = IDBDatabaseException::TransactionInactiveError;
313         return;
314     }
315     m_objectStores.remove(id);
316 }
317
318 void IDBDatabaseBackendImpl::DeleteObjectStoreOperation::perform(IDBTransactionBackendImpl* transaction)
319 {
320     IDB_TRACE("DeleteObjectStoreOperation");
321     m_database->m_backingStore->deleteObjectStore(transaction->backingStoreTransaction(), m_database->id(), m_objectStore->id());
322 }
323
324 void IDBDatabaseBackendImpl::VersionChangeOperation::perform(IDBTransactionBackendImpl* transaction)
325 {
326     IDB_TRACE("VersionChangeOperation");
327     int64_t databaseId = m_database->id();
328     int64_t oldVersion = m_database->m_metadata.intVersion;
329     ASSERT(m_version > oldVersion);
330     m_database->m_metadata.intVersion = m_version;
331     if (!m_database->m_backingStore->updateIDBDatabaseIntVersion(transaction->backingStoreTransaction(), databaseId, m_database->m_metadata.intVersion)) {
332         RefPtr<IDBDatabaseError> error = IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Error writing data to stable storage.");
333         m_callbacks->onError(error);
334         transaction->abort(error);
335         return;
336     }
337     ASSERT(!m_database->m_pendingSecondHalfOpenWithVersion);
338     m_database->m_pendingSecondHalfOpenWithVersion = PendingOpenWithVersionCall::create(m_callbacks, m_databaseCallbacks, m_version);
339     m_callbacks->onUpgradeNeeded(oldVersion, transaction, m_database);
340 }
341
342 void IDBDatabaseBackendImpl::transactionStarted(PassRefPtr<IDBTransactionBackendImpl> prpTransaction)
343 {
344     RefPtr<IDBTransactionBackendImpl> transaction = prpTransaction;
345     if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
346         ASSERT(!m_runningVersionChangeTransaction);
347         m_runningVersionChangeTransaction = transaction;
348     }
349 }
350
351 void IDBDatabaseBackendImpl::transactionFinished(PassRefPtr<IDBTransactionBackendImpl> prpTransaction)
352 {
353     RefPtr<IDBTransactionBackendImpl> transaction = prpTransaction;
354     ASSERT(m_transactions.contains(transaction.get()));
355     m_transactions.remove(transaction.get());
356     if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
357         ASSERT(transaction.get() == m_runningVersionChangeTransaction.get());
358         m_runningVersionChangeTransaction.clear();
359     }
360 }
361
362 void IDBDatabaseBackendImpl::transactionFinishedAndAbortFired(PassRefPtr<IDBTransactionBackendImpl> prpTransaction)
363 {
364     RefPtr<IDBTransactionBackendImpl> transaction = prpTransaction;
365     if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
366         // If this was an open-with-version call, there will be a "second
367         // half" open call waiting for us in processPendingCalls.
368         // FIXME: When we no longer support setVersion, assert such a thing.
369         if (m_pendingSecondHalfOpenWithVersion) {
370             m_pendingSecondHalfOpenWithVersion->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "Version change transaction was aborted in upgradeneeded event handler."));
371             m_pendingSecondHalfOpenWithVersion.release();
372         }
373         processPendingCalls();
374     }
375 }
376
377 void IDBDatabaseBackendImpl::transactionFinishedAndCompleteFired(PassRefPtr<IDBTransactionBackendImpl> prpTransaction)
378 {
379     RefPtr<IDBTransactionBackendImpl> transaction = prpTransaction;
380     if (transaction->mode() == IDBTransaction::VERSION_CHANGE)
381         processPendingCalls();
382 }
383
384 size_t IDBDatabaseBackendImpl::connectionCount()
385 {
386     // This does not include pending open calls, as those should not block version changes and deletes.
387     return m_databaseCallbacksSet.size();
388 }
389
390 void IDBDatabaseBackendImpl::processPendingCalls()
391 {
392     if (m_pendingSecondHalfOpenWithVersion) {
393         ASSERT(m_pendingSecondHalfOpenWithVersion->version() == m_metadata.intVersion);
394         ASSERT(m_metadata.id != InvalidId);
395         m_pendingSecondHalfOpenWithVersion->callbacks()->onSuccess(this);
396         m_pendingSecondHalfOpenWithVersion.release();
397         // Fall through when complete, as pending deletes may be (partially) unblocked.
398     }
399
400     // Note that this check is only an optimization to reduce queue-churn and
401     // not necessary for correctness; deleteDatabase and openConnection will
402     // requeue their calls if this condition is true.
403     if (m_runningVersionChangeTransaction)
404         return;
405
406     // Pending calls may be requeued.
407     Deque<OwnPtr<PendingDeleteCall> > pendingDeleteCalls;
408     m_pendingDeleteCalls.swap(pendingDeleteCalls);
409     while (!pendingDeleteCalls.isEmpty()) {
410         OwnPtr<PendingDeleteCall> pendingDeleteCall = pendingDeleteCalls.takeFirst();
411         deleteDatabase(pendingDeleteCall->callbacks());
412     }
413
414     // This check is also not really needed, openConnection would just requeue its calls.
415     if (m_runningVersionChangeTransaction || !m_pendingDeleteCalls.isEmpty())
416         return;
417
418     Deque<OwnPtr<PendingOpenWithVersionCall> > pendingOpenWithVersionCalls;
419     m_pendingOpenWithVersionCalls.swap(pendingOpenWithVersionCalls);
420     while (!pendingOpenWithVersionCalls.isEmpty()) {
421         OwnPtr<PendingOpenWithVersionCall> pendingOpenWithVersionCall = pendingOpenWithVersionCalls.takeFirst();
422         openConnectionWithVersion(pendingOpenWithVersionCall->callbacks(), pendingOpenWithVersionCall->databaseCallbacks(), pendingOpenWithVersionCall->version());
423     }
424
425     // Open calls can be requeued if an openWithVersion call started a version
426     // change transaction.
427     Deque<OwnPtr<PendingOpenCall> > pendingOpenCalls;
428     m_pendingOpenCalls.swap(pendingOpenCalls);
429     while (!pendingOpenCalls.isEmpty()) {
430         OwnPtr<PendingOpenCall> pendingOpenCall = pendingOpenCalls.takeFirst();
431         openConnection(pendingOpenCall->callbacks(), pendingOpenCall->databaseCallbacks());
432     }
433 }
434
435 // FIXME: Remove this method in https://bugs.webkit.org/show_bug.cgi?id=103923.
436 PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::createTransaction(int64_t transactionId, const Vector<int64_t>& objectStoreIds, IDBTransaction::Mode mode)
437 {
438     RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::create(transactionId, objectStoreIds, mode, this);
439     m_transactions.add(transaction.get());
440     return transaction.release();
441 }
442
443 void IDBDatabaseBackendImpl::createTransaction(int64_t transactionId, PassRefPtr<IDBDatabaseCallbacks> callbacks, const Vector<int64_t>& objectStoreIds, unsigned short mode)
444 {
445     ASSERT_NOT_REACHED();
446 }
447
448 void IDBDatabaseBackendImpl::openConnection(PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
449 {
450     ASSERT(m_backingStore.get());
451     if (!m_pendingDeleteCalls.isEmpty() || m_runningVersionChangeTransaction) {
452         m_pendingOpenCalls.append(PendingOpenCall::create(callbacks, databaseCallbacks));
453         return;
454     }
455     if (m_metadata.id == InvalidId && !openInternal()) {
456         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error."));
457         return;
458     }
459     if (m_metadata.version == NoStringVersion && m_metadata.intVersion == IDBDatabaseMetadata::NoIntVersion) {
460         // Spec says: If no version is specified and no database exists, set
461         // database version to 1. We infer that the database didn't exist from
462         // its lack of either type of version.
463         openConnectionWithVersion(callbacks, databaseCallbacks, 1);
464         return;
465     }
466     m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(databaseCallbacks));
467     callbacks->onSuccess(this);
468 }
469
470 void IDBDatabaseBackendImpl::runIntVersionChangeTransaction(int64_t requestedVersion, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks)
471 {
472     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
473     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
474     ASSERT(callbacks);
475     for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
476         // Front end ensures the event is not fired at connections that have closePending set.
477         if (*it != databaseCallbacks)
478             (*it)->onVersionChange(m_metadata.intVersion, requestedVersion);
479     }
480     // The spec dictates we wait until all the version change events are
481     // delivered and then check m_databaseCallbacks.empty() before proceeding
482     // or firing a blocked event, but instead we should be consistent with how
483     // the old setVersion (incorrectly) did it.
484     // FIXME: Remove the call to onBlocked and instead wait until the frontend
485     // tells us that all the blocked events have been delivered. See
486     // https://bugs.webkit.org/show_bug.cgi?id=71130
487     if (connectionCount())
488         callbacks->onBlocked(m_metadata.intVersion);
489     // FIXME: Add test for m_runningVersionChangeTransaction.
490     if (m_runningVersionChangeTransaction || connectionCount()) {
491         m_pendingOpenWithVersionCalls.append(PendingOpenWithVersionCall::create(callbacks, databaseCallbacks, requestedVersion));
492         return;
493     }
494
495     Vector<int64_t> objectStoreIds;
496     // FIXME: The transactionId needs to be piped in through IDBDatabaseBackendInterface::open().
497     RefPtr<IDBTransactionBackendInterface> transactionInterface = createTransaction(0, objectStoreIds, IDBTransaction::VERSION_CHANGE);
498     RefPtr<IDBTransactionBackendImpl> transaction = IDBTransactionBackendImpl::from(transactionInterface.get());
499
500     if (!transaction->scheduleTask(VersionChangeOperation::create(this, requestedVersion, callbacks, databaseCallbacks), VersionChangeAbortOperation::create(this, m_metadata.version, m_metadata.intVersion))) {
501         ASSERT_NOT_REACHED();
502     }
503     ASSERT(!m_pendingSecondHalfOpenWithVersion);
504     m_databaseCallbacksSet.add(databaseCallbacks);
505 }
506
507 void IDBDatabaseBackendImpl::openConnectionWithVersion(PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, int64_t version)
508 {
509     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
510     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
511     if (!m_pendingDeleteCalls.isEmpty() || m_runningVersionChangeTransaction) {
512         m_pendingOpenWithVersionCalls.append(PendingOpenWithVersionCall::create(callbacks, databaseCallbacks, version));
513         return;
514     }
515     if (m_metadata.id == InvalidId) {
516         if (openInternal())
517             ASSERT(m_metadata.intVersion == IDBDatabaseMetadata::NoIntVersion);
518         else {
519             callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error."));
520             return;
521         }
522     }
523     if (version > m_metadata.intVersion) {
524         runIntVersionChangeTransaction(version, callbacks, databaseCallbacks);
525         return;
526     }
527     if (version < m_metadata.intVersion) {
528         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))));
529         return;
530     }
531     ASSERT(version == m_metadata.intVersion);
532     m_databaseCallbacksSet.add(databaseCallbacks);
533     callbacks->onSuccess(this);
534 }
535
536 void IDBDatabaseBackendImpl::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
537 {
538     if (m_runningVersionChangeTransaction) {
539         m_pendingDeleteCalls.append(PendingDeleteCall::create(prpCallbacks));
540         return;
541     }
542     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
543     for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
544         // Front end ensures the event is not fired at connections that have closePending set.
545         (*it)->onVersionChange(NoStringVersion);
546     }
547     // FIXME: Only fire onBlocked if there are open connections after the
548     // VersionChangeEvents are received, not just set up to fire.
549     // https://bugs.webkit.org/show_bug.cgi?id=71130
550     if (connectionCount()) {
551         m_pendingDeleteCalls.append(PendingDeleteCall::create(callbacks));
552         callbacks->onBlocked();
553         return;
554     }
555     ASSERT(m_backingStore);
556     if (!m_backingStore->deleteDatabase(m_metadata.name)) {
557         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UnknownError, "Internal error."));
558         return;
559     }
560     m_metadata.version = NoStringVersion;
561     m_metadata.id = InvalidId;
562     m_metadata.intVersion = IDBDatabaseMetadata::NoIntVersion;
563     m_objectStores.clear();
564     callbacks->onSuccess();
565 }
566
567 void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
568 {
569     RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
570     ASSERT(m_databaseCallbacksSet.contains(callbacks));
571
572     m_databaseCallbacksSet.remove(callbacks);
573     if (m_pendingSecondHalfOpenWithVersion && m_pendingSecondHalfOpenWithVersion->databaseCallbacks() == callbacks) {
574         m_pendingSecondHalfOpenWithVersion->callbacks()->onError(IDBDatabaseError::create(IDBDatabaseException::AbortError, "The connection was closed."));
575         m_pendingSecondHalfOpenWithVersion.release();
576     }
577
578     if (connectionCount() > 1)
579         return;
580
581     // processPendingCalls allows the inspector to process a pending open call
582     // and call close, reentering IDBDatabaseBackendImpl::close. Then the
583     // backend would be removed both by the inspector closing its connection, and
584     // by the connection that first called close.
585     // To avoid that situation, don't proceed in case of reentrancy.
586     if (m_closingConnection)
587         return;
588     m_closingConnection = true;
589     processPendingCalls();
590
591     // FIXME: Add a test for the m_pendingOpenCalls and m_pendingOpenWithVersionCalls cases below.
592     if (!connectionCount() && !m_pendingOpenCalls.size() && !m_pendingOpenWithVersionCalls.size() && !m_pendingDeleteCalls.size()) {
593         TransactionSet transactions(m_transactions);
594         for (TransactionSet::const_iterator it = transactions.begin(); it != transactions.end(); ++it)
595             (*it)->abort();
596
597         ASSERT(m_transactions.isEmpty());
598
599         m_backingStore.clear();
600         // This check should only be false in tests.
601         if (m_factory)
602             m_factory->removeIDBDatabaseBackend(m_identifier);
603     }
604     m_closingConnection = false;
605 }
606
607 void IDBDatabaseBackendImpl::loadObjectStores()
608 {
609     Vector<int64_t> ids;
610     Vector<String> names;
611     Vector<IDBKeyPath> keyPaths;
612     Vector<bool> autoIncrementFlags;
613     Vector<int64_t> maxIndexIds;
614     Vector<IDBObjectStoreMetadata> objectStores = m_backingStore->getObjectStores(m_metadata.id);
615
616     for (size_t i = 0; i < objectStores.size(); i++)
617         m_objectStores.set(objectStores[i].id, IDBObjectStoreBackendImpl::create(this, objectStores[i]));
618 }
619
620 void IDBDatabaseBackendImpl::CreateObjectStoreAbortOperation::perform(IDBTransactionBackendImpl* transaction)
621 {
622     ASSERT(!transaction);
623     ASSERT(m_database->m_objectStores.contains(m_objectStore->id()));
624     m_database->m_objectStores.remove(m_objectStore->id());
625 }
626
627 void IDBDatabaseBackendImpl::DeleteObjectStoreAbortOperation::perform(IDBTransactionBackendImpl* transaction)
628 {
629     ASSERT(!transaction);
630     ASSERT(!m_database->m_objectStores.contains(m_objectStore->id()));
631     m_database->m_objectStores.set(m_objectStore->id(), m_objectStore);
632 }
633
634 void IDBDatabaseBackendImpl::VersionChangeAbortOperation::perform(IDBTransactionBackendImpl* transaction)
635 {
636     ASSERT(!transaction);
637     m_database->m_metadata.version = m_previousVersion;
638     m_database->m_metadata.intVersion = m_previousIntVersion;
639 }
640
641 } // namespace WebCore
642
643 #endif // ENABLE(INDEXED_DATABASE)