1/* 2 * Copyright (C) 2010 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 "IDBDatabase.h" 28 29#include "Document.h" 30#include "EventQueue.h" 31#include "IDBAny.h" 32#include "IDBDatabaseCallbacksImpl.h" 33#include "IDBDatabaseError.h" 34#include "IDBDatabaseException.h" 35#include "IDBEventDispatcher.h" 36#include "IDBFactoryBackendInterface.h" 37#include "IDBIndex.h" 38#include "IDBObjectStore.h" 39#include "IDBVersionChangeEvent.h" 40#include "IDBVersionChangeRequest.h" 41#include "IDBTransaction.h" 42#include "ScriptExecutionContext.h" 43#include <limits> 44 45#if ENABLE(INDEXED_DATABASE) 46 47namespace WebCore { 48 49PassRefPtr<IDBDatabase> IDBDatabase::create(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackendInterface> database) 50{ 51 return adoptRef(new IDBDatabase(context, database)); 52} 53 54IDBDatabase::IDBDatabase(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackendInterface> backend) 55 : ActiveDOMObject(context, this) 56 , m_backend(backend) 57 , m_noNewTransactions(false) 58 , m_stopped(false) 59{ 60 // We pass a reference of this object before it can be adopted. 61 relaxAdoptionRequirement(); 62 m_databaseCallbacks = IDBDatabaseCallbacksImpl::create(this); 63} 64 65IDBDatabase::~IDBDatabase() 66{ 67 m_databaseCallbacks->unregisterDatabase(this); 68} 69 70void IDBDatabase::setSetVersionTransaction(IDBTransaction* transaction) 71{ 72 m_setVersionTransaction = transaction; 73} 74 75PassRefPtr<IDBObjectStore> IDBDatabase::createObjectStore(const String& name, const OptionsObject& options, ExceptionCode& ec) 76{ 77 if (!m_setVersionTransaction) { 78 ec = IDBDatabaseException::NOT_ALLOWED_ERR; 79 return 0; 80 } 81 82 String keyPath; 83 options.getKeyString("keyPath", keyPath); 84 bool autoIncrement = false; 85 options.getKeyBool("autoIncrement", autoIncrement); 86 // FIXME: Look up evictable and pass that on as well. 87 88 RefPtr<IDBObjectStoreBackendInterface> objectStore = m_backend->createObjectStore(name, keyPath, autoIncrement, m_setVersionTransaction->backend(), ec); 89 if (!objectStore) { 90 ASSERT(ec); 91 return 0; 92 } 93 return IDBObjectStore::create(objectStore.release(), m_setVersionTransaction.get()); 94} 95 96void IDBDatabase::deleteObjectStore(const String& name, ExceptionCode& ec) 97{ 98 if (!m_setVersionTransaction) { 99 ec = IDBDatabaseException::NOT_ALLOWED_ERR; 100 return; 101 } 102 103 m_backend->deleteObjectStore(name, m_setVersionTransaction->backend(), ec); 104} 105 106PassRefPtr<IDBVersionChangeRequest> IDBDatabase::setVersion(ScriptExecutionContext* context, const String& version, ExceptionCode& ec) 107{ 108 RefPtr<IDBVersionChangeRequest> request = IDBVersionChangeRequest::create(context, IDBAny::create(this), version); 109 m_backend->setVersion(version, request, m_databaseCallbacks, ec); 110 return request; 111} 112 113PassRefPtr<IDBTransaction> IDBDatabase::transaction(ScriptExecutionContext* context, PassRefPtr<DOMStringList> prpStoreNames, unsigned short mode, ExceptionCode& ec) 114{ 115 RefPtr<DOMStringList> storeNames = prpStoreNames; 116 if (!storeNames) 117 storeNames = DOMStringList::create(); 118 119 if (mode != IDBTransaction::READ_WRITE && mode != IDBTransaction::READ_ONLY) { 120 // FIXME: May need to change when specced: http://www.w3.org/Bugs/Public/show_bug.cgi?id=11406 121 ec = IDBDatabaseException::CONSTRAINT_ERR; 122 return 0; 123 } 124 if (m_noNewTransactions) { 125 ec = IDBDatabaseException::NOT_ALLOWED_ERR; 126 return 0; 127 } 128 129 // We need to create a new transaction synchronously. Locks are acquired asynchronously. Operations 130 // can be queued against the transaction at any point. They will start executing as soon as the 131 // appropriate locks have been acquired. 132 // Also note that each backend object corresponds to exactly one IDBTransaction object. 133 RefPtr<IDBTransactionBackendInterface> transactionBackend = m_backend->transaction(storeNames.get(), mode, ec); 134 if (!transactionBackend) { 135 ASSERT(ec); 136 return 0; 137 } 138 RefPtr<IDBTransaction> transaction = IDBTransaction::create(context, transactionBackend, this); 139 transactionBackend->setCallbacks(transaction.get()); 140 return transaction.release(); 141} 142 143void IDBDatabase::close() 144{ 145 if (m_noNewTransactions) 146 return; 147 148 ASSERT(scriptExecutionContext()->isDocument()); 149 EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue(); 150 // Remove any pending versionchange events scheduled to fire on this 151 // connection. They would have been scheduled by the backend when another 152 // connection called setVersion, but the frontend connection is being 153 // closed before they could fire. 154 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { 155 bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get()); 156 ASSERT_UNUSED(removed, removed); 157 } 158 159 m_noNewTransactions = true; 160 m_backend->close(m_databaseCallbacks); 161} 162 163void IDBDatabase::onVersionChange(const String& version) 164{ 165 enqueueEvent(IDBVersionChangeEvent::create(version, eventNames().versionchangeEvent)); 166} 167 168bool IDBDatabase::hasPendingActivity() const 169{ 170 // FIXME: Try to find some way not to just leak this object until page navigation. 171 // FIXME: In an ideal world, we should return true as long as anyone has or can 172 // get a handle to us or any derivative transaction/request object and any 173 // of those have event listeners. This is in order to handle user generated 174 // events properly. 175 return !m_stopped || ActiveDOMObject::hasPendingActivity(); 176} 177 178void IDBDatabase::open() 179{ 180 m_backend->open(m_databaseCallbacks); 181} 182 183void IDBDatabase::enqueueEvent(PassRefPtr<Event> event) 184{ 185 ASSERT(scriptExecutionContext()->isDocument()); 186 EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue(); 187 event->setTarget(this); 188 eventQueue->enqueueEvent(event.get()); 189 m_enqueuedEvents.append(event); 190} 191 192bool IDBDatabase::dispatchEvent(PassRefPtr<Event> event) 193{ 194 ASSERT(event->type() == eventNames().versionchangeEvent); 195 for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) { 196 if (m_enqueuedEvents[i].get() == event.get()) 197 m_enqueuedEvents.remove(i); 198 } 199 return EventTarget::dispatchEvent(event.get()); 200} 201 202void IDBDatabase::stop() 203{ 204 // Stop fires at a deterministic time, so we need to call close in it. 205 close(); 206 207 m_stopped = true; 208} 209 210ScriptExecutionContext* IDBDatabase::scriptExecutionContext() const 211{ 212 return ActiveDOMObject::scriptExecutionContext(); 213} 214 215EventTargetData* IDBDatabase::eventTargetData() 216{ 217 return &m_eventTargetData; 218} 219 220EventTargetData* IDBDatabase::ensureEventTargetData() 221{ 222 return &m_eventTargetData; 223} 224 225} // namespace WebCore 226 227#endif // ENABLE(INDEXED_DATABASE) 228