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 47 namespace WebCore { 48 49 PassRefPtr<IDBDatabase> IDBDatabase::create(ScriptExecutionContext* context, PassRefPtr<IDBDatabaseBackendInterface> database) 50 { 51 return adoptRef(new IDBDatabase(context, database)); 52 } 53 54 IDBDatabase::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 65 IDBDatabase::~IDBDatabase() 66 { 67 m_databaseCallbacks->unregisterDatabase(this); 68 } 69 70 void IDBDatabase::setSetVersionTransaction(IDBTransaction* transaction) 71 { 72 m_setVersionTransaction = transaction; 73 } 74 75 PassRefPtr<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 96 void 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 106 PassRefPtr<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 113 PassRefPtr<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 143 void 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 163 void IDBDatabase::onVersionChange(const String& version) 164 { 165 enqueueEvent(IDBVersionChangeEvent::create(version, eventNames().versionchangeEvent)); 166 } 167 168 bool 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 178 void IDBDatabase::open() 179 { 180 m_backend->open(m_databaseCallbacks); 181 } 182 183 void 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 192 bool 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 202 void 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 210 ScriptExecutionContext* IDBDatabase::scriptExecutionContext() const 211 { 212 return ActiveDOMObject::scriptExecutionContext(); 213 } 214 215 EventTargetData* IDBDatabase::eventTargetData() 216 { 217 return &m_eventTargetData; 218 } 219 220 EventTargetData* IDBDatabase::ensureEventTargetData() 221 { 222 return &m_eventTargetData; 223 } 224 225 } // namespace WebCore 226 227 #endif // ENABLE(INDEXED_DATABASE) 228