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