Upstream version 10.39.225.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/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"
42
43 using blink::WebIDBDatabase;
44
45 namespace blink {
46
47 IDBTransaction* IDBTransaction::create(ScriptState* scriptState, int64_t id, const Vector<String>& objectStoreNames, WebIDBTransactionMode mode, IDBDatabase* db)
48 {
49     IDBOpenDBRequest* openDBRequest = 0;
50     IDBTransaction* transaction = adoptRefCountedGarbageCollectedWillBeNoop(new IDBTransaction(scriptState, id, objectStoreNames, mode, db, openDBRequest, IDBDatabaseMetadata()));
51     transaction->suspendIfNeeded();
52     return transaction;
53 }
54
55 IDBTransaction* IDBTransaction::create(ScriptState* scriptState, int64_t id, IDBDatabase* db, IDBOpenDBRequest* openDBRequest, const IDBDatabaseMetadata& previousMetadata)
56 {
57     IDBTransaction* transaction = adoptRefCountedGarbageCollectedWillBeNoop(new IDBTransaction(scriptState, id, Vector<String>(), WebIDBTransactionModeVersionChange, db, openDBRequest, previousMetadata));
58     transaction->suspendIfNeeded();
59     return transaction;
60 }
61
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())
64     , m_id(id)
65     , m_database(db)
66     , m_objectStoreNames(objectStoreNames)
67     , m_openDBRequest(openDBRequest)
68     , m_mode(mode)
69     , m_state(Active)
70     , m_hasPendingActivity(true)
71     , m_contextStopped(false)
72     , m_previousMetadata(previousMetadata)
73 {
74     if (mode == WebIDBTransactionModeVersionChange) {
75         // Not active until the callback.
76         m_state = Inactive;
77     }
78
79     if (m_state == Active)
80         V8PerIsolateData::from(scriptState->isolate())->ensureIDBPendingTransactionMonitor()->addNewTransaction(*this);
81     m_database->transactionCreated(this);
82 }
83
84 IDBTransaction::~IDBTransaction()
85 {
86     ASSERT(m_state == Finished || m_contextStopped);
87     ASSERT(m_requestList.isEmpty() || m_contextStopped);
88 }
89
90 void IDBTransaction::trace(Visitor* visitor)
91 {
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);
100 }
101
102 void IDBTransaction::setError(PassRefPtrWillBeRawPtr<DOMError> error)
103 {
104     ASSERT(m_state != Finished);
105     ASSERT(error);
106
107     // The first error to be set is the true cause of the
108     // transaction abort.
109     if (!m_error) {
110         m_error = error;
111     }
112 }
113
114 IDBObjectStore* IDBTransaction::objectStore(const String& name, ExceptionState& exceptionState)
115 {
116     if (m_state == Finished) {
117         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
118         return 0;
119     }
120
121     IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
122     if (it != m_objectStoreMap.end())
123         return it->value;
124
125     if (!isVersionChange() && !m_objectStoreNames.contains(name)) {
126         exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
127         return 0;
128     }
129
130     int64_t objectStoreId = m_database->findObjectStoreId(name);
131     if (objectStoreId == IDBObjectStoreMetadata::InvalidId) {
132         ASSERT(isVersionChange());
133         exceptionState.throwDOMException(NotFoundError, IDBDatabase::noSuchObjectStoreErrorMessage);
134         return 0;
135     }
136
137     const IDBDatabaseMetadata& metadata = m_database->metadata();
138
139     IDBObjectStore* objectStore = IDBObjectStore::create(metadata.objectStores.get(objectStoreId), this);
140     objectStoreCreated(name, objectStore);
141     return objectStore;
142 }
143
144 void IDBTransaction::objectStoreCreated(const String& name, IDBObjectStore* objectStore)
145 {
146     ASSERT(m_state != Finished);
147     m_objectStoreMap.set(name, objectStore);
148     if (isVersionChange())
149         m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
150 }
151
152 void IDBTransaction::objectStoreDeleted(const String& name)
153 {
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);
163     }
164 }
165
166 void IDBTransaction::setActive(bool active)
167 {
168     ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to setActive(%s)", active ? "true" : "false");
169     if (m_state == Finishing)
170         return;
171     ASSERT(active != (m_state == Active));
172     m_state = active ? Active : Inactive;
173
174     if (!active && m_requestList.isEmpty() && backendDB())
175         backendDB()->commit(m_id);
176 }
177
178 void IDBTransaction::abort(ExceptionState& exceptionState)
179 {
180     if (m_state == Finishing || m_state == Finished) {
181         exceptionState.throwDOMException(InvalidStateError, IDBDatabase::transactionFinishedErrorMessage);
182         return;
183     }
184
185     m_state = Finishing;
186
187     if (m_contextStopped)
188         return;
189
190     while (!m_requestList.isEmpty()) {
191         IDBRequest* request = *m_requestList.begin();
192         m_requestList.remove(request);
193         request->abort();
194     }
195
196     if (backendDB())
197         backendDB()->abort(m_id);
198 }
199
200 void IDBTransaction::registerRequest(IDBRequest* request)
201 {
202     ASSERT(request);
203     ASSERT(m_state == Active);
204     m_requestList.add(request);
205 }
206
207 void IDBTransaction::unregisterRequest(IDBRequest* request)
208 {
209     ASSERT(request);
210     // If we aborted the request, it will already have been removed.
211     m_requestList.remove(request);
212 }
213
214 void IDBTransaction::onAbort(PassRefPtrWillBeRawPtr<DOMError> prpError)
215 {
216     IDB_TRACE("IDBTransaction::onAbort");
217     if (m_contextStopped) {
218         m_database->transactionFinished(this);
219         return;
220     }
221
222     RefPtrWillBeRawPtr<DOMError> error = prpError;
223     ASSERT(m_state != Finished);
224
225     if (m_state != Finishing) {
226         ASSERT(error.get());
227         setError(error.release());
228
229         // Abort was not triggered by front-end, so outstanding requests must
230         // be aborted now.
231         while (!m_requestList.isEmpty()) {
232             IDBRequest* request = *m_requestList.begin();
233             m_requestList.remove(request);
234             request->abort();
235         }
236         m_state = Finishing;
237     }
238
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);
243         m_database->close();
244     }
245     m_objectStoreCleanupMap.clear();
246
247     // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
248     enqueueEvent(Event::createBubble(EventTypeNames::abort));
249
250     m_database->transactionFinished(this);
251 }
252
253 void IDBTransaction::onComplete()
254 {
255     IDB_TRACE("IDBTransaction::onComplete");
256     if (m_contextStopped) {
257         m_database->transactionFinished(this);
258         return;
259     }
260
261     ASSERT(m_state != Finished);
262     m_state = Finishing;
263     m_objectStoreCleanupMap.clear();
264
265     // Enqueue events before notifying database, as database may close which enqueues more events and order matters.
266     enqueueEvent(Event::create(EventTypeNames::complete));
267
268     m_database->transactionFinished(this);
269 }
270
271 bool IDBTransaction::hasPendingActivity() const
272 {
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;
277 }
278
279 WebIDBTransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionState& exceptionState)
280 {
281     if (modeString == IndexedDBNames::readonly)
282         return WebIDBTransactionModeReadOnly;
283     if (modeString == IndexedDBNames::readwrite)
284         return WebIDBTransactionModeReadWrite;
285
286     exceptionState.throwTypeError("The mode provided ('" + modeString + "') is not one of 'readonly' or 'readwrite'.");
287     return WebIDBTransactionModeReadOnly;
288 }
289
290 const String& IDBTransaction::mode() const
291 {
292     switch (m_mode) {
293     case WebIDBTransactionModeReadOnly:
294         return IndexedDBNames::readonly;
295
296     case WebIDBTransactionModeReadWrite:
297         return IndexedDBNames::readwrite;
298
299     case WebIDBTransactionModeVersionChange:
300         return IndexedDBNames::versionchange;
301     }
302
303     ASSERT_NOT_REACHED();
304     return IndexedDBNames::readonly;
305 }
306
307 const AtomicString& IDBTransaction::interfaceName() const
308 {
309     return EventTargetNames::IDBTransaction;
310 }
311
312 ExecutionContext* IDBTransaction::executionContext() const
313 {
314     return ActiveDOMObject::executionContext();
315 }
316
317 bool IDBTransaction::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
318 {
319     IDB_TRACE("IDBTransaction::dispatchEvent");
320     if (m_contextStopped || !executionContext()) {
321         m_state = Finished;
322         return false;
323     }
324     ASSERT(m_state != Finished);
325     ASSERT(m_hasPendingActivity);
326     ASSERT(executionContext());
327     ASSERT(event->target() == this);
328     m_state = Finished;
329
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();
337
338     WillBeHeapVector<RefPtrWillBeMember<EventTarget> > targets;
339     targets.append(this);
340     targets.append(db());
341
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
346     // get a crash.
347     if (m_openDBRequest) {
348         ASSERT(isVersionChange());
349         m_openDBRequest->transactionDidFinishAndDispatch();
350     }
351     m_hasPendingActivity = false;
352     return returnValue;
353 }
354
355 void IDBTransaction::stop()
356 {
357     if (m_contextStopped)
358         return;
359
360     m_contextStopped = true;
361
362     abort(IGNORE_EXCEPTION);
363 }
364
365 void IDBTransaction::enqueueEvent(PassRefPtrWillBeRawPtr<Event> event)
366 {
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())
369         return;
370
371     EventQueue* eventQueue = executionContext()->eventQueue();
372     event->setTarget(this);
373     eventQueue->enqueueEvent(event);
374 }
375
376 WebIDBDatabase* IDBTransaction::backendDB() const
377 {
378     return m_database->backend();
379 }
380
381 } // namespace blink