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/ExecutionContext.h"
32 #include "core/events/EventQueue.h"
33 #include "core/inspector/ScriptCallStack.h"
34 #include "modules/indexeddb/IDBDatabase.h"
35 #include "modules/indexeddb/IDBEventDispatcher.h"
36 #include "modules/indexeddb/IDBIndex.h"
37 #include "modules/indexeddb/IDBObjectStore.h"
38 #include "modules/indexeddb/IDBOpenDBRequest.h"
39 #include "modules/indexeddb/IDBPendingTransactionMonitor.h"
40 #include "modules/indexeddb/IDBTracing.h"
42 using blink::WebIDBDatabase;
46 PassRefPtr<IDBTransaction> IDBTransaction::create(ExecutionContext* context, int64_t id, const Vector<String>& objectStoreNames, WebIDBDatabase::TransactionMode mode, IDBDatabase* db)
48 IDBOpenDBRequest* openDBRequest = 0;
49 RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, id, objectStoreNames, mode, db, openDBRequest, IDBDatabaseMetadata())));
50 transaction->suspendIfNeeded();
51 return transaction.release();
54 PassRefPtr<IDBTransaction> IDBTransaction::create(ExecutionContext* context, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
56 RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, id, Vector<String>(), WebIDBDatabase::TransactionVersionChange, db, openDBRequest, previousMetadata)));
57 transaction->suspendIfNeeded();
58 return transaction.release();
61 const AtomicString& IDBTransaction::modeReadOnly()
63 DEFINE_STATIC_LOCAL(AtomicString, readonly, ("readonly", AtomicString::ConstructFromLiteral));
67 const AtomicString& IDBTransaction::modeReadWrite()
69 DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("readwrite", AtomicString::ConstructFromLiteral));
73 const AtomicString& IDBTransaction::modeVersionChange()
75 DEFINE_STATIC_LOCAL(AtomicString, versionchange, ("versionchange", AtomicString::ConstructFromLiteral));
79 IDBTransaction::IDBTransaction(ExecutionContext* context, int64_t id, const Vector<String>& objectStoreNames, WebIDBDatabase::TransactionMode mode, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
80 : ActiveDOMObject(context)
83 , m_objectStoreNames(objectStoreNames)
84 , m_openDBRequest(openDBRequest)
87 , m_hasPendingActivity(true)
88 , m_contextStopped(false)
89 , m_previousMetadata(previousMetadata)
91 ScriptWrappable::init(this);
92 if (mode == WebIDBDatabase::TransactionVersionChange) {
93 // Not active until the callback.
97 // We pass a reference of this object before it can be adopted.
98 relaxAdoptionRequirement();
99 if (m_state == Active)
100 IDBPendingTransactionMonitor::addNewTransaction(this);
101 m_database->transactionCreated(this);
104 IDBTransaction::~IDBTransaction()
106 ASSERT(m_state == Finished || m_contextStopped);
107 ASSERT(m_requestList.isEmpty() || m_contextStopped);
110 const String& IDBTransaction::mode() const
112 return modeToString(m_mode);
115 void IDBTransaction::setError(PassRefPtr<DOMError> error)
117 ASSERT(m_state != Finished);
120 // The first error to be set is the true cause of the
121 // transaction abort.
127 PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, ExceptionState& exceptionState)
129 if (m_state == Finished) {
130 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
134 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
135 if (it != m_objectStoreMap.end())
138 if (!isVersionChange() && !m_objectStoreNames.contains(name)) {
139 exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
143 int64_t objectStoreId = m_database->findObjectStoreId(name);
144 if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
145 ASSERT(isVersionChange());
146 exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
150 const IDBDatabaseMetadata& metadata = m_database->metadata();
152 RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this);
153 objectStoreCreated(name, objectStore);
154 return objectStore.release();
157 void IDBTransaction::objectStoreCreated(const String& name, PassRefPtr<IDBObjectStore> prpObjectStore)
159 ASSERT(m_state != Finished);
160 RefPtr<IDBObjectStore> objectStore = prpObjectStore;
161 m_objectStoreMap.set(name, objectStore);
162 if (isVersionChange())
163 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
166 void IDBTransaction::objectStoreDeleted(const String& name)
168 ASSERT(m_state != Finished);
169 ASSERT(isVersionChange());
170 IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
171 if (it != m_objectStoreMap.end()) {
172 RefPtr<IDBObjectStore> objectStore = it->value;
173 m_objectStoreMap.remove(name);
174 objectStore->markDeleted();
175 m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
176 m_deletedObjectStores.add(objectStore);
180 void IDBTransaction::setActive(bool active)
182 ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to setActive(%s)", active ? "true" : "false");
183 if (m_state == Finishing)
185 ASSERT(active != (m_state == Active));
186 m_state = active ? Active : Inactive;
188 if (!active && m_requestList.isEmpty())
189 backendDB()->commit(m_id);
192 void IDBTransaction::abort(ExceptionState& exceptionState)
194 if (m_state == Finishing || m_state == Finished) {
195 exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
201 if (m_contextStopped)
204 while (!m_requestList.isEmpty()) {
205 RefPtr<IDBRequest> request = *m_requestList.begin();
206 m_requestList.remove(request);
210 RefPtr<IDBTransaction> selfRef = this;
211 backendDB()->abort(m_id);
214 void IDBTransaction::registerRequest(IDBRequest* request)
217 ASSERT(m_state == Active);
218 m_requestList.add(request);
221 void IDBTransaction::unregisterRequest(IDBRequest* request)
224 // If we aborted the request, it will already have been removed.
225 m_requestList.remove(request);
228 void IDBTransaction::onAbort(PassRefPtr<DOMError> prpError)
230 IDB_TRACE("IDBTransaction::onAbort");
231 if (m_contextStopped) {
232 RefPtr<IDBTransaction> protect(this);
233 m_database->transactionFinished(this);
237 RefPtr<DOMError> error = prpError;
238 ASSERT(m_state != Finished);
240 if (m_state != Finishing) {
242 setError(error.release());
244 // Abort was not triggered by front-end, so outstanding requests must
246 while (!m_requestList.isEmpty()) {
247 RefPtr<IDBRequest> request = *m_requestList.begin();
248 m_requestList.remove(request);
254 if (isVersionChange()) {
255 for (IDBObjectStoreMetadataMap::iterator it = m_objectStoreCleanupMap.begin(); it != m_objectStoreCleanupMap.end(); ++it)
256 it->key->setMetadata(it->value);
257 m_database->setMetadata(m_previousMetadata);
260 m_objectStoreCleanupMap.clear();
262 // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
263 enqueueEvent(Event::createBubble(EventTypeNames::abort));
265 // If script has stopped and GC has completed, database may have last reference to this object.
266 RefPtr<IDBTransaction> protect(this);
267 m_database->transactionFinished(this);
270 void IDBTransaction::onComplete()
272 IDB_TRACE("IDBTransaction::onComplete");
273 if (m_contextStopped) {
274 RefPtr<IDBTransaction> protect(this);
275 m_database->transactionFinished(this);
279 ASSERT(m_state != Finished);
281 m_objectStoreCleanupMap.clear();
283 // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
284 enqueueEvent(Event::create(EventTypeNames::complete));
286 // If script has stopped and GC has completed, database may have last reference to this object.
287 RefPtr<IDBTransaction> protect(this);
288 m_database->transactionFinished(this);
291 bool IDBTransaction::hasPendingActivity() const
293 // FIXME: In an ideal world, we should return true as long as anyone has a or can
294 // get a handle to us or any child request object and any of those have
295 // event listeners. This is in order to handle user generated events properly.
296 return m_hasPendingActivity && !m_contextStopped;
299 WebIDBDatabase::TransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionState& exceptionState)
301 if (modeString.isNull()
302 || modeString == IDBTransaction::modeReadOnly())
303 return WebIDBDatabase::TransactionReadOnly;
304 if (modeString == IDBTransaction::modeReadWrite())
305 return WebIDBDatabase::TransactionReadWrite;
307 exceptionState.throwTypeError("The mode provided ('" + modeString + "') is not one of 'readonly' or 'readwrite'.");
308 return WebIDBDatabase::TransactionReadOnly;
311 const AtomicString& IDBTransaction::modeToString(WebIDBDatabase::TransactionMode mode)
314 case WebIDBDatabase::TransactionReadOnly:
315 return IDBTransaction::modeReadOnly();
318 case WebIDBDatabase::TransactionReadWrite:
319 return IDBTransaction::modeReadWrite();
322 case WebIDBDatabase::TransactionVersionChange:
323 return IDBTransaction::modeVersionChange();
327 ASSERT_NOT_REACHED();
328 return IDBTransaction::modeReadOnly();
331 const AtomicString& IDBTransaction::interfaceName() const
333 return EventTargetNames::IDBTransaction;
336 ExecutionContext* IDBTransaction::executionContext() const
338 return ActiveDOMObject::executionContext();
341 bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
343 IDB_TRACE("IDBTransaction::dispatchEvent");
344 ASSERT(m_state != Finished);
345 ASSERT(m_hasPendingActivity);
346 ASSERT(executionContext());
347 ASSERT(event->target() == this);
350 // Break reference cycles.
351 for (IDBObjectStoreMap::iterator it = m_objectStoreMap.begin(); it != m_objectStoreMap.end(); ++it)
352 it->value->transactionFinished();
353 m_objectStoreMap.clear();
354 for (IDBObjectStoreSet::iterator it = m_deletedObjectStores.begin(); it != m_deletedObjectStores.end(); ++it)
355 (*it)->transactionFinished();
356 m_deletedObjectStores.clear();
358 Vector<RefPtr<EventTarget> > targets;
359 targets.append(this);
360 targets.append(db());
362 // FIXME: When we allow custom event dispatching, this will probably need to change.
363 ASSERT(event->type() == EventTypeNames::complete || event->type() == EventTypeNames::abort);
364 bool returnValue = IDBEventDispatcher::dispatch(event.get(), targets);
365 // FIXME: Try to construct a test where |this| outlives openDBRequest and we
367 if (m_openDBRequest) {
368 ASSERT(isVersionChange());
369 m_openDBRequest->transactionDidFinishAndDispatch();
371 m_hasPendingActivity = false;
375 void IDBTransaction::stop()
377 if (m_contextStopped)
380 m_contextStopped = true;
382 abort(IGNORE_EXCEPTION);
385 void IDBTransaction::enqueueEvent(PassRefPtr<Event> event)
387 ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to enqueue an event of type %s.", event->type().utf8().data());
388 if (m_contextStopped || !executionContext())
391 EventQueue* eventQueue = executionContext()->eventQueue();
392 event->setTarget(this);
393 eventQueue->enqueueEvent(event);
396 blink::WebIDBDatabase* IDBTransaction::backendDB() const
398 return m_database->backend();
401 } // namespace WebCore