2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "IDBDatabaseBackendImpl.h"
29 #if ENABLE(INDEXED_DATABASE)
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"
42 class IDBDatabaseBackendImpl::PendingOpenCall : public RefCounted<PendingOpenCall> {
44 static PassRefPtr<PendingOpenCall> create(PassRefPtr<IDBCallbacks> callbacks)
46 return adoptRef(new PendingOpenCall(callbacks));
48 PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
51 PendingOpenCall(PassRefPtr<IDBCallbacks> callbacks)
52 : m_callbacks(callbacks)
55 RefPtr<IDBCallbacks> m_callbacks;
58 class IDBDatabaseBackendImpl::PendingDeleteCall : public RefCounted<PendingDeleteCall> {
60 static PassRefPtr<PendingDeleteCall> create(PassRefPtr<IDBCallbacks> callbacks)
62 return adoptRef(new PendingDeleteCall(callbacks));
64 PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
67 PendingDeleteCall(PassRefPtr<IDBCallbacks> callbacks)
68 : m_callbacks(callbacks)
71 RefPtr<IDBCallbacks> m_callbacks;
74 class IDBDatabaseBackendImpl::PendingSetVersionCall : public RefCounted<PendingSetVersionCall> {
76 static PassRefPtr<PendingSetVersionCall> create(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
78 return adoptRef(new PendingSetVersionCall(version, callbacks, databaseCallbacks));
80 String version() { return m_version; }
81 PassRefPtr<IDBCallbacks> callbacks() { return m_callbacks; }
82 PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks() { return m_databaseCallbacks; }
85 PendingSetVersionCall(const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBDatabaseCallbacks> databaseCallbacks)
87 , m_callbacks(callbacks)
88 , m_databaseCallbacks(databaseCallbacks)
92 RefPtr<IDBCallbacks> m_callbacks;
93 RefPtr<IDBDatabaseCallbacks> m_databaseCallbacks;
96 PassRefPtr<IDBDatabaseBackendImpl> IDBDatabaseBackendImpl::create(const String& name, IDBBackingStore* database, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
98 RefPtr<IDBDatabaseBackendImpl> backend = adoptRef(new IDBDatabaseBackendImpl(name, database, coordinator, factory, uniqueIdentifier));
99 if (!backend->openInternal())
101 return backend.release();
104 IDBDatabaseBackendImpl::IDBDatabaseBackendImpl(const String& name, IDBBackingStore* backingStore, IDBTransactionCoordinator* coordinator, IDBFactoryBackendImpl* factory, const String& uniqueIdentifier)
105 : m_backingStore(backingStore)
109 , m_identifier(uniqueIdentifier)
111 , m_transactionCoordinator(coordinator)
112 , m_pendingConnectionCount(0)
114 ASSERT(!m_name.isNull());
117 bool IDBDatabaseBackendImpl::openInternal()
119 bool success = m_backingStore->getIDBDatabaseMetaData(m_name, m_version, m_id);
120 ASSERT(success == (m_id != InvalidId));
125 return m_backingStore->createIDBDatabaseMetaData(m_name, m_version, m_id);
128 IDBDatabaseBackendImpl::~IDBDatabaseBackendImpl()
132 PassRefPtr<IDBBackingStore> IDBDatabaseBackendImpl::backingStore() const
134 return m_backingStore;
137 IDBDatabaseMetadata IDBDatabaseBackendImpl::metadata() const
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());
145 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::createObjectStore(const String& name, const IDBKeyPath& keyPath, bool autoIncrement, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
147 ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
148 if (m_objectStores.contains(name)) {
149 ec = IDBDatabaseException::CONSTRAINT_ERR;
153 RefPtr<IDBObjectStoreBackendImpl> objectStore = IDBObjectStoreBackendImpl::create(this, name, keyPath, autoIncrement);
154 ASSERT(objectStore->name() == name);
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;
164 m_objectStores.set(name, objectStore);
165 return objectStore.release();
168 void IDBDatabaseBackendImpl::createObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
170 int64_t objectStoreId;
172 if (!database->m_backingStore->createObjectStore(database->id(), objectStore->name(), objectStore->keyPath(), objectStore->autoIncrement(), objectStoreId)) {
173 transaction->abort();
177 objectStore->setId(objectStoreId);
178 transaction->didCompleteTaskEvents();
181 PassRefPtr<IDBObjectStoreBackendInterface> IDBDatabaseBackendImpl::objectStore(const String& name)
183 return m_objectStores.get(name);
186 void IDBDatabaseBackendImpl::deleteObjectStore(const String& name, IDBTransactionBackendInterface* transactionPtr, ExceptionCode& ec)
188 ASSERT(transactionPtr->mode() == IDBTransaction::VERSION_CHANGE);
189 RefPtr<IDBObjectStoreBackendImpl> objectStore = m_objectStores.get(name);
191 ec = IDBDatabaseException::IDB_NOT_FOUND_ERR;
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;
201 m_objectStores.remove(name);
204 void IDBDatabaseBackendImpl::deleteObjectStoreInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore, PassRefPtr<IDBTransactionBackendInterface> transaction)
206 database->m_backingStore->deleteObjectStore(database->id(), objectStore->id());
207 transaction->didCompleteTaskEvents();
210 void IDBDatabaseBackendImpl::setVersion(const String& version, PassRefPtr<IDBCallbacks> prpCallbacks, PassRefPtr<IDBDatabaseCallbacks> prpDatabaseCallbacks, ExceptionCode& ec)
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"));
218 for (DatabaseCallbacksSet::const_iterator it = m_databaseCallbacksSet.begin(); it != m_databaseCallbacksSet.end(); ++it) {
219 if (*it != databaseCallbacks)
220 (*it)->onVersionChange(version);
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);
231 if (m_runningVersionChangeTransaction) {
232 RefPtr<PendingSetVersionCall> pendingSetVersionCall = PendingSetVersionCall::create(version, callbacks, databaseCallbacks);
233 m_pendingSetVersionCalls.append(pendingSetVersionCall);
237 RefPtr<DOMStringList> objectStoreNames = DOMStringList::create();
238 RefPtr<IDBTransactionBackendInterface> transaction = this->transaction(objectStoreNames.get(), IDBTransaction::VERSION_CHANGE, ec);
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;
248 void IDBDatabaseBackendImpl::setVersionInternal(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version, PassRefPtr<IDBCallbacks> callbacks, PassRefPtr<IDBTransactionBackendInterface> transaction)
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();
257 callbacks->onSuccess(transaction);
260 void IDBDatabaseBackendImpl::transactionStarted(PassRefPtr<IDBTransactionBackendInterface> prpTransaction)
262 RefPtr<IDBTransactionBackendInterface> transaction = prpTransaction;
263 if (transaction->mode() == IDBTransaction::VERSION_CHANGE) {
264 ASSERT(!m_runningVersionChangeTransaction);
265 m_runningVersionChangeTransaction = transaction;
269 void IDBDatabaseBackendImpl::transactionFinished(PassRefPtr<IDBTransactionBackendInterface> prpTransaction)
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();
281 int32_t IDBDatabaseBackendImpl::connectionCount()
283 return m_databaseCallbacksSet.size() + m_pendingConnectionCount;
286 void IDBDatabaseBackendImpl::processPendingCalls()
288 ASSERT(connectionCount() <= 1);
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);
300 // If there were any pending set version calls, we better have started one.
301 ASSERT(m_pendingSetVersionCalls.isEmpty() || m_runningVersionChangeTransaction);
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())
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());
322 // This check is also not really needed, openConnection would just requeue its calls.
323 if (m_runningVersionChangeTransaction || !m_pendingSetVersionCalls.isEmpty() || !m_pendingDeleteCalls.isEmpty())
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());
334 ASSERT(m_pendingOpenCalls.isEmpty());
337 PassRefPtr<IDBTransactionBackendInterface> IDBDatabaseBackendImpl::transaction(DOMStringList* objectStoreNames, unsigned short mode, ExceptionCode& ec)
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;
346 RefPtr<IDBTransactionBackendInterface> transaction = IDBTransactionBackendImpl::create(objectStoreNames, mode, this);
347 m_transactions.add(transaction.get());
348 return transaction.release();
351 void IDBDatabaseBackendImpl::registerFrontendCallbacks(PassRefPtr<IDBDatabaseCallbacks> callbacks)
353 ASSERT(m_backingStore.get());
354 ASSERT(m_pendingConnectionCount);
355 --m_pendingConnectionCount;
356 m_databaseCallbacksSet.add(RefPtr<IDBDatabaseCallbacks>(callbacks));
359 void IDBDatabaseBackendImpl::openConnection(PassRefPtr<IDBCallbacks> callbacks)
361 ASSERT(m_backingStore.get());
362 if (!m_pendingDeleteCalls.isEmpty() || m_runningVersionChangeTransaction || !m_pendingSetVersionCalls.isEmpty())
363 m_pendingOpenCalls.append(PendingOpenCall::create(callbacks));
365 if (m_id == InvalidId && !openInternal())
366 callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
368 ++m_pendingConnectionCount;
369 callbacks->onSuccess(this);
374 void IDBDatabaseBackendImpl::deleteDatabase(PassRefPtr<IDBCallbacks> prpCallbacks)
376 if (m_runningVersionChangeTransaction || !m_pendingSetVersionCalls.isEmpty()) {
377 m_pendingDeleteCalls.append(PendingDeleteCall::create(prpCallbacks));
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();
394 if (!m_backingStore->deleteDatabase(m_name)) {
395 callbacks->onError(IDBDatabaseError::create(IDBDatabaseException::UNKNOWN_ERR, "Internal error."));
400 m_objectStores.clear();
401 callbacks->onSuccess(SerializedScriptValue::nullValue());
404 void IDBDatabaseBackendImpl::close(PassRefPtr<IDBDatabaseCallbacks> prpCallbacks)
406 RefPtr<IDBDatabaseCallbacks> callbacks = prpCallbacks;
407 ASSERT(m_databaseCallbacksSet.contains(callbacks));
408 m_databaseCallbacksSet.remove(callbacks);
409 if (connectionCount() > 1)
412 processPendingCalls();
414 if (!connectionCount()) {
415 TransactionSet transactions(m_transactions);
416 for (TransactionSet::const_iterator it = transactions.begin(); it != transactions.end(); ++it)
418 ASSERT(m_transactions.isEmpty());
420 m_backingStore.clear();
421 // This check should only be false in tests.
423 m_factory->removeIDBDatabaseBackend(m_identifier);
427 void IDBDatabaseBackendImpl::loadObjectStores()
430 Vector<String> names;
431 Vector<IDBKeyPath> keyPaths;
432 Vector<bool> autoIncrementFlags;
433 m_backingStore->getObjectStores(m_id, ids, names, keyPaths, autoIncrementFlags);
435 ASSERT(names.size() == ids.size());
436 ASSERT(keyPaths.size() == ids.size());
437 ASSERT(autoIncrementFlags.size() == ids.size());
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]));
443 void IDBDatabaseBackendImpl::removeObjectStoreFromMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> prpObjectStore)
445 RefPtr<IDBObjectStoreBackendImpl> objectStore = prpObjectStore;
446 ASSERT(database->m_objectStores.contains(objectStore->name()));
447 database->m_objectStores.remove(objectStore->name());
450 void IDBDatabaseBackendImpl::addObjectStoreToMap(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, PassRefPtr<IDBObjectStoreBackendImpl> objectStore)
452 RefPtr<IDBObjectStoreBackendImpl> objectStorePtr = objectStore;
453 ASSERT(!database->m_objectStores.contains(objectStorePtr->name()));
454 database->m_objectStores.set(objectStorePtr->name(), objectStorePtr);
457 void IDBDatabaseBackendImpl::resetVersion(ScriptExecutionContext*, PassRefPtr<IDBDatabaseBackendImpl> database, const String& version)
459 database->m_version = version;
463 } // namespace WebCore
465 #endif // ENABLE(INDEXED_DATABASE)