c09aa0ece0f6e235bc0a867f2a28c313d20d7f16
[WebKit-https.git] / Source / WebCore / 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 "IDBTransaction.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "EventException.h"
32 #include "EventQueue.h"
33 #include "IDBDatabase.h"
34 #include "IDBDatabaseException.h"
35 #include "IDBEventDispatcher.h"
36 #include "IDBIndex.h"
37 #include "IDBObjectStore.h"
38 #include "IDBObjectStoreBackendInterface.h"
39 #include "IDBPendingTransactionMonitor.h"
40 #include "IDBTracing.h"
41
42 namespace WebCore {
43
44 PassRefPtr<IDBTransaction> IDBTransaction::create(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBTransaction::Mode mode, IDBDatabase* db)
45 {
46     RefPtr<IDBTransaction> transaction(adoptRef(new IDBTransaction(context, backend, mode, db)));
47     transaction->suspendIfNeeded();
48     return transaction.release();
49 }
50
51 const AtomicString& IDBTransaction::modeReadOnly()
52 {
53     DEFINE_STATIC_LOCAL(AtomicString, readonly, ("readonly"));
54     return readonly;
55 }
56
57 const AtomicString& IDBTransaction::modeReadWrite()
58 {
59     DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("readwrite"));
60     return readwrite;
61 }
62
63 const AtomicString& IDBTransaction::modeVersionChange()
64 {
65     DEFINE_STATIC_LOCAL(AtomicString, versionchange, ("versionchange"));
66     return versionchange;
67 }
68
69 const AtomicString& IDBTransaction::modeReadOnlyLegacy()
70 {
71     DEFINE_STATIC_LOCAL(AtomicString, readonly, ("0"));
72     return readonly;
73 }
74
75 const AtomicString& IDBTransaction::modeReadWriteLegacy()
76 {
77     DEFINE_STATIC_LOCAL(AtomicString, readwrite, ("1"));
78     return readwrite;
79 }
80
81
82 IDBTransaction::IDBTransaction(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBTransaction::Mode mode, IDBDatabase* db)
83     : ActiveDOMObject(context, this)
84     , m_backend(backend)
85     , m_database(db)
86     , m_mode(mode)
87     , m_active(true)
88     , m_state(Unused)
89     , m_contextStopped(false)
90 {
91     ASSERT(m_backend);
92
93     if (mode == VERSION_CHANGE) {
94         // Not active until the callback.
95         m_active = false;
96         // Implicitly used by the version change itself.
97         m_state = Used;
98     }
99
100     // We pass a reference of this object before it can be adopted.
101     relaxAdoptionRequirement();
102     if (m_active)
103         IDBPendingTransactionMonitor::addNewTransaction(this);
104     m_database->transactionCreated(this);
105 }
106
107 IDBTransaction::~IDBTransaction()
108 {
109     ASSERT(m_state == Finished);
110 }
111
112 IDBTransactionBackendInterface* IDBTransaction::backend() const
113 {
114     return m_backend.get();
115 }
116
117 const String& IDBTransaction::mode() const
118 {
119     ExceptionCode ec = 0;
120     const AtomicString& mode = modeToString(m_mode, ec);
121     ASSERT(!ec);
122     return mode;
123 }
124
125 IDBDatabase* IDBTransaction::db() const
126 {
127     return m_database.get();
128 }
129
130 PassRefPtr<DOMError> IDBTransaction::error(ExceptionCode& ec) const
131 {
132     if (m_state != Finished) {
133         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
134         return 0;
135     }
136     return m_error;
137 }
138
139 void IDBTransaction::setError(PassRefPtr<DOMError> error)
140 {
141     ASSERT(m_state != Finished);
142     ASSERT(error);
143
144     // The first error to be set is the true cause of the
145     // transaction abort.
146     if (!m_error)
147         m_error = error;
148 }
149
150 PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, ExceptionCode& ec)
151 {
152     if (m_state == Finished) {
153         ec = IDBDatabaseException::IDB_INVALID_STATE_ERR;
154         return 0;
155     }
156
157     IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
158     if (it != m_objectStoreMap.end())
159         return it->second;
160
161     RefPtr<IDBObjectStoreBackendInterface> objectStoreBackend = m_backend->objectStore(name, ec);
162     ASSERT(!objectStoreBackend != !ec); // If we didn't get a store, we should have gotten an exception code. And vice versa.
163     if (ec)
164         return 0;
165
166     const IDBDatabaseMetadata& metadata = m_database->metadata();
167     IDBDatabaseMetadata::ObjectStoreMap::const_iterator mdit = metadata.objectStores.find(name);
168     ASSERT(mdit != metadata.objectStores.end());
169
170     RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(mdit->second, objectStoreBackend, this);
171     objectStoreCreated(name, objectStore);
172     return objectStore.release();
173 }
174
175 void IDBTransaction::objectStoreCreated(const String& name, PassRefPtr<IDBObjectStore> prpObjectStore)
176 {
177     ASSERT(m_state != Finished);
178     RefPtr<IDBObjectStore> objectStore = prpObjectStore;
179     m_objectStoreMap.set(name, objectStore);
180     if (isVersionChange())
181         m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
182 }
183
184 void IDBTransaction::objectStoreDeleted(const String& name)
185 {
186     ASSERT(m_state != Finished);
187     ASSERT(isVersionChange());
188     IDBObjectStoreMap::iterator it = m_objectStoreMap.find(name);
189     if (it != m_objectStoreMap.end()) {
190         RefPtr<IDBObjectStore> objectStore = it->second;
191         m_objectStoreMap.remove(name);
192         objectStore->markDeleted();
193         m_objectStoreCleanupMap.set(objectStore, objectStore->metadata());
194     }
195 }
196
197 void IDBTransaction::setActive(bool active)
198 {
199     ASSERT(m_state != Finished);
200     if (m_state == Finishing)
201         return;
202     ASSERT(m_state == Unused || m_state == Used);
203     ASSERT(active != m_active);
204     m_active = active;
205
206     if (!active && m_state == Unused)
207         m_backend->commit();
208 }
209
210 void IDBTransaction::abort()
211 {
212     if (m_state == Finishing || m_state == Finished)
213         return;
214     m_state = Finishing;
215     m_active = false;
216
217     while (!m_requestList.isEmpty()) {
218         IDBRequest* request = *m_requestList.begin();
219         m_requestList.remove(request);
220         request->abort();
221     }
222
223     RefPtr<IDBTransaction> selfRef = this;
224     if (m_backend)
225         m_backend->abort();
226 }
227
228 IDBTransaction::OpenCursorNotifier::OpenCursorNotifier(PassRefPtr<IDBTransaction> transaction, IDBCursor* cursor)
229     : m_transaction(transaction),
230       m_cursor(cursor)
231 {
232     m_transaction->registerOpenCursor(m_cursor);
233 }
234
235 IDBTransaction::OpenCursorNotifier::~OpenCursorNotifier()
236 {
237     m_transaction->unregisterOpenCursor(m_cursor);
238 }
239
240 void IDBTransaction::registerOpenCursor(IDBCursor* cursor)
241 {
242     m_openCursors.add(cursor);
243 }
244
245 void IDBTransaction::unregisterOpenCursor(IDBCursor* cursor)
246 {
247     m_openCursors.remove(cursor);
248 }
249
250 void IDBTransaction::closeOpenCursors()
251 {
252     HashSet<IDBCursor*> cursors;
253     cursors.swap(m_openCursors);
254     for (HashSet<IDBCursor*>::iterator i = cursors.begin(); i != cursors.end(); ++i)
255         (*i)->close();
256 }
257
258 void IDBTransaction::registerRequest(IDBRequest* request)
259 {
260     ASSERT(request);
261     ASSERT(m_state == Unused || m_state == Used);
262     ASSERT(m_active);
263     m_requestList.add(request);
264     m_state = Used;
265 }
266
267 void IDBTransaction::unregisterRequest(IDBRequest* request)
268 {
269     ASSERT(request);
270     // If we aborted the request, it will already have been removed.
271     m_requestList.remove(request);
272 }
273
274 void IDBTransaction::onAbort()
275 {
276     ASSERT(m_state != Finished);
277
278     if (m_state != Finishing) {
279         // Abort was not triggered by front-end, so outstanding requests must
280         // be aborted now.
281         while (!m_requestList.isEmpty()) {
282             IDBRequest* request = *m_requestList.begin();
283             m_requestList.remove(request);
284             request->abort();
285         }
286         m_state = Finishing;
287     }
288
289     if (isVersionChange()) {
290         for (IDBObjectStoreMetadataMap::iterator it = m_objectStoreCleanupMap.begin(); it != m_objectStoreCleanupMap.end(); ++it)
291             it->first->setMetadata(it->second);
292     }
293     m_objectStoreCleanupMap.clear();
294     closeOpenCursors();
295     m_database->transactionFinished(this);
296
297     if (m_contextStopped || !scriptExecutionContext())
298         return;
299
300     enqueueEvent(Event::create(eventNames().abortEvent, true, false));
301 }
302
303 void IDBTransaction::onComplete()
304 {
305     ASSERT(m_state != Finished);
306     m_state = Finishing;
307     m_objectStoreCleanupMap.clear();
308     closeOpenCursors();
309     m_database->transactionFinished(this);
310
311     if (m_contextStopped || !scriptExecutionContext())
312         return;
313
314     enqueueEvent(Event::create(eventNames().completeEvent, false, false));
315 }
316
317 bool IDBTransaction::hasPendingActivity() const
318 {
319     // FIXME: In an ideal world, we should return true as long as anyone has a or can
320     //        get a handle to us or any child request object and any of those have
321     //        event listeners. This is  in order to handle user generated events properly.
322     return m_state != Finished || ActiveDOMObject::hasPendingActivity();
323 }
324
325 IDBTransaction::Mode IDBTransaction::stringToMode(const String& modeString, ExceptionCode& ec)
326 {
327     if (modeString.isNull()
328         || modeString == IDBTransaction::modeReadOnly())
329         return IDBTransaction::READ_ONLY;
330     if (modeString == IDBTransaction::modeReadWrite())
331         return IDBTransaction::READ_WRITE;
332     ec = IDBDatabaseException::IDB_TYPE_ERR;
333     return IDBTransaction::READ_ONLY;
334 }
335
336 const AtomicString& IDBTransaction::modeToString(IDBTransaction::Mode mode, ExceptionCode& ec)
337 {
338     switch (mode) {
339     case IDBTransaction::READ_ONLY:
340         return IDBTransaction::modeReadOnly();
341         break;
342
343     case IDBTransaction::READ_WRITE:
344         return IDBTransaction::modeReadWrite();
345         break;
346
347     case IDBTransaction::VERSION_CHANGE:
348         return IDBTransaction::modeVersionChange();
349         break;
350
351     default:
352         ec = IDBDatabaseException::IDB_TYPE_ERR;
353         return IDBTransaction::modeReadOnly();
354     }
355 }
356
357 const AtomicString& IDBTransaction::interfaceName() const
358 {
359     return eventNames().interfaceForIDBTransaction;
360 }
361
362 ScriptExecutionContext* IDBTransaction::scriptExecutionContext() const
363 {
364     return ActiveDOMObject::scriptExecutionContext();
365 }
366
367 bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
368 {
369     IDB_TRACE("IDBTransaction::dispatchEvent");
370     ASSERT(m_state != Finished);
371     ASSERT(scriptExecutionContext());
372     ASSERT(event->target() == this);
373     m_state = Finished;
374
375     // Break reference cycles.
376     for (IDBObjectStoreMap::iterator it = m_objectStoreMap.begin(); it != m_objectStoreMap.end(); ++it)
377         it->second->transactionFinished();
378     m_objectStoreMap.clear();
379
380     Vector<RefPtr<EventTarget> > targets;
381     targets.append(this);
382     targets.append(db());
383
384     // FIXME: When we allow custom event dispatching, this will probably need to change.
385     ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent);
386     return IDBEventDispatcher::dispatch(event.get(), targets);
387 }
388
389 bool IDBTransaction::canSuspend() const
390 {
391     // FIXME: Technically we can suspend before the first request is schedule
392     //        and after the complete/abort event is enqueued.
393     return m_state == Finished;
394 }
395
396 void IDBTransaction::stop()
397 {
398     ActiveDOMObject::stop();
399     m_contextStopped = true;
400
401     abort();
402 }
403
404 void IDBTransaction::enqueueEvent(PassRefPtr<Event> event)
405 {
406     ASSERT_WITH_MESSAGE(m_state != Finished, "A finished transaction tried to enqueue an event of type %s.", event->type().string().utf8().data());
407     if (m_contextStopped || !scriptExecutionContext())
408         return;
409
410     EventQueue* eventQueue = scriptExecutionContext()->eventQueue();
411     event->setTarget(this);
412     eventQueue->enqueueEvent(event);
413 }
414
415 EventTargetData* IDBTransaction::eventTargetData()
416 {
417     return &m_eventTargetData;
418 }
419
420 EventTargetData* IDBTransaction::ensureEventTargetData()
421 {
422     return &m_eventTargetData;
423 }
424
425 }
426
427 #endif // ENABLE(INDEXED_DATABASE)