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 "modules/indexeddb/IDBTransaction.h" 28 29 #include "bindings/core/v8/ExceptionState.h" 30 #include "bindings/core/v8/ExceptionStatePlaceholder.h" 31 #include "bindings/core/v8/V8PerIsolateData.h" 32 #include "core/dom/ExecutionContext.h" 33 #include "core/events/EventQueue.h" 34 #include "core/inspector/ScriptCallStack.h" 35 #include "modules/IndexedDBNames.h" 36 #include "modules/indexeddb/IDBDatabase.h" 37 #include "modules/indexeddb/IDBEventDispatcher.h" 38 #include "modules/indexeddb/IDBIndex.h" 39 #include "modules/indexeddb/IDBObjectStore.h" 40 #include "modules/indexeddb/IDBOpenDBRequest.h" 41 #include "modules/indexeddb/IDBTracing.h" 42 43 using blink::WebIDBDatabase; 44 45 namespace blink { 46 47 IDBTransaction* IDBTransaction::create(ScriptState* scriptState, int64_t id, const Vector<String>& objectStoreNames, WebIDBTransactionMode mode, IDBDatabase* db) 48 { 49 IDBOpenDBRequest* openDBRequest = 0; 50 IDBTransaction* transaction = adoptRefCountedGarbageCollectedWillBeNoop(new IDBTransaction(scriptState, id, objectStoreNames, mode, db, openDBRequest, IDBDatabaseMetadata())); 51 transaction->suspendIfNeeded(); 52 return transaction; 53 } 54 55 IDBTransaction* IDBTransaction::create(ScriptState* scriptState, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata) 56 { 57 IDBTransaction* transaction = adoptRefCountedGarbageCollectedWillBeNoop(new IDBTransaction(scriptState, id, Vector<String>(), WebIDBTransactionModeVersionChange, db, openDBRequest, previousMetadata)); 58 transaction->suspendIfNeeded(); 59 return transaction; 60 } 61 62 IDBTransaction::IDBTransaction(ScriptState* scriptState, int64_t id, const Vector<String>& objectStoreNames, WebIDBTransactionMode mode, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata) 63 : ActiveDOMObject(scriptState->executionContext()) 64 , m_id(id) 65 , m_database(db) 66 , m_objectStoreNames(objectStoreNames) 67 , m_openDBRequest(openDBRequest) 68 , m_mode(mode) 69 , m_state(Active) 70 , m_hasPendingActivity(true) 71 , m_contextStopped(false) 72 , m_previousMetadata(previousMetadata) 73 { 74 if (mode == WebIDBTransactionModeVersionChange) { 75 // Not active until the callback. 76 m_state = Inactive; 77 } 78 79 if (m_state == Active) 80 V8PerIsolateData::from(scriptState->isolate())->ensureIDBPendingTransactionMonitor()->addNewTransaction(*this); 81 m_database->transactionCreated(this); 82 } 83 84 IDBTransaction::~IDBTransaction() 85 { 86 ASSERT(m_state == Finished || m_contextStopped); 87 ASSERT(m_requestList.isEmpty() || m_contextStopped); 88 } 89 90 void IDBTransaction::trace(Visitor* visitor) 91 { 92 visitor->trace(m_database); 93 visitor->trace(m_openDBRequest); 94 visitor->trace(m_error); 95 visitor->trace(m_requestList); 96 visitor->trace(m_objectStoreMap); 97 visitor->trace(m_deletedObjectStores); 98 visitor->trace(m_objectStoreCleanupMap); 99 EventTargetWithInlineData::trace(visitor); 100 } 101 102 void IDBTransaction::setError(PassRefPtrWillBeRawPtr<DOMError> error) 103 { 104 ASSERT(m_state != Finished); 105 ASSERT(error); 106 107 // The first error to be set is the true cause of the 108 // transaction abort. 109 if (!m_error) { 110 m_error = error; 111 } 112 } 113 114 IDBObjectStore* IDBTransaction::objectStore(const String& name, ExceptionState& exceptionState) 115 { 116 if (m_state == Finished) { 117 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage); 118 return 0; 119 } 120 121 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); 122 if (it != m_objectStoreMap.end()) 123 return it->value; 124 125 if (!isVersionChange() && !m_objectStoreNames.contains(name)) { 126 exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); 127 return 0; 128 } 129 130 int64_t objectStoreId = m_database->findObjectStoreId(name); 131 if (objectStoreId == IDBObjectStoreMetadata::InvalidId) { 132 ASSERT(isVersionChange()); 133 exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage); 134 return 0; 135 } 136 137 const IDBDatabaseMetadata& metadata = m_database->metadata(); 138 139 IDBObjectStore* objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this); 140 objectStoreCreated(name, objectStore); 141 return objectStore; 142 } 143 144 void IDBTransaction::objectStoreCreated(const String& name, IDBObjectStore* objectStore) 145 { 146 ASSERT(m_state != Finished); 147 m_objectStoreMap.set(name, objectStore); 148 if (isVersionChange()) 149 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); 150 } 151 152 void IDBTransaction::objectStoreDeleted(const String& name) 153 { 154 ASSERT(m_state != Finished); 155 ASSERT(isVersionChange()); 156 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name); 157 if (it != m_objectStoreMap.end()) { 158 IDBObjectStore* objectStore = it->value; 159 m_objectStoreMap.remove(name); 160 objectStore->markDeleted(); 161 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata()); 162 m_deletedObjectStores.add(objectStore); 163 } 164 } 165 166 void IDBTransaction::setActive(bool active) 167 { 168 ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to setActive(%s)", active ? "true" : "false"); 169 if (m_state == Finishing) 170 return; 171 ASSERT(active != (m_state == Active)); 172 m_state = active ? Active : Inactive; 173 174 if (!active && m_requestList.isEmpty() && backendDB()) 175 backendDB()->commit(m_id); 176 } 177 178 void IDBTransaction::abort(ExceptionState& exceptionState) 179 { 180 if (m_state == Finishing || m_state == Finished) { 181 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage); 182 return; 183 } 184 185 m_state = Finishing; 186 187 if (m_contextStopped) 188 return; 189 190 while (!m_requestList.isEmpty()) { 191 IDBRequest* request = *m_requestList.begin(); 192 m_requestList.remove(request); 193 request->abort(); 194 } 195 196 if (backendDB()) 197 backendDB()->abort(m_id); 198 } 199 200 void IDBTransaction::registerRequest(IDBRequest* request) 201 { 202 ASSERT(request); 203 ASSERT(m_state == Active); 204 m_requestList.add(request); 205 } 206 207 void IDBTransaction::unregisterRequest(IDBRequest* request) 208 { 209 ASSERT(request); 210 // If we aborted the request, it will already have been removed. 211 m_requestList.remove(request); 212 } 213 214 void IDBTransaction::onAbort(PassRefPtrWillBeRawPtr<DOMError> prpError) 215 { 216 IDB_TRACE("IDBTransaction::onAbort"); 217 if (m_contextStopped) { 218 m_database->transactionFinished(this); 219 return; 220 } 221 222 RefPtrWillBeRawPtr<DOMError> error = prpError; 223 ASSERT(m_state != Finished); 224 225 if (m_state != Finishing) { 226 ASSERT(error.get()); 227 setError(error.release()); 228 229 // Abort was not triggered by front-end, so outstanding requests must 230 // be aborted now. 231 while (!m_requestList.isEmpty()) { 232 IDBRequest* request = *m_requestList.begin(); 233 m_requestList.remove(request); 234 request->abort(); 235 } 236 m_state = Finishing; 237 } 238 239 if (isVersionChange()) { 240 for (IDBObjectStoreMetadataMap::iterator it = m_objectStoreCleanupMap.begin(); it != m_objectStoreCleanupMap.end(); ++it) 241 it->key->setMetadata(it->value); 242 m_database->setMetadata(m_previousMetadata); 243 m_database->close(); 244 } 245 m_objectStoreCleanupMap.clear(); 246 247 // Enqueue events before notifying database, as database may close which enqueues more events and order matters. 248 enqueueEvent(Event::createBubble(EventTypeNames::abort)); 249 250 m_database->transactionFinished(this); 251 } 252 253 void IDBTransaction::onComplete() 254 { 255 IDB_TRACE("IDBTransaction::onComplete"); 256 if (m_contextStopped) { 257 m_database->transactionFinished(this); 258 return; 259 } 260 261 ASSERT(m_state != Finished); 262 m_state = Finishing; 263 m_objectStoreCleanupMap.clear(); 264 265 // Enqueue events before notifying database, as database may close which enqueues more events and order matters. 266 enqueueEvent(Event::create(EventTypeNames::complete)); 267 268 m_database->transactionFinished(this); 269 } 270 271 bool IDBTransaction::hasPendingActivity() const 272 { 273 // FIXME: In an ideal world, we should return true as long as anyone has a or can 274 // get a handle to us or any child request object and any of those have 275 // event listeners. This is in order to handle user generated events properly. 276 return m_hasPendingActivity && !m_contextStopped; 277 } 278 279 WebIDBTransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionState& exceptionState) 280 { 281 if (modeString == IndexedDBNames::readonly) 282 return WebIDBTransactionModeReadOnly; 283 if (modeString == IndexedDBNames::readwrite) 284 return WebIDBTransactionModeReadWrite; 285 286 exceptionState.throwTypeError("The mode provided ('" + modeString + "') is not one of 'readonly' or 'readwrite'."); 287 return WebIDBTransactionModeReadOnly; 288 } 289 290 const String& IDBTransaction::mode() const 291 { 292 switch (m_mode) { 293 case WebIDBTransactionModeReadOnly: 294 return IndexedDBNames::readonly; 295 296 case WebIDBTransactionModeReadWrite: 297 return IndexedDBNames::readwrite; 298 299 case WebIDBTransactionModeVersionChange: 300 return IndexedDBNames::versionchange; 301 } 302 303 ASSERT_NOT_REACHED(); 304 return IndexedDBNames::readonly; 305 } 306 307 const AtomicString& IDBTransaction::interfaceName() const 308 { 309 return EventTargetNames::IDBTransaction; 310 } 311 312 ExecutionContext* IDBTransaction::executionContext() const 313 { 314 return ActiveDOMObject::executionContext(); 315 } 316 317 bool IDBTransaction::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event) 318 { 319 IDB_TRACE("IDBTransaction::dispatchEvent"); 320 if (m_contextStopped || !executionContext()) { 321 m_state = Finished; 322 return false; 323 } 324 ASSERT(m_state != Finished); 325 ASSERT(m_hasPendingActivity); 326 ASSERT(executionContext()); 327 ASSERT(event->target() == this); 328 m_state = Finished; 329 330 // Break reference cycles. 331 for (IDBObjectStoreMap::iterator it = m_objectStoreMap.begin(); it != m_objectStoreMap.end(); ++it) 332 it->value->transactionFinished(); 333 m_objectStoreMap.clear(); 334 for (IDBObjectStoreSet::iterator it = m_deletedObjectStores.begin(); it != m_deletedObjectStores.end(); ++it) 335 (*it)->transactionFinished(); 336 m_deletedObjectStores.clear(); 337 338 WillBeHeapVector<RefPtrWillBeMember<EventTarget> > targets; 339 targets.append(this); 340 targets.append(db()); 341 342 // FIXME: When we allow custom event dispatching, this will probably need to change. 343 ASSERT(event->type() == EventTypeNames::complete || event->type() == EventTypeNames::abort); 344 bool returnValue = IDBEventDispatcher::dispatch(event.get(), targets); 345 // FIXME: Try to construct a test where |this| outlives openDBRequest and we 346 // get a crash. 347 if (m_openDBRequest) { 348 ASSERT(isVersionChange()); 349 m_openDBRequest->transactionDidFinishAndDispatch(); 350 } 351 m_hasPendingActivity = false; 352 return returnValue; 353 } 354 355 void IDBTransaction::stop() 356 { 357 if (m_contextStopped) 358 return; 359 360 m_contextStopped = true; 361 362 abort(IGNORE_EXCEPTION); 363 } 364 365 void IDBTransaction::enqueueEvent(PassRefPtrWillBeRawPtr<Event> event) 366 { 367 ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to enqueue an event of type %s.", event->type().utf8().data()); 368 if (m_contextStopped || !executionContext()) 369 return; 370 371 EventQueue* eventQueue = executionContext()->eventQueue(); 372 event->setTarget(this); 373 eventQueue->enqueueEvent(event); 374 } 375 376 WebIDBDatabase* IDBTransaction::backendDB() const 377 { 378 return m_database->backend(); 379 } 380 381 } // namespace blink 382