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