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 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "IDBRequest.h"
31
32#if ENABLE(INDEXED_DATABASE)
33
34#include "Document.h"
35#include "EventException.h"
36#include "EventListener.h"
37#include "EventNames.h"
38#include "EventQueue.h"
39#include "IDBCursorWithValue.h"
40#include "IDBDatabase.h"
41#include "IDBEventDispatcher.h"
42#include "IDBPendingTransactionMonitor.h"
43#include "IDBTransaction.h"
44
45namespace WebCore {
46
47PassRefPtr<IDBRequest> IDBRequest::create(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
48{
49    return adoptRef(new IDBRequest(context, source, transaction));
50}
51
52IDBRequest::IDBRequest(ScriptExecutionContext* context, PassRefPtr<IDBAny> source, IDBTransaction* transaction)
53    : ActiveDOMObject(context, this)
54    , m_errorCode(0)
55    , m_source(source)
56    , m_transaction(transaction)
57    , m_readyState(LOADING)
58    , m_finished(false)
59    , m_cursorType(IDBCursorBackendInterface::InvalidCursorType)
60{
61    if (m_transaction) {
62        m_transaction->registerRequest(this);
63        IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend());
64    }
65}
66
67IDBRequest::~IDBRequest()
68{
69    ASSERT(m_readyState == DONE || m_readyState == EarlyDeath);
70    if (m_transaction)
71        m_transaction->unregisterRequest(this);
72}
73
74PassRefPtr<IDBAny> IDBRequest::result(ExceptionCode& ec) const
75{
76    if (m_readyState != DONE) {
77        ec = IDBDatabaseException::NOT_ALLOWED_ERR;
78        return 0;
79    }
80    return m_result;
81}
82
83unsigned short IDBRequest::errorCode(ExceptionCode& ec) const
84{
85    if (m_readyState != DONE) {
86        ec = IDBDatabaseException::NOT_ALLOWED_ERR;
87        return 0;
88    }
89    return m_errorCode;
90}
91
92String IDBRequest::webkitErrorMessage(ExceptionCode& ec) const
93{
94    if (m_readyState != DONE) {
95        ec = IDBDatabaseException::NOT_ALLOWED_ERR;
96        return String();
97    }
98    return m_errorMessage;
99}
100
101PassRefPtr<IDBAny> IDBRequest::source() const
102{
103    return m_source;
104}
105
106PassRefPtr<IDBTransaction> IDBRequest::transaction() const
107{
108    return m_transaction;
109}
110
111unsigned short IDBRequest::readyState() const
112{
113    ASSERT(m_readyState == LOADING || m_readyState == DONE);
114    return m_readyState;
115}
116
117void IDBRequest::markEarlyDeath()
118{
119    ASSERT(m_readyState == LOADING);
120    m_readyState = EarlyDeath;
121}
122
123bool IDBRequest::resetReadyState(IDBTransaction* transaction)
124{
125    ASSERT(!m_finished);
126    ASSERT(scriptExecutionContext());
127    ASSERT(transaction == m_transaction);
128    if (m_readyState != DONE)
129        return false;
130
131    m_readyState = LOADING;
132    m_result.clear();
133    m_errorCode = 0;
134    m_errorMessage = String();
135
136    IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend());
137
138    return true;
139}
140
141IDBAny* IDBRequest::source()
142{
143    return m_source.get();
144}
145
146void IDBRequest::abort()
147{
148    if (m_readyState != LOADING) {
149        ASSERT(m_readyState == DONE);
150        return;
151    }
152
153    ASSERT(scriptExecutionContext()->isDocument());
154    EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue();
155    for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
156        bool removed = eventQueue->cancelEvent(m_enqueuedEvents[i].get());
157        ASSERT_UNUSED(removed, removed);
158    }
159    m_enqueuedEvents.clear();
160
161    m_errorCode = 0;
162    m_errorMessage = String();
163    m_result.clear();
164    onError(IDBDatabaseError::create(IDBDatabaseException::ABORT_ERR, "The transaction was aborted, so the request cannot be fulfilled."));
165}
166
167void IDBRequest::setCursorType(IDBCursorBackendInterface::CursorType cursorType)
168{
169    ASSERT(m_cursorType == IDBCursorBackendInterface::InvalidCursorType);
170    m_cursorType = cursorType;
171}
172
173void IDBRequest::onError(PassRefPtr<IDBDatabaseError> error)
174{
175    ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
176    m_errorCode = error->code();
177    m_errorMessage = error->message();
178    enqueueEvent(Event::create(eventNames().errorEvent, true, true));
179}
180
181static PassRefPtr<Event> createSuccessEvent()
182{
183    return Event::create(eventNames().successEvent, false, false);
184}
185
186void IDBRequest::onSuccess(PassRefPtr<IDBCursorBackendInterface> backend)
187{
188    ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
189    ASSERT(m_cursorType != IDBCursorBackendInterface::InvalidCursorType);
190    if (m_cursorType == IDBCursorBackendInterface::IndexKeyCursor)
191        m_result = IDBAny::create(IDBCursor::create(backend, this, m_source.get(), m_transaction.get()));
192    else
193        m_result = IDBAny::create(IDBCursorWithValue::create(backend, this, m_source.get(), m_transaction.get()));
194    enqueueEvent(createSuccessEvent());
195}
196
197void IDBRequest::onSuccess(PassRefPtr<IDBDatabaseBackendInterface> backend)
198{
199    ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
200    RefPtr<IDBDatabase> idbDatabase = IDBDatabase::create(scriptExecutionContext(), backend);
201    idbDatabase->open();
202
203    m_result = IDBAny::create(idbDatabase.release());
204    enqueueEvent(createSuccessEvent());
205}
206
207void IDBRequest::onSuccess(PassRefPtr<IDBKey> idbKey)
208{
209    ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
210    m_result = IDBAny::create(idbKey);
211    enqueueEvent(createSuccessEvent());
212}
213
214void IDBRequest::onSuccess(PassRefPtr<IDBTransactionBackendInterface> prpBackend)
215{
216    ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
217    if (!scriptExecutionContext())
218        return;
219
220    RefPtr<IDBTransactionBackendInterface> backend = prpBackend;
221    RefPtr<IDBTransaction> frontend = IDBTransaction::create(scriptExecutionContext(), backend, m_source->idbDatabase().get());
222    backend->setCallbacks(frontend.get());
223    m_transaction = frontend;
224
225    ASSERT(m_source->type() == IDBAny::IDBDatabaseType);
226    m_source->idbDatabase()->setSetVersionTransaction(frontend.get());
227
228    IDBPendingTransactionMonitor::removePendingTransaction(m_transaction->backend());
229
230    m_result = IDBAny::create(frontend.release());
231    enqueueEvent(createSuccessEvent());
232}
233
234void IDBRequest::onSuccess(PassRefPtr<SerializedScriptValue> serializedScriptValue)
235{
236    ASSERT(!m_errorCode && m_errorMessage.isNull() && !m_result);
237    m_result = IDBAny::create(serializedScriptValue);
238    enqueueEvent(createSuccessEvent());
239}
240
241bool IDBRequest::hasPendingActivity() const
242{
243    // FIXME: In an ideal world, we should return true as long as anyone has a or can
244    //        get a handle to us and we have event listeners. This is order to handle
245    //        user generated events properly.
246    return !m_finished || ActiveDOMObject::hasPendingActivity();
247}
248
249void IDBRequest::onBlocked()
250{
251    ASSERT_NOT_REACHED();
252}
253
254ScriptExecutionContext* IDBRequest::scriptExecutionContext() const
255{
256    return ActiveDOMObject::scriptExecutionContext();
257}
258
259bool IDBRequest::dispatchEvent(PassRefPtr<Event> event)
260{
261    ASSERT(!m_finished);
262    ASSERT(m_enqueuedEvents.size());
263    ASSERT(scriptExecutionContext());
264    ASSERT(event->target() == this);
265    ASSERT(m_readyState < DONE);
266    if (event->type() != eventNames().blockedEvent)
267        m_readyState = DONE;
268
269    for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
270        if (m_enqueuedEvents[i].get() == event.get())
271            m_enqueuedEvents.remove(i);
272    }
273
274    Vector<RefPtr<EventTarget> > targets;
275    targets.append(this);
276    if (m_transaction) {
277        targets.append(m_transaction);
278        // If there ever are events that are associated with a database but
279        // that do not have a transaction, then this will not work and we need
280        // this object to actually hold a reference to the database (to ensure
281        // it stays alive).
282        targets.append(m_transaction->db());
283    }
284
285    // FIXME: When we allow custom event dispatching, this will probably need to change.
286    ASSERT(event->type() == eventNames().successEvent || event->type() == eventNames().errorEvent || event->type() == eventNames().blockedEvent);
287    bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
288
289    // If the result was of type IDBCursor, then we'll fire again.
290    if (m_result && m_result->type() != IDBAny::IDBCursorType && m_result->type() != IDBAny::IDBCursorWithValueType)
291        m_finished = true;
292
293    if (m_transaction) {
294        // If an error event and the default wasn't prevented...
295        if (dontPreventDefault && event->type() == eventNames().errorEvent)
296            m_transaction->backend()->abort();
297        m_transaction->backend()->didCompleteTaskEvents();
298    }
299    return dontPreventDefault;
300}
301
302void IDBRequest::uncaughtExceptionInEventHandler()
303{
304    if (m_transaction)
305        m_transaction->backend()->abort();
306}
307
308void IDBRequest::enqueueEvent(PassRefPtr<Event> event)
309{
310    ASSERT(!m_finished);
311    ASSERT(m_readyState < DONE);
312    if (!scriptExecutionContext())
313        return;
314
315    ASSERT(scriptExecutionContext()->isDocument());
316    EventQueue* eventQueue = static_cast<Document*>(scriptExecutionContext())->eventQueue();
317    event->setTarget(this);
318    eventQueue->enqueueEvent(event.get());
319    m_enqueuedEvents.append(event);
320}
321
322EventTargetData* IDBRequest::eventTargetData()
323{
324    return &m_eventTargetData;
325}
326
327EventTargetData* IDBRequest::ensureEventTargetData()
328{
329    return &m_eventTargetData;
330}
331
332} // namespace WebCore
333
334#endif
335