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