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 "modules/indexeddb/IDBRequest.h"
31
32#include "bindings/core/v8/ExceptionState.h"
33#include "bindings/core/v8/ExceptionStatePlaceholder.h"
34#include "bindings/modules/v8/IDBBindingUtilities.h"
35#include "core/dom/ExecutionContext.h"
36#include "core/events/EventQueue.h"
37#include "modules/IndexedDBNames.h"
38#include "modules/indexeddb/IDBCursorWithValue.h"
39#include "modules/indexeddb/IDBDatabase.h"
40#include "modules/indexeddb/IDBEventDispatcher.h"
41#include "modules/indexeddb/IDBPendingTransactionMonitor.h"
42#include "modules/indexeddb/IDBTracing.h"
43#include "platform/SharedBuffer.h"
44#include "public/platform/WebBlobInfo.h"
45
46using blink::WebIDBCursor;
47
48namespace blink {
49
50IDBRequest* IDBRequest::create(ScriptState* scriptState, IDBAny* source, IDBTransaction* transaction)
51{
52    IDBRequest* request = adoptRefCountedGarbageCollectedWillBeNoop(new IDBRequest(scriptState, source, transaction));
53    request->suspendIfNeeded();
54    // Requests associated with IDBFactory (open/deleteDatabase/getDatabaseNames) are not associated with transactions.
55    if (transaction)
56        transaction->registerRequest(request);
57    return request;
58}
59
60IDBRequest::IDBRequest(ScriptState* scriptState, IDBAny* source, IDBTransaction* transaction)
61    : ActiveDOMObject(scriptState->executionContext())
62    , m_contextStopped(false)
63    , m_transaction(transaction)
64    , m_readyState(PENDING)
65    , m_requestAborted(false)
66    , m_scriptState(scriptState)
67    , m_source(source)
68    , m_hasPendingActivity(true)
69    , m_cursorType(IndexedDB::CursorKeyAndValue)
70    , m_cursorDirection(WebIDBCursorDirectionNext)
71    , m_pendingCursor(nullptr)
72    , m_didFireUpgradeNeededEvent(false)
73    , m_preventPropagation(false)
74    , m_resultDirty(true)
75{
76}
77
78IDBRequest::~IDBRequest()
79{
80    ASSERT(m_readyState == DONE || m_readyState == EarlyDeath || !executionContext());
81    ASSERT(!m_blobInfo || m_blobInfo->size() == 0);
82}
83
84void IDBRequest::dispose()
85{
86    handleBlobAcks();
87}
88
89void IDBRequest::trace(Visitor* visitor)
90{
91    visitor->trace(m_transaction);
92    visitor->trace(m_source);
93    visitor->trace(m_result);
94    visitor->trace(m_error);
95#if ENABLE(OILPAN)
96    visitor->trace(m_enqueuedEvents);
97#endif
98    visitor->trace(m_pendingCursor);
99    visitor->trace(m_cursorKey);
100    visitor->trace(m_cursorPrimaryKey);
101    EventTargetWithInlineData::trace(visitor);
102}
103
104ScriptValue IDBRequest::result(ExceptionState& exceptionState)
105{
106    if (m_readyState != DONE) {
107        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage);
108        return ScriptValue();
109    }
110    if (m_contextStopped || !executionContext())
111        return ScriptValue();
112    m_resultDirty = false;
113    ScriptValue value = idbAnyToScriptValue(m_scriptState.get(), m_result);
114    handleBlobAcks();
115    return value;
116}
117
118PassRefPtrWillBeRawPtr<DOMError> IDBRequest::error(ExceptionState& exceptionState) const
119{
120    if (m_readyState != DONE) {
121        exceptionState.throwDOMException(InvalidStateError, IDBDatabase::requestNotFinishedErrorMessage);
122        return nullptr;
123    }
124    return m_error;
125}
126
127ScriptValue IDBRequest::source() const
128{
129    if (m_contextStopped || !executionContext())
130        return ScriptValue();
131
132    return idbAnyToScriptValue(m_scriptState.get(), m_source);
133}
134
135const String& IDBRequest::readyState() const
136{
137    ASSERT(m_readyState == PENDING || m_readyState == DONE);
138
139    if (m_readyState == PENDING)
140        return IndexedDBNames::pending;
141
142    return IndexedDBNames::done;
143}
144
145void IDBRequest::abort()
146{
147    ASSERT(!m_requestAborted);
148    if (m_contextStopped || !executionContext())
149        return;
150    ASSERT(m_readyState == PENDING || m_readyState == DONE);
151    if (m_readyState == DONE)
152        return;
153
154    EventQueue* eventQueue = executionContext()->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_error.clear();
162    m_result.clear();
163    onError(DOMError::create(AbortError, "The transaction was aborted, so the request cannot be fulfilled."));
164    m_requestAborted = true;
165}
166
167void IDBRequest::setCursorDetails(IndexedDB::CursorType cursorType, WebIDBCursorDirection direction)
168{
169    ASSERT(m_readyState == PENDING);
170    ASSERT(!m_pendingCursor);
171    m_cursorType = cursorType;
172    m_cursorDirection = direction;
173}
174
175void IDBRequest::setPendingCursor(IDBCursor* cursor)
176{
177    ASSERT(m_readyState == DONE);
178    ASSERT(executionContext());
179    ASSERT(m_transaction);
180    ASSERT(!m_pendingCursor);
181    ASSERT(cursor == getResultCursor());
182
183    m_hasPendingActivity = true;
184    m_pendingCursor = cursor;
185    setResult(0);
186    m_readyState = PENDING;
187    m_error.clear();
188    m_transaction->registerRequest(this);
189}
190
191IDBCursor* IDBRequest::getResultCursor() const
192{
193    if (!m_result)
194        return 0;
195    if (m_result->type() == IDBAny::IDBCursorType)
196        return m_result->idbCursor();
197    if (m_result->type() == IDBAny::IDBCursorWithValueType)
198        return m_result->idbCursorWithValue();
199    return 0;
200}
201
202void IDBRequest::setResultCursor(IDBCursor* cursor, IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
203{
204    ASSERT(m_readyState == PENDING);
205    m_cursorKey = key;
206    m_cursorPrimaryKey = primaryKey;
207    m_cursorValue = value;
208    setBlobInfo(blobInfo);
209
210    onSuccessInternal(IDBAny::create(cursor));
211}
212
213void IDBRequest::setBlobInfo(PassOwnPtr<Vector<WebBlobInfo>> blobInfo)
214{
215    ASSERT(!m_blobInfo);
216    m_blobInfo = blobInfo;
217    if (m_blobInfo && m_blobInfo->size() > 0)
218        V8PerIsolateData::from(scriptState()->isolate())->ensureIDBPendingTransactionMonitor()->registerRequest(*this);
219}
220
221void IDBRequest::handleBlobAcks()
222{
223    if (m_blobInfo.get() && m_blobInfo->size()) {
224        m_transaction->db()->ackReceivedBlobs(m_blobInfo.get());
225        m_blobInfo.clear();
226        V8PerIsolateData::from(scriptState()->isolate())->ensureIDBPendingTransactionMonitor()->unregisterRequest(*this);
227    }
228}
229
230bool IDBRequest::shouldEnqueueEvent() const
231{
232    if (m_contextStopped || !executionContext())
233        return false;
234    ASSERT(m_readyState == PENDING || m_readyState == DONE);
235    if (m_requestAborted)
236        return false;
237    ASSERT(m_readyState == PENDING);
238    ASSERT(!m_error && !m_result);
239    return true;
240}
241
242void IDBRequest::onError(PassRefPtrWillBeRawPtr<DOMError> error)
243{
244    IDB_TRACE("IDBRequest::onError()");
245    if (!shouldEnqueueEvent())
246        return;
247
248    m_error = error;
249    setResult(IDBAny::createUndefined());
250    m_pendingCursor.clear();
251    enqueueEvent(Event::createCancelableBubble(EventTypeNames::error));
252}
253
254void IDBRequest::onSuccess(const Vector<String>& stringList)
255{
256    IDB_TRACE("IDBRequest::onSuccess(StringList)");
257    if (!shouldEnqueueEvent())
258        return;
259
260    RefPtrWillBeRawPtr<DOMStringList> domStringList = DOMStringList::create();
261    for (size_t i = 0; i < stringList.size(); ++i)
262        domStringList->append(stringList[i]);
263    onSuccessInternal(IDBAny::create(domStringList.release()));
264}
265
266void IDBRequest::onSuccess(PassOwnPtr<WebIDBCursor> backend, IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
267{
268    IDB_TRACE("IDBRequest::onSuccess(IDBCursor)");
269    if (!shouldEnqueueEvent())
270        return;
271
272    ASSERT(!m_pendingCursor);
273    IDBCursor* cursor = 0;
274    switch (m_cursorType) {
275    case IndexedDB::CursorKeyOnly:
276        cursor = IDBCursor::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
277        break;
278    case IndexedDB::CursorKeyAndValue:
279        cursor = IDBCursorWithValue::create(backend, m_cursorDirection, this, m_source.get(), m_transaction.get());
280        break;
281    default:
282        ASSERT_NOT_REACHED();
283    }
284    setResultCursor(cursor, key, primaryKey, value, blobInfo);
285}
286
287void IDBRequest::onSuccess(IDBKey* idbKey)
288{
289    IDB_TRACE("IDBRequest::onSuccess(IDBKey)");
290    if (!shouldEnqueueEvent())
291        return;
292
293    if (idbKey && idbKey->isValid())
294        onSuccessInternal(IDBAny::create(idbKey));
295    else
296        onSuccessInternal(IDBAny::createUndefined());
297}
298
299void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> valueBuffer, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
300{
301    IDB_TRACE("IDBRequest::onSuccess(SharedBuffer)");
302    if (!shouldEnqueueEvent())
303        return;
304
305    if (m_pendingCursor) {
306        // Value should be null, signifying the end of the cursor's range.
307        ASSERT(!valueBuffer.get());
308        ASSERT(!blobInfo->size());
309        m_pendingCursor->close();
310        m_pendingCursor.clear();
311    }
312
313    setBlobInfo(blobInfo);
314    onSuccessInternal(IDBAny::create(valueBuffer, m_blobInfo.get()));
315}
316
317#if ENABLE(ASSERT)
318static IDBObjectStore* effectiveObjectStore(IDBAny* source)
319{
320    if (source->type() == IDBAny::IDBObjectStoreType)
321        return source->idbObjectStore();
322    if (source->type() == IDBAny::IDBIndexType)
323        return source->idbIndex()->objectStore();
324
325    ASSERT_NOT_REACHED();
326    return 0;
327}
328#endif
329
330void IDBRequest::onSuccess(PassRefPtr<SharedBuffer> prpValueBuffer, PassOwnPtr<Vector<WebBlobInfo> > blobInfo, IDBKey* prpPrimaryKey, const IDBKeyPath& keyPath)
331{
332    IDB_TRACE("IDBRequest::onSuccess(SharedBuffer, IDBKey, IDBKeyPath)");
333    if (!shouldEnqueueEvent())
334        return;
335
336    ASSERT(keyPath == effectiveObjectStore(m_source)->metadata().keyPath);
337
338    RefPtr<SharedBuffer> valueBuffer = prpValueBuffer;
339    IDBKey* primaryKey = prpPrimaryKey;
340    setBlobInfo(blobInfo);
341
342#if ENABLE(ASSERT)
343    assertPrimaryKeyValidOrInjectable(m_scriptState.get(), valueBuffer, m_blobInfo.get(), primaryKey, keyPath);
344#endif
345
346    onSuccessInternal(IDBAny::create(valueBuffer, m_blobInfo.get(), primaryKey, keyPath));
347}
348
349void IDBRequest::onSuccess(int64_t value)
350{
351    IDB_TRACE("IDBRequest::onSuccess(int64_t)");
352    if (!shouldEnqueueEvent())
353        return;
354    onSuccessInternal(IDBAny::create(value));
355}
356
357void IDBRequest::onSuccess()
358{
359    IDB_TRACE("IDBRequest::onSuccess()");
360    if (!shouldEnqueueEvent())
361        return;
362    onSuccessInternal(IDBAny::createUndefined());
363}
364
365void IDBRequest::onSuccessInternal(IDBAny* result)
366{
367    ASSERT(!m_contextStopped);
368    ASSERT(!m_pendingCursor);
369    setResult(result);
370    enqueueEvent(Event::create(EventTypeNames::success));
371}
372
373void IDBRequest::setResult(IDBAny* result)
374{
375    m_result = result;
376    m_resultDirty = true;
377}
378
379void IDBRequest::onSuccess(IDBKey* key, IDBKey* primaryKey, PassRefPtr<SharedBuffer> value, PassOwnPtr<Vector<WebBlobInfo> > blobInfo)
380{
381    IDB_TRACE("IDBRequest::onSuccess(key, primaryKey, value)");
382    if (!shouldEnqueueEvent())
383        return;
384
385    ASSERT(m_pendingCursor);
386    setResultCursor(m_pendingCursor.release(), key, primaryKey, value, blobInfo);
387}
388
389bool IDBRequest::hasPendingActivity() const
390{
391    // FIXME: In an ideal world, we should return true as long as anyone has a or can
392    //        get a handle to us and we have event listeners. This is order to handle
393    //        user generated events properly.
394    return m_hasPendingActivity && !m_contextStopped;
395}
396
397void IDBRequest::stop()
398{
399    if (m_contextStopped)
400        return;
401
402    m_contextStopped = true;
403
404    if (m_readyState == PENDING) {
405        m_readyState = EarlyDeath;
406        if (m_transaction) {
407            m_transaction->unregisterRequest(this);
408            m_transaction.clear();
409        }
410    }
411
412    m_enqueuedEvents.clear();
413    if (m_source)
414        m_source->contextWillBeDestroyed();
415    if (m_result)
416        m_result->contextWillBeDestroyed();
417    if (m_pendingCursor)
418        m_pendingCursor->contextWillBeDestroyed();
419}
420
421const AtomicString& IDBRequest::interfaceName() const
422{
423    return EventTargetNames::IDBRequest;
424}
425
426ExecutionContext* IDBRequest::executionContext() const
427{
428    return ActiveDOMObject::executionContext();
429}
430
431bool IDBRequest::dispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
432{
433    IDB_TRACE("IDBRequest::dispatchEvent");
434    if (m_contextStopped || !executionContext())
435        return false;
436    ASSERT(m_readyState == PENDING);
437    ASSERT(m_hasPendingActivity);
438    ASSERT(m_enqueuedEvents.size());
439    ASSERT(event->target() == this);
440
441    ScriptState::Scope scope(m_scriptState.get());
442
443    if (event->type() != EventTypeNames::blocked)
444        m_readyState = DONE;
445    dequeueEvent(event.get());
446
447    WillBeHeapVector<RefPtrWillBeMember<EventTarget> > targets;
448    targets.append(this);
449    if (m_transaction && !m_preventPropagation) {
450        targets.append(m_transaction);
451        // If there ever are events that are associated with a database but
452        // that do not have a transaction, then this will not work and we need
453        // this object to actually hold a reference to the database (to ensure
454        // it stays alive).
455        targets.append(m_transaction->db());
456    }
457
458    // Cursor properties should not be updated until the success event is being dispatched.
459    IDBCursor* cursorToNotify = 0;
460    if (event->type() == EventTypeNames::success) {
461        cursorToNotify = getResultCursor();
462        if (cursorToNotify) {
463            if (m_blobInfo && m_blobInfo->size() > 0)
464                V8PerIsolateData::from(scriptState()->isolate())->ensureIDBPendingTransactionMonitor()->unregisterRequest(*this);
465            cursorToNotify->setValueReady(m_cursorKey.release(), m_cursorPrimaryKey.release(), m_cursorValue.release(), m_blobInfo.release());
466        }
467    }
468
469    if (event->type() == EventTypeNames::upgradeneeded) {
470        ASSERT(!m_didFireUpgradeNeededEvent);
471        m_didFireUpgradeNeededEvent = true;
472    }
473
474    // FIXME: When we allow custom event dispatching, this will probably need to change.
475    ASSERT_WITH_MESSAGE(event->type() == EventTypeNames::success || event->type() == EventTypeNames::error || event->type() == EventTypeNames::blocked || event->type() == EventTypeNames::upgradeneeded, "event type was %s", event->type().utf8().data());
476    const bool setTransactionActive = m_transaction && (event->type() == EventTypeNames::success || event->type() == EventTypeNames::upgradeneeded || (event->type() == EventTypeNames::error && !m_requestAborted));
477
478    if (setTransactionActive)
479        m_transaction->setActive(true);
480
481    bool dontPreventDefault = IDBEventDispatcher::dispatch(event.get(), targets);
482
483    if (m_transaction) {
484        if (m_readyState == DONE)
485            m_transaction->unregisterRequest(this);
486
487        // Possibly abort the transaction. This must occur after unregistering (so this request
488        // doesn't receive a second error) and before deactivating (which might trigger commit).
489        if (event->type() == EventTypeNames::error && dontPreventDefault && !m_requestAborted) {
490            m_transaction->setError(m_error);
491            m_transaction->abort(IGNORE_EXCEPTION);
492        }
493
494        // If this was the last request in the transaction's list, it may commit here.
495        if (setTransactionActive)
496            m_transaction->setActive(false);
497    }
498
499    if (cursorToNotify)
500        cursorToNotify->postSuccessHandlerCallback();
501
502    // An upgradeneeded event will always be followed by a success or error event, so must
503    // be kept alive.
504    if (m_readyState == DONE && event->type() != EventTypeNames::upgradeneeded)
505        m_hasPendingActivity = false;
506
507    return dontPreventDefault;
508}
509
510void IDBRequest::uncaughtExceptionInEventHandler()
511{
512    if (m_transaction && !m_requestAborted) {
513        m_transaction->setError(DOMError::create(AbortError, "Uncaught exception in event handler."));
514        m_transaction->abort(IGNORE_EXCEPTION);
515    }
516}
517
518void IDBRequest::transactionDidFinishAndDispatch()
519{
520    ASSERT(m_transaction);
521    ASSERT(m_transaction->isVersionChange());
522    ASSERT(m_didFireUpgradeNeededEvent);
523    ASSERT(m_readyState == DONE);
524    ASSERT(executionContext());
525    m_transaction.clear();
526
527    if (m_contextStopped)
528        return;
529
530    m_readyState = PENDING;
531}
532
533void IDBRequest::enqueueEvent(PassRefPtrWillBeRawPtr<Event> event)
534{
535    ASSERT(m_readyState == PENDING || m_readyState == DONE);
536
537    if (m_contextStopped || !executionContext())
538        return;
539
540    ASSERT_WITH_MESSAGE(m_readyState == PENDING || m_didFireUpgradeNeededEvent, "When queueing event %s, m_readyState was %d", event->type().utf8().data(), m_readyState);
541
542    EventQueue* eventQueue = executionContext()->eventQueue();
543    event->setTarget(this);
544
545    // Keep track of enqueued events in case we need to abort prior to dispatch,
546    // in which case these must be cancelled. If the events not dispatched for
547    // other reasons they must be removed from this list via dequeueEvent().
548    if (eventQueue->enqueueEvent(event.get()))
549        m_enqueuedEvents.append(event);
550}
551
552void IDBRequest::dequeueEvent(Event* event)
553{
554    for (size_t i = 0; i < m_enqueuedEvents.size(); ++i) {
555        if (m_enqueuedEvents[i].get() == event)
556            m_enqueuedEvents.remove(i);
557    }
558}
559
560} // namespace blink
561