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/v8/ExceptionState.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "core/dom/DOMError.h"
32 #include "core/dom/ExecutionContext.h"
33 #include "core/events/EventQueue.h"
34 #include "core/inspector/ScriptCallStack.h"
35 #include "modules/indexeddb/IDBDatabase.h"
36 #include "modules/indexeddb/IDBEventDispatcher.h"
37 #include "modules/indexeddb/IDBIndex.h"
38 #include "modules/indexeddb/IDBObjectStore.h"
39 #include "modules/indexeddb/IDBOpenDBRequest.h"
40 #include "modules/indexeddb/IDBPendingTransactionMonitor.h"
41 #include "modules/indexeddb/IDBTracing.h"
45 PassRefPtr<IDBTransaction> IDBTransaction::create(ExecutionContext* context, int64_t id, const Vector<String>& objectStoreNames, IndexedDB::TransactionMode mode, IDBDatabase* db)
47 IDBOpenDBRequest* openDBRequest = 0;
48 RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, id, objectStoreNames, mode, db, openDBRequest, IDBDatabaseMetadata())));
49 transaction->suspendIfNeeded();
50 return transaction.release();
53 PassRefPtr<IDBTransaction> IDBTransaction::create(ExecutionContext* context, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
55 RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, id, Vector<String>(), IndexedDB::TransactionVersionChange, db, openDBRequest, previousMetadata)));
56 transaction->suspendIfNeeded();
57 return transaction.release();
60 const AtomicString& IDBTransaction::modeReadOnly()
62 DEFINE_STATIC_LOCAL(AtomicString, readonly, ("readonly", AtomicString::ConstructFromLiteral));
66 const AtomicString& IDBTransaction::modeReadWrite()
68 DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("readwrite", AtomicString::ConstructFromLiteral));
72 const AtomicString& IDBTransaction::modeVersionChange()
74 DEFINE_STATIC_LOCAL(AtomicString, versionchange, ("versionchange", AtomicString::ConstructFromLiteral));
78 const AtomicString& IDBTransaction::modeReadOnlyLegacy()
80 DEFINE_STATIC_LOCAL(AtomicString, readonly, ("0", AtomicString::ConstructFromLiteral));
84 const AtomicString& IDBTransaction::modeReadWriteLegacy()
86 DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("1", AtomicString::ConstructFromLiteral));
91 IDBTransaction::IDBTransaction(ExecutionContext* context, int64_t id, const Vector<String>& objectStoreNames, IndexedDB::TransactionMode mode, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
92 : ActiveDOMObject(context)
95 , m_objectStoreNames(objectStoreNames)
96 , m_openDBRequest(openDBRequest)
99 , m_hasPendingActivity(true)
100 , m_contextStopped(false)
101 , m_previousMetadata(previousMetadata)
103 ScriptWrappable::init(this);
104 if (mode == IndexedDB::TransactionVersionChange) {
105 // Not active until the callback.
109 // We pass a reference of this object before it can be adopted.
110 relaxAdoptionRequirement();
111 if (m_state == Active)
112 IDBPendingTransactionMonitor::addNewTransaction(this);
113 m_database->transactionCreated(this);
116 IDBTransaction::~IDBTransaction()
118 ASSERT(m_state == Finished || m_contextStopped);
119 ASSERT(m_requestList.isEmpty() || m_contextStopped);
122 const String& IDBTransaction::mode() const
124 return modeToString(m_mode);
127 void IDBTransaction::setError(PassRefPtr<DOMError> error)
129 ASSERT(m_state != Finished);
132 // The first error to be set is the true cause of the
133 // transaction abort.
139 PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, ExceptionState& es)
141 if (m_state == Finished) {
142 es.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
146 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
147 if (it != m_objectStoreMap.end())
150 if (!isVersionChange() && !m_objectStoreNames.contains(name)) {
151 es.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
155 int64_t objectStoreId = m_database->findObjectStoreId(name);
156 if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
157 ASSERT(isVersionChange());
158 es.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
162 const IDBDatabaseMetadata& metadata = m_database->metadata();
164 RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this);
165 objectStoreCreated(name, objectStore);
166 return objectStore.release();
169 void IDBTransaction::objectStoreCreated(const String& name, PassRefPtr<IDBObjectStore> prpObjectStore)
171 ASSERT(m_state != Finished);
172 RefPtr<IDBObjectStore> objectStore = prpObjectStore;
173 m_objectStoreMap.set(name, objectStore);
174 if (isVersionChange())
175 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
178 void IDBTransaction::objectStoreDeleted(const String& name)
180 ASSERT(m_state != Finished);
181 ASSERT(isVersionChange());
182 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
183 if (it != m_objectStoreMap.end()) {
184 RefPtr<IDBObjectStore> objectStore = it->value;
185 m_objectStoreMap.remove(name);
186 objectStore->markDeleted();
187 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
188 m_deletedObjectStores.add(objectStore);
192 void IDBTransaction::setActive(bool active)
194 ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to setActive(%s)", active ? "true" : "false");
195 if (m_state == Finishing)
197 ASSERT(active != (m_state == Active));
198 m_state = active ? Active : Inactive;
200 if (!active && m_requestList.isEmpty())
201 backendDB()->commit(m_id);
204 void IDBTransaction::abort(ExceptionState& es)
206 if (m_state == Finishing || m_state == Finished) {
207 es.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
213 if (!m_contextStopped) {
214 while (!m_requestList.isEmpty()) {
215 RefPtr<IDBRequest> request = *m_requestList.begin();
216 m_requestList.remove(request);
221 RefPtr<IDBTransaction> selfRef = this;
222 backendDB()->abort(m_id);
225 void IDBTransaction::registerRequest(IDBRequest* request)
228 ASSERT(m_state == Active);
229 m_requestList.add(request);
232 void IDBTransaction::unregisterRequest(IDBRequest* request)
235 // If we aborted the request, it will already have been removed.
236 m_requestList.remove(request);
239 void IDBTransaction::onAbort(PassRefPtr<DOMError> prpError)
241 IDB_TRACE("IDBTransaction::onAbort");
242 RefPtr<DOMError> error = prpError;
243 ASSERT(m_state != Finished);
245 if (m_state != Finishing) {
247 setError(error.release());
249 // Abort was not triggered by front-end, so outstanding requests must
251 while (!m_requestList.isEmpty()) {
252 RefPtr<IDBRequest> request = *m_requestList.begin();
253 m_requestList.remove(request);
259 if (isVersionChange()) {
260 for (IDBObjectStoreMetadataMap::iterator it = m_objectStoreCleanupMap.begin(); it != m_objectStoreCleanupMap.end(); ++it)
261 it->key->setMetadata(it->value);
262 m_database->setMetadata(m_previousMetadata);
265 m_objectStoreCleanupMap.clear();
267 // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
268 enqueueEvent(Event::createBubble(EventTypeNames::abort));
270 // If script has stopped and GC has completed, database may have last reference to this object.
271 RefPtr<IDBTransaction> protect(this);
272 m_database->transactionFinished(this);
275 void IDBTransaction::onComplete()
277 IDB_TRACE("IDBTransaction::onComplete");
278 ASSERT(m_state != Finished);
280 m_objectStoreCleanupMap.clear();
282 // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
283 enqueueEvent(Event::create(EventTypeNames::complete));
285 // If script has stopped and GC has completed, database may have last reference to this object.
286 RefPtr<IDBTransaction> protect(this);
287 m_database->transactionFinished(this);
290 bool IDBTransaction::hasPendingActivity() const
292 // FIXME: In an ideal world, we should return true as long as anyone has a or can
293 // get a handle to us or any child request object and any of those have
294 // event listeners. This is in order to handle user generated events properly.
295 return m_hasPendingActivity && !m_contextStopped;
298 IndexedDB::TransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionState& es)
300 if (modeString.isNull()
301 || modeString == IDBTransaction::modeReadOnly())
302 return IndexedDB::TransactionReadOnly;
303 if (modeString == IDBTransaction::modeReadWrite())
304 return IndexedDB::TransactionReadWrite;
306 es.throwUninformativeAndGenericTypeError();
307 return IndexedDB::TransactionReadOnly;
310 const AtomicString& IDBTransaction::modeToString(IndexedDB::TransactionMode mode)
313 case IndexedDB::TransactionReadOnly:
314 return IDBTransaction::modeReadOnly();
317 case IndexedDB::TransactionReadWrite:
318 return IDBTransaction::modeReadWrite();
321 case IndexedDB::TransactionVersionChange:
322 return IDBTransaction::modeVersionChange();
326 ASSERT_NOT_REACHED();
327 return IDBTransaction::modeReadOnly();
330 const AtomicString& IDBTransaction::interfaceName() const
332 return EventTargetNames::IDBTransaction;
335 ExecutionContext* IDBTransaction::executionContext() const
337 return ActiveDOMObject::executionContext();
340 bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
342 IDB_TRACE("IDBTransaction::dispatchEvent");
343 ASSERT(m_state != Finished);
344 ASSERT(m_hasPendingActivity);
345 ASSERT(executionContext());
346 ASSERT(event->target() == this);
349 // Break reference cycles.
350 for (IDBObjectStoreMap::iterator it = m_objectStoreMap.begin(); it != m_objectStoreMap.end(); ++it)
351 it->value->transactionFinished();
352 m_objectStoreMap.clear();
353 for (IDBObjectStoreSet::iterator it = m_deletedObjectStores.begin(); it != m_deletedObjectStores.end(); ++it)
354 (*it)->transactionFinished();
355 m_deletedObjectStores.clear();
357 Vector<RefPtr<EventTarget> > targets;
358 targets.append(this);
359 targets.append(db());
361 // FIXME: When we allow custom event dispatching, this will probably need to change.
362 ASSERT(event->type() == EventTypeNames::complete || event->type() == EventTypeNames::abort);
363 bool returnValue = IDBEventDispatcher::dispatch(event.get(), targets);
364 // FIXME: Try to construct a test where |this| outlives openDBRequest and we
366 if (m_openDBRequest) {
367 ASSERT(isVersionChange());
368 m_openDBRequest->transactionDidFinishAndDispatch();
370 m_hasPendingActivity = false;
374 void IDBTransaction::stop()
376 if (m_contextStopped)
379 m_contextStopped = true;
381 abort(IGNORE_EXCEPTION);
384 void IDBTransaction::enqueueEvent(PassRefPtr<Event> event)
386 ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to enqueue an event of type %s.", event->type().string().utf8().data());
387 if (m_contextStopped || !executionContext())
390 EventQueue* eventQueue = executionContext()->eventQueue();
391 event->setTarget(this);
392 eventQueue->enqueueEvent(event);
395 IDBDatabaseBackendInterface* IDBTransaction::backendDB() const
397 return db()->backend();
400 } // namespace WebCore