2 * Copyright (C) 2010 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 "modules/indexeddb/IDBTransaction.h"
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"
43 using blink::WebIDBDatabase;
47 IDBTransaction* IDBTransaction::create(ScriptState* scriptState, int64_t id, const Vector<String>& objectStoreNames, WebIDBTransactionMode mode, IDBDatabase* db)
49 IDBOpenDBRequest* openDBRequest = 0;
50 IDBTransaction* transaction = adoptRefCountedGarbageCollectedWillBeNoop(new IDBTransaction(scriptState, id, objectStoreNames, mode, db, openDBRequest, IDBDatabaseMetadata()));
51 transaction->suspendIfNeeded();
55 IDBTransaction* IDBTransaction::create(ScriptState* scriptState, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
57 IDBTransaction* transaction = adoptRefCountedGarbageCollectedWillBeNoop(new IDBTransaction(scriptState, id, Vector<String>(), WebIDBTransactionModeVersionChange, db, openDBRequest, previousMetadata));
58 transaction->suspendIfNeeded();
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())
66 , m_objectStoreNames(objectStoreNames)
67 , m_openDBRequest(openDBRequest)
70 , m_hasPendingActivity(true)
71 , m_contextStopped(false)
72 , m_previousMetadata(previousMetadata)
74 if (mode == WebIDBTransactionModeVersionChange) {
75 // Not active until the callback.
79 if (m_state == Active)
80 V8PerIsolateData::from(scriptState->isolate())->ensureIDBPendingTransactionMonitor()->addNewTransaction(*this);
81 m_database->transactionCreated(this);
84 IDBTransaction::~IDBTransaction()
86 ASSERT(m_state == Finished || m_contextStopped);
87 ASSERT(m_requestList.isEmpty() || m_contextStopped);
90 void IDBTransaction::trace(Visitor* visitor)
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);
102 void IDBTransaction::setError(PassRefPtrWillBeRawPtr<DOMError> error)
104 ASSERT(m_state != Finished);
107 // The first error to be set is the true cause of the
108 // transaction abort.
114 IDBObjectStore* IDBTransaction::objectStore(const String& name, ExceptionState& exceptionState)
116 if (m_state == Finished) {
117 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
121 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
122 if (it != m_objectStoreMap.end())
125 if (!isVersionChange() && !m_objectStoreNames.contains(name)) {
126 exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
130 int64_t objectStoreId = m_database->findObjectStoreId(name);
131 if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
132 ASSERT(isVersionChange());
133 exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
137 const IDBDatabaseMetadata& metadata = m_database->metadata();
139 IDBObjectStore* objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this);
140 objectStoreCreated(name, objectStore);
144 void IDBTransaction::objectStoreCreated(const String& name, IDBObjectStore* objectStore)
146 ASSERT(m_state != Finished);
147 m_objectStoreMap.set(name, objectStore);
148 if (isVersionChange())
149 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
152 void IDBTransaction::objectStoreDeleted(const String& name)
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);
166 void IDBTransaction::setActive(bool active)
168 ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to setActive(%s)", active ? "true" : "false");
169 if (m_state == Finishing)
171 ASSERT(active != (m_state == Active));
172 m_state = active ? Active : Inactive;
174 if (!active && m_requestList.isEmpty() && backendDB())
175 backendDB()->commit(m_id);
178 void IDBTransaction::abort(ExceptionState& exceptionState)
180 if (m_state == Finishing || m_state == Finished) {
181 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
187 if (m_contextStopped)
190 while (!m_requestList.isEmpty()) {
191 IDBRequest* request = *m_requestList.begin();
192 m_requestList.remove(request);
197 backendDB()->abort(m_id);
200 void IDBTransaction::registerRequest(IDBRequest* request)
203 ASSERT(m_state == Active);
204 m_requestList.add(request);
207 void IDBTransaction::unregisterRequest(IDBRequest* request)
210 // If we aborted the request, it will already have been removed.
211 m_requestList.remove(request);
214 void IDBTransaction::onAbort(PassRefPtrWillBeRawPtr<DOMError> prpError)
216 IDB_TRACE("IDBTransaction::onAbort");
217 if (m_contextStopped) {
218 m_database->transactionFinished(this);
222 RefPtrWillBeRawPtr<DOMError> error = prpError;
223 ASSERT(m_state != Finished);
225 if (m_state != Finishing) {
227 setError(error.release());
229 // Abort was not triggered by front-end, so outstanding requests must
231 while (!m_requestList.isEmpty()) {
232 IDBRequest* request = *m_requestList.begin();
233 m_requestList.remove(request);
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);
245 m_objectStoreCleanupMap.clear();
247 // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
248 enqueueEvent(Event::createBubble(EventTypeNames::abort));
250 m_database->transactionFinished(this);
253 void IDBTransaction::onComplete()
255 IDB_TRACE("IDBTransaction::onComplete");
256 if (m_contextStopped) {
257 m_database->transactionFinished(this);
261 ASSERT(m_state != Finished);
263 m_objectStoreCleanupMap.clear();
265 // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
266 enqueueEvent(Event::create(EventTypeNames::complete));
268 m_database->transactionFinished(this);
271 bool IDBTransaction::hasPendingActivity() const
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;
279 WebIDBTransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionState& exceptionState)
281 if (modeString == IndexedDBNames::readonly)
282 return WebIDBTransactionModeReadOnly;
283 if (modeString == IndexedDBNames::readwrite)
284 return WebIDBTransactionModeReadWrite;
286 exceptionState.throwTypeError("The mode provided ('" + modeString + "') is not one of 'readonly' or 'readwrite'.");
287 return WebIDBTransactionModeReadOnly;
290 const String& IDBTransaction::mode() const
293 case WebIDBTransactionModeReadOnly:
294 return IndexedDBNames::readonly;
296 case WebIDBTransactionModeReadWrite:
297 return IndexedDBNames::readwrite;
299 case WebIDBTransactionModeVersionChange:
300 return IndexedDBNames::versionchange;
303 ASSERT_NOT_REACHED();
304 return IndexedDBNames::readonly;
307 const AtomicString& IDBTransaction::interfaceName() const
309 return EventTargetNames::IDBTransaction;
312 ExecutionContext* IDBTransaction::executionContext() const
314 return ActiveDOMObject::executionContext();
317 bool IDBTransaction::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
319 IDB_TRACE("IDBTransaction::dispatchEvent");
320 if (m_contextStopped || !executionContext()) {
324 ASSERT(m_state != Finished);
325 ASSERT(m_hasPendingActivity);
326 ASSERT(executionContext());
327 ASSERT(event->target() == this);
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();
338 WillBeHeapVector<RefPtrWillBeMember<EventTarget> > targets;
339 targets.append(this);
340 targets.append(db());
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
347 if (m_openDBRequest) {
348 ASSERT(isVersionChange());
349 m_openDBRequest->transactionDidFinishAndDispatch();
351 m_hasPendingActivity = false;
355 void IDBTransaction::stop()
357 if (m_contextStopped)
360 m_contextStopped = true;
362 abort(IGNORE_EXCEPTION);
365 void IDBTransaction::enqueueEvent(PassRefPtrWillBeRawPtr<Event> event)
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())
371 EventQueue* eventQueue = executionContext()->eventQueue();
372 event->setTarget(this);
373 eventQueue->enqueueEvent(event);
376 WebIDBDatabase* IDBTransaction::backendDB() const
378 return m_database->backend();