- add third_party src.
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / modules / indexeddb / IDBTransaction.cpp
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/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"
42
43 namespace WebCore {
44
45 PassRefPtr<IDBTransaction> IDBTransaction::create(ExecutionContext* context, int64_t id, const Vector<String>& objectStoreNames, IndexedDB::TransactionMode mode, IDBDatabase* db)
46 {
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();
51 }
52
53 PassRefPtr<IDBTransaction> IDBTransaction::create(ExecutionContext* context, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
54 {
55     RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, id, Vector<String>(), IndexedDB::TransactionVersionChange, db, openDBRequest, previousMetadata)));
56     transaction->suspendIfNeeded();
57     return transaction.release();
58 }
59
60 const AtomicString& IDBTransaction::modeReadOnly()
61 {
62     DEFINE_STATIC_LOCAL(AtomicString, readonly, ("readonly", AtomicString::ConstructFromLiteral));
63     return readonly;
64 }
65
66 const AtomicString& IDBTransaction::modeReadWrite()
67 {
68     DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("readwrite", AtomicString::ConstructFromLiteral));
69     return readwrite;
70 }
71
72 const AtomicString& IDBTransaction::modeVersionChange()
73 {
74     DEFINE_STATIC_LOCAL(AtomicString, versionchange, ("versionchange", AtomicString::ConstructFromLiteral));
75     return versionchange;
76 }
77
78 const AtomicString& IDBTransaction::modeReadOnlyLegacy()
79 {
80     DEFINE_STATIC_LOCAL(AtomicString, readonly, ("0", AtomicString::ConstructFromLiteral));
81     return readonly;
82 }
83
84 const AtomicString& IDBTransaction::modeReadWriteLegacy()
85 {
86     DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("1", AtomicString::ConstructFromLiteral));
87     return readwrite;
88 }
89
90
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)
93     , m_id(id)
94     , m_database(db)
95     , m_objectStoreNames(objectStoreNames)
96     , m_openDBRequest(openDBRequest)
97     , m_mode(mode)
98     , m_state(Active)
99     , m_hasPendingActivity(true)
100     , m_contextStopped(false)
101     , m_previousMetadata(previousMetadata)
102 {
103     ScriptWrappable::init(this);
104     if (mode == IndexedDB::TransactionVersionChange) {
105         // Not active until the callback.
106         m_state = Inactive;
107     }
108
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);
114 }
115
116 IDBTransaction::~IDBTransaction()
117 {
118     ASSERT(m_state == Finished || m_contextStopped);
119     ASSERT(m_requestList.isEmpty() || m_contextStopped);
120 }
121
122 const String& IDBTransaction::mode() const
123 {
124     return modeToString(m_mode);
125 }
126
127 void IDBTransaction::setError(PassRefPtr<DOMError> error)
128 {
129     ASSERT(m_state != Finished);
130     ASSERT(error);
131
132     // The first error to be set is the true cause of the
133     // transaction abort.
134     if (!m_error) {
135         m_error = error;
136     }
137 }
138
139 PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, ExceptionState& es)
140 {
141     if (m_state == Finished) {
142         es.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
143         return 0;
144     }
145
146     IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
147     if (it != m_objectStoreMap.end())
148         return it->value;
149
150     if (!isVersionChange() && !m_objectStoreNames.contains(name)) {
151         es.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
152         return 0;
153     }
154
155     int64_t objectStoreId = m_database->findObjectStoreId(name);
156     if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
157         ASSERT(isVersionChange());
158         es.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
159         return 0;
160     }
161
162     const IDBDatabaseMetadata& metadata = m_database->metadata();
163
164     RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this);
165     objectStoreCreated(name, objectStore);
166     return objectStore.release();
167 }
168
169 void IDBTransaction::objectStoreCreated(const String& name, PassRefPtr<IDBObjectStore> prpObjectStore)
170 {
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());
176 }
177
178 void IDBTransaction::objectStoreDeleted(const String& name)
179 {
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);
189     }
190 }
191
192 void IDBTransaction::setActive(bool active)
193 {
194     ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to setActive(%s)", active ? "true" : "false");
195     if (m_state == Finishing)
196         return;
197     ASSERT(active != (m_state == Active));
198     m_state = active ? Active : Inactive;
199
200     if (!active && m_requestList.isEmpty())
201         backendDB()->commit(m_id);
202 }
203
204 void IDBTransaction::abort(ExceptionState& es)
205 {
206     if (m_state == Finishing || m_state == Finished) {
207         es.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
208         return;
209     }
210
211     m_state = Finishing;
212
213     if (!m_contextStopped) {
214         while (!m_requestList.isEmpty()) {
215             RefPtr<IDBRequest> request = *m_requestList.begin();
216             m_requestList.remove(request);
217             request->abort();
218         }
219     }
220
221     RefPtr<IDBTransaction> selfRef = this;
222     backendDB()->abort(m_id);
223 }
224
225 void IDBTransaction::registerRequest(IDBRequest* request)
226 {
227     ASSERT(request);
228     ASSERT(m_state == Active);
229     m_requestList.add(request);
230 }
231
232 void IDBTransaction::unregisterRequest(IDBRequest* request)
233 {
234     ASSERT(request);
235     // If we aborted the request, it will already have been removed.
236     m_requestList.remove(request);
237 }
238
239 void IDBTransaction::onAbort(PassRefPtr<DOMError> prpError)
240 {
241     IDB_TRACE("IDBTransaction::onAbort");
242     RefPtr<DOMError> error = prpError;
243     ASSERT(m_state != Finished);
244
245     if (m_state != Finishing) {
246         ASSERT(error.get());
247         setError(error.release());
248
249         // Abort was not triggered by front-end, so outstanding requests must
250         // be aborted now.
251         while (!m_requestList.isEmpty()) {
252             RefPtr<IDBRequest> request = *m_requestList.begin();
253             m_requestList.remove(request);
254             request->abort();
255         }
256         m_state = Finishing;
257     }
258
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);
263         m_database->close();
264     }
265     m_objectStoreCleanupMap.clear();
266
267     // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
268     enqueueEvent(Event::createBubble(EventTypeNames::abort));
269
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);
273 }
274
275 void IDBTransaction::onComplete()
276 {
277     IDB_TRACE("IDBTransaction::onComplete");
278     ASSERT(m_state != Finished);
279     m_state = Finishing;
280     m_objectStoreCleanupMap.clear();
281
282     // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
283     enqueueEvent(Event::create(EventTypeNames::complete));
284
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);
288 }
289
290 bool IDBTransaction::hasPendingActivity() const
291 {
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;
296 }
297
298 IndexedDB::TransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionState& es)
299 {
300     if (modeString.isNull()
301         || modeString == IDBTransaction::modeReadOnly())
302         return IndexedDB::TransactionReadOnly;
303     if (modeString == IDBTransaction::modeReadWrite())
304         return IndexedDB::TransactionReadWrite;
305
306     es.throwUninformativeAndGenericTypeError();
307     return IndexedDB::TransactionReadOnly;
308 }
309
310 const AtomicString& IDBTransaction::modeToString(IndexedDB::TransactionMode mode)
311 {
312     switch (mode) {
313     case IndexedDB::TransactionReadOnly:
314         return IDBTransaction::modeReadOnly();
315         break;
316
317     case IndexedDB::TransactionReadWrite:
318         return IDBTransaction::modeReadWrite();
319         break;
320
321     case IndexedDB::TransactionVersionChange:
322         return IDBTransaction::modeVersionChange();
323         break;
324     }
325
326     ASSERT_NOT_REACHED();
327     return IDBTransaction::modeReadOnly();
328 }
329
330 const AtomicString& IDBTransaction::interfaceName() const
331 {
332     return EventTargetNames::IDBTransaction;
333 }
334
335 ExecutionContext* IDBTransaction::executionContext() const
336 {
337     return ActiveDOMObject::executionContext();
338 }
339
340 bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
341 {
342     IDB_TRACE("IDBTransaction::dispatchEvent");
343     ASSERT(m_state != Finished);
344     ASSERT(m_hasPendingActivity);
345     ASSERT(executionContext());
346     ASSERT(event->target() == this);
347     m_state = Finished;
348
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();
356
357     Vector<RefPtr<EventTarget> > targets;
358     targets.append(this);
359     targets.append(db());
360
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
365     // get a crash.
366     if (m_openDBRequest) {
367         ASSERT(isVersionChange());
368         m_openDBRequest->transactionDidFinishAndDispatch();
369     }
370     m_hasPendingActivity = false;
371     return returnValue;
372 }
373
374 void IDBTransaction::stop()
375 {
376     if (m_contextStopped)
377         return;
378
379     m_contextStopped = true;
380
381     abort(IGNORE_EXCEPTION);
382 }
383
384 void IDBTransaction::enqueueEvent(PassRefPtr<Event> event)
385 {
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())
388         return;
389
390     EventQueue* eventQueue = executionContext()->eventQueue();
391     event->setTarget(this);
392     eventQueue->enqueueEvent(event);
393 }
394
395 IDBDatabaseBackendInterface* IDBTransaction::backendDB() const
396 {
397     return db()->backend();
398 }
399
400 } // namespace WebCore