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 "Document.h"
32#include "EventException.h"
33#include "EventQueue.h"
34#include "IDBDatabase.h"
35#include "IDBDatabaseException.h"
36#include "IDBEventDispatcher.h"
37#include "IDBIndex.h"
38#include "IDBObjectStore.h"
39#include "IDBObjectStoreBackendInterface.h"
40#include "IDBPendingTransactionMonitor.h"
41
42namespace WebCore {
43
44PassRefPtr<IDBTransaction> IDBTransaction::create(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBDatabase* db)
45{
46    return adoptRef(new IDBTransaction(context, backend, db));
47}
48
49IDBTransaction::IDBTransaction(ScriptExecutionContext* context, PassRefPtr<IDBTransactionBackendInterface> backend, IDBDatabase* db)
50    : ActiveDOMObject(context, this)
51    , m_backend(backend)
52    , m_database(db)
53    , m_mode(m_backend->mode())
54    , m_finished(false)
55{
56    ASSERT(m_backend);
57    IDBPendingTransactionMonitor::addPendingTransaction(m_backend.get());
58}
59
60IDBTransaction::~IDBTransaction()
61{
62}
63
64IDBTransactionBackendInterface* IDBTransaction::backend() const
65{
66    return m_backend.get();
67}
68
69bool IDBTransaction::finished() const
70{
71    return m_finished;
72}
73
74unsigned short IDBTransaction::mode() const
75{
76    return m_mode;
77}
78
79IDBDatabase* IDBTransaction::db() const
80{
81    return m_database.get();
82}
83
84PassRefPtr<IDBObjectStore> IDBTransaction::objectStore(const String& name, ExceptionCode& ec)
85{
86    if (m_finished) {
87        ec = IDBDatabaseException::NOT_ALLOWED_ERR;
88        return 0;
89    }
90    RefPtr<IDBObjectStoreBackendInterface> objectStoreBackend = m_backend->objectStore(name, ec);
91    if (!objectStoreBackend) {
92        ASSERT(ec);
93        return 0;
94    }
95    RefPtr<IDBObjectStore> objectStore = IDBObjectStore::create(objectStoreBackend, this);
96    return objectStore.release();
97}
98
99void IDBTransaction::abort()
100{
101    RefPtr<IDBTransaction> selfRef = this;
102    if (m_backend)
103        m_backend->abort();
104}
105
106void IDBTransaction::registerRequest(IDBRequest* request)
107{
108    m_childRequests.add(request);
109}
110
111void IDBTransaction::unregisterRequest(IDBRequest* request)
112{
113    // If we aborted the request, it will already have been removed.
114    m_childRequests.remove(request);
115}
116
117void IDBTransaction::onAbort()
118{
119    while (!m_childRequests.isEmpty()) {
120        IDBRequest* request = *m_childRequests.begin();
121        m_childRequests.remove(request);
122        request->abort();
123    }
124
125    enqueueEvent(Event::create(eventNames().abortEvent, true, false));
126}
127
128void IDBTransaction::onComplete()
129{
130    enqueueEvent(Event::create(eventNames().completeEvent, false, false));
131}
132
133bool IDBTransaction::hasPendingActivity() const
134{
135    // FIXME: In an ideal world, we should return true as long as anyone has a or can
136    //        get a handle to us or any child request object and any of those have
137    //        event listeners. This is  in order to handle user generated events properly.
138    return !m_finished || ActiveDOMObject::hasPendingActivity();
139}
140
141ScriptExecutionContext* IDBTransaction::scriptExecutionContext() const
142{
143    return ActiveDOMObject::scriptExecutionContext();
144}
145
146bool IDBTransaction::dispatchEvent(PassRefPtr<Event> event)
147{
148    ASSERT(!m_finished);
149    ASSERT(scriptExecutionContext());
150    ASSERT(event->target() == this);
151    ASSERT(!m_finished);
152    m_finished = true;
153
154    Vector<RefPtr<EventTarget> > targets;
155    targets.append(this);
156    targets.append(db());
157
158    // FIXME: When we allow custom event dispatching, this will probably need to change.
159    ASSERT(event->type() == eventNames().completeEvent || event->type() == eventNames().abortEvent);
160    return IDBEventDispatcher::dispatch(event.get(), targets);
161}
162
163bool IDBTransaction::canSuspend() const
164{
165    // FIXME: Technically we can suspend before the first request is schedule
166    //        and after the complete/abort event is enqueued.
167    return m_finished;
168}
169
170void IDBTransaction::contextDestroyed()
171{
172    ActiveDOMObject::contextDestroyed();
173
174    // Must happen in contextDestroyed since it can result in ActiveDOMObjects being destructed
175    // (and contextDestroyed is the only one resilient against this).
176    RefPtr<IDBTransaction> selfRef = this;
177    if (m_backend)
178        m_backend->abort();
179
180    m_finished = true;
181}
182
183void IDBTransaction::enqueueEvent(PassRefPtr<Event> event)
184{
185    ASSERT(!m_finished);
186    if (!scriptExecutionContext())
187        return;
188
189    ASSERT(scriptExecutionContext()->isDocument());
190    EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue();
191    event->setTarget(this);
192    eventQueue->enqueueEvent(event);
193}
194
195EventTargetData* IDBTransaction::eventTargetData()
196{
197    return &m_eventTargetData;
198}
199
200EventTargetData* IDBTransaction::ensureEventTargetData()
201{
202    return &m_eventTargetData;
203}
204
205}
206
207#endif // ENABLE(INDEXED_DATABASE)
208