5cccaa29a7fd6b56908b567838eb2c131bd73c83
[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 "CrossThreadTask.h"
32 #include "DOMStringList.h"
33 #include "IDBBackingStore.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBFactoryBackendImpl.h"
36 #include "IDBObjectStoreBackendImpl.h"
37 #include "IDBTransactionBackendImpl.h"
38 #include "IDBTransactionCoordinator.h"
39
40 namespace WebCore {
41
42 class IDBDatabaseBackendImpl::PendingOpenCall : public RefCounted<PendingOpenCall> {
43 public:
44     static PassRefPtr<PendingOpenCall> create(PassRefPtr<IDBCallbacks> callbacks)
45     {
46         return adoptRef(new PendingOpenCall(callbacks));
47     }
48     PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
49
50 private:
51     PendingOpenCall(PassRefPtr<IDBCallbacks> callbacks)
52         : m_callbacks(callbacks)
53     {
54     }
55     RefPtr<IDBCallbacks> m_callbacks;
56 };
57
58 class IDBDatabaseBackendImpl::PendingDeleteCall : public RefCounted<PendingDeleteCall> {
59 public:
60     static PassRefPtr<PendingDeleteCall> create(PassRefPtr<IDBCallbacks> callbacks)
61     {
62         return adoptRef(new PendingDeleteCall(callbacks));
63     }
64     PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
65
66 private:
67     PendingDeleteCall(PassRefPtr<IDBCallbacks> callbacks)
68         : m_callbacks(callbacks)
69     {
70     }
71     RefPtr<IDBCallbacks> m_callbacks;
72 };
73
74 class IDBDatabaseBackendImpl::PendingSetVersionCall : public RefCounted<PendingSetVersionCall> {
75 public:
76     static PassRefPtr<PendingSetVersionCall> create(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
77     {
78         return adoptRef(new PendingSetVersionCall(version, callbacks, databaseCallbacks));
79     }
80     String version() { return m_version; }
81     PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
82     PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks() { return m_databaseCallbacks; }
83
84 private:
85     PendingSetVersionCall(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
86         : m_version(version)
87         , m_callbacks(callbacks)
88         , m_databaseCallbacks(databaseCallbacks)
89     {
90     }
91     String m_version;
92     RefPtr<IDBCallbacks> m_callbacks;
93     RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
94 };
95
96 PassRefPtr<IDBDatabaseBackendImpl> IDBDatabaseBackendImpl::create(const String& name, IDBBackingStore* database, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
97 {
98     RefPtr<IDBDatabaseBackendImpl> backend = adoptRef(new IDBDatabaseBackendImpl(name, database, coordinator, factory, uniqueIdentifier));
99     if (!backend->openInternal())
100         return 0;
101     return backend.release();
102 }
103
104 IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBBackingStore* backingStore, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
105     : m_backingStore(backingStore)
106     , m_id(InvalidId)
107     , m_name(name)
108     , m_version("")
109     , m_identifier(uniqueIdentifier)
110     , m_factory(factory)
111     , m_transactionCoordinator(coordinator)
112     , m_pendingConnectionCount(0)
113 {
114     ASSERT(!m_name.isNull());
115 }
116
117 bool IDBDatabaseBackendImpl::openInternal()
118 {
119     bool success = m_backingStore->getIDBDatabaseMetaData(m_name, m_version, m_id);
120     ASSERT(success == (m_id != InvalidId));
121     if (success) {
122         loadObjectStores();
123         return true;
124     }
125     return m_backingStore->createIDBDatabaseMetaData(m_name, m_version, m_id);
126 }
127
128 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
129 {
130 }
131
132 PassRefPtr<IDBBackingStore> IDBDatabaseBackendImpl::backingStore() const
133 {
134     return m_backingStore;
135 }
136
137 IDBDatabaseMetadata IDBDatabaseBackendImpl::metadata() const
138 {
139     IDBDatabaseMetadata metadata(m_name, m_version);
140     for (ObjectStoreMap::const_iterator it = m_objectStores.begin(); it != m_objectStores.end(); ++it)
141         metadata.objectStores.set(it->first, it->second->metadata());
142     return metadata;
143 }
144
145 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const IDBKeyPath& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
146 {
147     ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
148     if (m_objectStores.contains(name)) {
149         ec = IDBDatabaseException::CONSTRAINT_ERR;
150         return 0;
151     }
152
153     RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(this, name, keyPath, autoIncrement);
154     ASSERT(objectStore->name() == name);
155
156     RefPtr<IDBDatabaseBackendImpl> database = this;
157     RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
158     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::createObjectStoreInternal, database, objectStore, transaction),
159                                    createCallbackTask(&IDBDatabaseBackendImpl::removeObjectStoreFromMap, database, objectStore))) {
160         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
161         return 0;
162     }
163
164     m_objectStores.set(name, objectStore);
165     return objectStore.release();
166 }
167
168 void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore,  PassRefPtr<IDBTransactionBackendInterface> transaction)
169 {
170     int64_t objectStoreId;
171
172     if (!database->m_backingStore->createObjectStore(database->id(), objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), objectStoreId)) {
173         transaction->abort();
174         return;
175     }
176
177     objectStore->setId(objectStoreId);
178     transaction->didCompleteTaskEvents();
179 }
180
181 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(const String& name)
182 {
183     return m_objectStores.get(name);
184 }
185
186 void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
187 {
188     ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
189     RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
190     if (!objectStore) {
191         ec = IDBDatabaseException::IDB_NOT_FOUND_ERR;
192         return;
193     }
194     RefPtr<IDBDatabaseBackendImpl> database = this;
195     RefPtr<IDBTransactionBackendInterface> transaction = transactionPtr;
196     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::deleteObjectStoreInternal, database, objectStore, transaction),
197                                    createCallbackTask(&IDBDatabaseBackendImpl::addObjectStoreToMap, database, objectStore))) {
198         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
199         return;
200     }
201     m_objectStores.remove(name);
202 }
203
204 void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
205 {
206     database->m_backingStore->deleteObjectStore(database->id(), objectStore->id());
207     transaction->didCompleteTaskEvents();
208 }
209
210 void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, ExceptionCode& ec)
211 {
212     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
213     RefPtr<IDBDatabaseCallbacks> databaseCallbacks = prpDatabaseCallbacks;
214     if (!m_databaseCallbacksSet.contains(databaseCallbacks)) {
215         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::IDB_ABORT_ERR, "Connection was closed before set version transaction was created"));
216         return;
217     }
218     for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
219         if (*it != databaseCallbacks)
220             (*it)->onVersionChange(version);
221     }
222     // FIXME: Only fire onBlocked if there are open connections after the
223     // VersionChangeEvents are received, not just set up to fire.
224     // https://bugs.webkit.org/show_bug.cgi?id=71130
225     if (connectionCount() > 1) {
226         callbacks->onBlocked();
227         RefPtr<PendingSetVersionCall> pendingSetVersionCall = PendingSetVersionCall::create(version, callbacks, databaseCallbacks);
228         m_pendingSetVersionCalls.append(pendingSetVersionCall);
229         return;
230     }
231     if (m_runningVersionChangeTransaction) {
232         RefPtr<PendingSetVersionCall> pendingSetVersionCall = PendingSetVersionCall::create(version, callbacks, databaseCallbacks);
233         m_pendingSetVersionCalls.append(pendingSetVersionCall);
234         return;
235     }
236
237     RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
238     RefPtr<IDBTransactionBackendInterface> transaction = this->transaction(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, ec);
239     ASSERT(!ec);
240
241     RefPtr<IDBDatabaseBackendImpl> database = this;
242     if (!transaction->scheduleTask(createCallbackTask(&IDBDatabaseBackendImpl::setVersionInternal, database, version, callbacks, transaction),
243                                    createCallbackTask(&IDBDatabaseBackendImpl::resetVersion, database, m_version))) {
244         ec = IDBDatabaseException::TRANSACTION_INACTIVE_ERR;
245     }
246 }
247
248 void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
249 {
250     int64_t databaseId = database->id();
251     database->m_version = version;
252     if (!database->m_backingStore->updateIDBDatabaseMetaData(databaseId, database->m_version)) {
253         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Error writing data to stable storage."));
254         transaction->abort();
255         return;
256     }
257     callbacks->onSuccess(transaction);
258 }
259
260 void IDBDatabaseBackendImpl::transactionStarted(PassRefPtr<IDBTransactionBackendInterface> prpTransaction)
261 {
262     RefPtr<IDBTransactionBackendInterface> transaction = prpTransaction;
263     if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
264         ASSERT(!m_runningVersionChangeTransaction);
265         m_runningVersionChangeTransaction = transaction;
266     }
267 }
268
269 void IDBDatabaseBackendImpl::transactionFinished(PassRefPtr<IDBTransactionBackendInterface> prpTransaction)
270 {
271     RefPtr<IDBTransactionBackendInterface> transaction = prpTransaction;
272     ASSERT(m_transactions.contains(transaction.get()));
273     m_transactions.remove(transaction.get());
274     if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
275         ASSERT(transaction.get() == m_runningVersionChangeTransaction.get());
276         m_runningVersionChangeTransaction.clear();
277         processPendingCalls();
278     }
279 }
280
281 int32_t IDBDatabaseBackendImpl::connectionCount()
282 {
283     return m_databaseCallbacksSet.size() + m_pendingConnectionCount;
284 }
285
286 void IDBDatabaseBackendImpl::processPendingCalls()
287 {
288     ASSERT(connectionCount() <= 1);
289
290     // Pending calls may be requeued or aborted
291     Deque<RefPtr<PendingSetVersionCall> > pendingSetVersionCalls;
292     m_pendingSetVersionCalls.swap(pendingSetVersionCalls);
293     while (!pendingSetVersionCalls.isEmpty()) {
294         ExceptionCode ec = 0;
295         RefPtr<PendingSetVersionCall> pendingSetVersionCall = pendingSetVersionCalls.takeFirst();
296         setVersion(pendingSetVersionCall->version(), pendingSetVersionCall->callbacks(), pendingSetVersionCall->databaseCallbacks(), ec);
297         ASSERT(!ec);
298     }
299
300     // If there were any pending set version calls, we better have started one.
301     ASSERT(m_pendingSetVersionCalls.isEmpty() || m_runningVersionChangeTransaction);
302
303     // m_pendingSetVersionCalls is non-empty in two cases:
304     // 1) When two versionchange transactions are requested while another
305     //    version change transaction is running.
306     // 2) When three versionchange transactions are requested in a row, before
307     //    any of their event handlers are run.
308     // Note that this check is only an optimization to reduce queue-churn and
309     // not necessary for correctness; deleteDatabase and openConnection will
310     // requeue their calls if this condition is true.
311     if (m_runningVersionChangeTransaction || !m_pendingSetVersionCalls.isEmpty())
312         return;
313
314     // Pending calls may be requeued.
315     Deque<RefPtr<PendingDeleteCall> > pendingDeleteCalls;
316     m_pendingDeleteCalls.swap(pendingDeleteCalls);
317     while (!pendingDeleteCalls.isEmpty()) {
318         RefPtr<PendingDeleteCall> pendingDeleteCall = pendingDeleteCalls.takeFirst();
319         deleteDatabase(pendingDeleteCall->callbacks());
320     }
321
322     // This check is also not really needed, openConnection would just requeue its calls.
323     if (m_runningVersionChangeTransaction || !m_pendingSetVersionCalls.isEmpty() || !m_pendingDeleteCalls.isEmpty())
324         return;
325
326     // Given the check above, it appears that calls cannot be requeued by
327     // openConnection, but use a different queue for iteration to be safe.
328     Deque<RefPtr<PendingOpenCall> > pendingOpenCalls;
329     m_pendingOpenCalls.swap(pendingOpenCalls);
330     while (!pendingOpenCalls.isEmpty()) {
331         RefPtr<PendingOpenCall> pendingOpenCall = pendingOpenCalls.takeFirst();
332         openConnection(pendingOpenCall->callbacks());
333     }
334     ASSERT(m_pendingOpenCalls.isEmpty());
335 }
336
337 PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode& ec)
338 {
339     for (size_t i = 0; i < objectStoreNames->length(); ++i) {
340         if (!m_objectStores.contains(objectStoreNames->item(i))) {
341             ec = IDBDatabaseException::IDB_NOT_FOUND_ERR;
342             return 0;
343         }
344     }
345
346     RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames, mode, this);
347     m_transactions.add(transaction.get());
348     return transaction.release();
349 }
350
351 void IDBDatabaseBackendImpl::registerFrontendCallbacks(PassRefPtr<IDBDatabaseCallbacks> callbacks)
352 {
353     ASSERT(m_backingStore.get());
354     ASSERT(m_pendingConnectionCount);
355     --m_pendingConnectionCount;
356     m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(callbacks));
357 }
358
359 void IDBDatabaseBackendImpl::openConnection(PassRefPtr<IDBCallbacks> callbacks)
360 {
361     ASSERT(m_backingStore.get());
362     if (!m_pendingDeleteCalls.isEmpty() || m_runningVersionChangeTransaction || !m_pendingSetVersionCalls.isEmpty())
363         m_pendingOpenCalls.append(PendingOpenCall::create(callbacks));
364     else {
365         if (m_id == InvalidId && !openInternal())
366             callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
367         else {
368             ++m_pendingConnectionCount;
369             callbacks->onSuccess(this);
370         }
371     }
372 }
373
374 void IDBDatabaseBackendImpl::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
375 {
376     if (m_runningVersionChangeTransaction || !m_pendingSetVersionCalls.isEmpty()) {
377         m_pendingDeleteCalls.append(PendingDeleteCall::create(prpCallbacks));
378         return;
379     }
380     RefPtr<IDBCallbacks> callbacks = prpCallbacks;
381     // FIXME: Only fire onVersionChange if there the connection isn't in
382     // the process of closing.
383     // https://bugs.webkit.org/show_bug.cgi?id=71129
384     for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it)
385         (*it)->onVersionChange("");
386     // FIXME: Only fire onBlocked if there are open connections after the
387     // VersionChangeEvents are received, not just set up to fire.
388     // https://bugs.webkit.org/show_bug.cgi?id=71130
389     if (!m_databaseCallbacksSet.isEmpty()) {
390         m_pendingDeleteCalls.append(PendingDeleteCall::create(callbacks));
391         callbacks->onBlocked();
392         return;
393     }
394     if (!m_backingStore->deleteDatabase(m_name)) {
395         callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
396         return;
397     }
398     m_version = "";
399     m_id = InvalidId;
400     m_objectStores.clear();
401     callbacks->onSuccess(SerializedScriptValue::nullValue());
402 }
403
404 void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
405 {
406     RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
407     ASSERT(m_databaseCallbacksSet.contains(callbacks));
408     m_databaseCallbacksSet.remove(callbacks);
409     if (connectionCount() > 1)
410         return;
411
412     processPendingCalls();
413
414     if (!connectionCount()) {
415         TransactionSet transactions(m_transactions);
416         for (TransactionSet::const_iterator it = transactions.begin(); it != transactions.end(); ++it)
417             (*it)->abort();
418         ASSERT(m_transactions.isEmpty());
419
420         m_backingStore.clear();
421         // This check should only be false in tests.
422         if (m_factory)
423             m_factory->removeIDBDatabaseBackend(m_identifier);
424     }
425 }
426
427 void IDBDatabaseBackendImpl::loadObjectStores()
428 {
429     Vector<int64_t> ids;
430     Vector<String> names;
431     Vector<IDBKeyPath> keyPaths;
432     Vector<bool> autoIncrementFlags;
433     m_backingStore->getObjectStores(m_id, ids, names, keyPaths, autoIncrementFlags);
434
435     ASSERT(names.size() == ids.size());
436     ASSERT(keyPaths.size() == ids.size());
437     ASSERT(autoIncrementFlags.size() == ids.size());
438
439     for (size_t i = 0; i < ids.size(); i++)
440         m_objectStores.set(names[i], IDBObjectStoreBackendImpl::create(this, ids[i], names[i], keyPaths[i], autoIncrementFlags[i]));
441 }
442
443 void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> prpObjectStore)
444 {
445     RefPtr<IDBObjectStoreBackendImpl> objectStore = prpObjectStore;
446     ASSERT(database->m_objectStores.contains(objectStore->name()));
447     database->m_objectStores.remove(objectStore->name());
448 }
449
450 void IDBDatabaseBackendImpl::addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
451 {
452     RefPtr<IDBObjectStoreBackendImpl> objectStorePtr = objectStore;
453     ASSERT(!database->m_objectStores.contains(objectStorePtr->name()));
454     database->m_objectStores.set(objectStorePtr->name(), objectStorePtr);
455 }
456
457 void IDBDatabaseBackendImpl::resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version)
458 {
459     database->m_version = version;
460 }
461
462
463 } // namespace WebCore
464
465 #endif // ENABLE(INDEXED_DATABASE)