Blob type cannot be stored correctly in IDB when IDBObjectStore has autoIncrement...
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBRequest.cpp
1 /*
2  * Copyright (C) 2015-2017 Apple 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  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "IDBRequest.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "DOMException.h"
32 #include "Event.h"
33 #include "EventDispatcher.h"
34 #include "EventNames.h"
35 #include "EventQueue.h"
36 #include "IDBBindingUtilities.h"
37 #include "IDBConnectionProxy.h"
38 #include "IDBCursor.h"
39 #include "IDBDatabase.h"
40 #include "IDBIndex.h"
41 #include "IDBKeyData.h"
42 #include "IDBObjectStore.h"
43 #include "IDBResultData.h"
44 #include "JSDOMConvertIndexedDB.h"
45 #include "JSDOMConvertNumbers.h"
46 #include "JSDOMConvertSequences.h"
47 #include "Logging.h"
48 #include "ScriptExecutionContext.h"
49 #include "ThreadSafeDataBuffer.h"
50 #include <JavaScriptCore/StrongInlines.h>
51 #include <wtf/Scope.h>
52 #include <wtf/Variant.h>
53
54
55 namespace WebCore {
56 using namespace JSC;
57
58 Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
59 {
60     return adoptRef(*new IDBRequest(context, objectStore, transaction));
61 }
62
63 Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
64 {
65     return adoptRef(*new IDBRequest(context, cursor, transaction));
66 }
67
68 Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
69 {
70     return adoptRef(*new IDBRequest(context, index, transaction));
71 }
72
73 Ref<IDBRequest> IDBRequest::createObjectStoreGet(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction)
74 {
75     return adoptRef(*new IDBRequest(context, objectStore, type, transaction));
76 }
77
78 Ref<IDBRequest> IDBRequest::createIndexGet(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
79 {
80     return adoptRef(*new IDBRequest(context, index, requestedRecordType, transaction));
81 }
82
83 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBClient::IDBConnectionProxy& connectionProxy)
84     : IDBActiveDOMObject(&context)
85     , m_resourceIdentifier(connectionProxy)
86     , m_connectionProxy(connectionProxy)
87 {
88     m_result = NullResultType::Undefined;
89     suspendIfNeeded();
90 }
91
92 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
93     : IDBActiveDOMObject(&context)
94     , m_transaction(&transaction)
95     , m_resourceIdentifier(transaction.connectionProxy())
96     , m_source(&objectStore)
97     , m_connectionProxy(transaction.database().connectionProxy())
98 {
99     m_result = NullResultType::Undefined;
100     suspendIfNeeded();
101 }
102
103 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
104     : IDBActiveDOMObject(&context)
105     , m_transaction(&transaction)
106     , m_resourceIdentifier(transaction.connectionProxy())
107     , m_pendingCursor(&cursor)
108     , m_connectionProxy(transaction.database().connectionProxy())
109 {
110     suspendIfNeeded();
111
112     WTF::switchOn(cursor.source(),
113         [this] (const auto& value) { this->m_source = IDBRequest::Source { value }; }
114     );
115
116     m_result = NullResultType::Undefined;
117     cursor.setRequest(*this);
118 }
119
120 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
121     : IDBActiveDOMObject(&context)
122     , m_transaction(&transaction)
123     , m_resourceIdentifier(transaction.connectionProxy())
124     , m_source(&index)
125     , m_connectionProxy(transaction.database().connectionProxy())
126 {
127     m_result = NullResultType::Undefined;
128     suspendIfNeeded();
129 }
130
131 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction)
132     : IDBActiveDOMObject(&context)
133     , m_transaction(&transaction)
134     , m_resourceIdentifier(transaction.connectionProxy())
135     , m_source(&objectStore)
136     , m_requestedObjectStoreRecordType(type)
137     , m_connectionProxy(transaction.database().connectionProxy())
138 {
139     m_result = NullResultType::Undefined;
140     suspendIfNeeded();
141 }
142
143 IDBRequest::IDBRequest(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
144     : IDBRequest(context, index, transaction)
145 {
146     m_result = NullResultType::Undefined;
147     m_requestedIndexRecordType = requestedRecordType;
148 }
149
150 IDBRequest::~IDBRequest()
151 {
152     ASSERT(&originThread() == &Thread::current());
153
154     WTF::switchOn(m_result,
155         [] (RefPtr<IDBCursor>& cursor) { cursor->clearRequest(); },
156         [] (const auto&) { }
157     );
158 }
159
160 ExceptionOr<IDBRequest::Result> IDBRequest::result() const
161 {
162     if (!isDone())
163         return Exception { InvalidStateError, "Failed to read the 'result' property from 'IDBRequest': The request has not finished."_s };
164
165     return IDBRequest::Result { m_result };
166 }
167
168 ExceptionOr<DOMException*> IDBRequest::error() const
169 {
170     ASSERT(&originThread() == &Thread::current());
171
172     if (!isDone())
173         return Exception { InvalidStateError, "Failed to read the 'error' property from 'IDBRequest': The request has not finished."_s };
174
175     return m_domError.get();
176 }
177
178 void IDBRequest::setSource(IDBCursor& cursor)
179 {
180     ASSERT(&originThread() == &Thread::current());
181
182     m_source = Source { &cursor };
183 }
184
185 void IDBRequest::setVersionChangeTransaction(IDBTransaction& transaction)
186 {
187     ASSERT(&originThread() == &Thread::current());
188     ASSERT(!m_transaction);
189     ASSERT(transaction.isVersionChange());
190     ASSERT(!transaction.isFinishedOrFinishing());
191
192     m_transaction = &transaction;
193 }
194
195 RefPtr<WebCore::IDBTransaction> IDBRequest::transaction() const
196 {
197     ASSERT(&originThread() == &Thread::current());
198     return m_shouldExposeTransactionToDOM ? m_transaction : nullptr;
199 }
200
201 uint64_t IDBRequest::sourceObjectStoreIdentifier() const
202 {
203     ASSERT(&originThread() == &Thread::current());
204
205     if (!m_source)
206         return 0;
207
208     return WTF::switchOn(m_source.value(),
209         [] (const RefPtr<IDBObjectStore>& objectStore) { return objectStore->info().identifier(); },
210         [] (const RefPtr<IDBIndex>& index) { return index->info().objectStoreIdentifier(); },
211         [] (const RefPtr<IDBCursor>&) { return 0; }
212     );
213 }
214
215 uint64_t IDBRequest::sourceIndexIdentifier() const
216 {
217     ASSERT(&originThread() == &Thread::current());
218
219     if (!m_source)
220         return 0;
221
222     return WTF::switchOn(m_source.value(),
223         [] (const RefPtr<IDBObjectStore>&) -> uint64_t { return 0; },
224         [] (const RefPtr<IDBIndex>& index) -> uint64_t { return index->info().identifier(); },
225         [] (const RefPtr<IDBCursor>&) -> uint64_t { return 0; }
226     );
227 }
228
229 IndexedDB::ObjectStoreRecordType IDBRequest::requestedObjectStoreRecordType() const
230 {
231     ASSERT(&originThread() == &Thread::current());
232
233     return m_requestedObjectStoreRecordType;
234 }
235
236 IndexedDB::IndexRecordType IDBRequest::requestedIndexRecordType() const
237 {
238     ASSERT(&originThread() == &Thread::current());
239     ASSERT(m_source);
240     ASSERT(WTF::holds_alternative<RefPtr<IDBIndex>>(m_source.value()));
241
242     return m_requestedIndexRecordType;
243 }
244
245 EventTargetInterface IDBRequest::eventTargetInterface() const
246 {
247     ASSERT(&originThread() == &Thread::current());
248
249     return IDBRequestEventTargetInterfaceType;
250 }
251
252 const char* IDBRequest::activeDOMObjectName() const
253 {
254     ASSERT(&originThread() == &Thread::current());
255
256     return "IDBRequest";
257 }
258
259 bool IDBRequest::canSuspendForDocumentSuspension() const
260 {
261     ASSERT(&originThread() == &Thread::current());
262     return false;
263 }
264
265 bool IDBRequest::hasPendingActivity() const
266 {
267     ASSERT(&originThread() == &Thread::current() || mayBeGCThread());
268     return !m_contextStopped && m_hasPendingActivity;
269 }
270
271 void IDBRequest::stop()
272 {
273     ASSERT(&originThread() == &Thread::current());
274     ASSERT(!m_contextStopped);
275
276     cancelForStop();
277
278     removeAllEventListeners();
279
280     clearWrappers();
281
282     m_contextStopped = true;
283 }
284
285 void IDBRequest::cancelForStop()
286 {
287     // The base IDBRequest class has nothing additional to do here.
288 }
289
290 void IDBRequest::enqueueEvent(Ref<Event>&& event)
291 {
292     ASSERT(&originThread() == &Thread::current());
293     if (m_contextStopped)
294         return;
295
296     event->setTarget(this);
297     scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
298 }
299
300 void IDBRequest::dispatchEvent(Event& event)
301 {
302     LOG(IndexedDB, "IDBRequest::dispatchEvent - %s (%p)", event.type().string().utf8().data(), this);
303
304     ASSERT(&originThread() == &Thread::current());
305     ASSERT(m_hasPendingActivity);
306     ASSERT(!m_contextStopped);
307
308     auto protectedThis = makeRef(*this);
309     m_dispatchingEvent = true;
310
311     if (event.type() != eventNames().blockedEvent)
312         m_readyState = ReadyState::Done;
313
314     Vector<EventTarget*> targets { this };
315
316     if (&event == m_openDatabaseSuccessEvent)
317         m_openDatabaseSuccessEvent = nullptr;
318     else if (m_transaction && !m_transaction->isFinished())
319         targets = { this, m_transaction.get(), &m_transaction->database() };
320
321     m_hasPendingActivity = false;
322
323     {
324         TransactionActivator activator(m_transaction.get());
325         EventDispatcher::dispatchEvent(targets, event);
326     }
327
328     // Dispatching the event might have set the pending activity flag back to true, suggesting the request will be reused.
329     // We might also re-use the request if this event was the upgradeneeded event for an IDBOpenDBRequest.
330     if (!m_hasPendingActivity)
331         m_hasPendingActivity = isOpenDBRequest() && (event.type() == eventNames().upgradeneededEvent || event.type() == eventNames().blockedEvent);
332
333     m_dispatchingEvent = false;
334     if (!m_transaction)
335         return;
336
337     // The request should only remain in the transaction's request list if it represents a pending cursor operation, or this is an open request that was blocked.
338     if (!m_pendingCursor && event.type() != eventNames().blockedEvent)
339         m_transaction->removeRequest(*this);
340
341     if (m_hasUncaughtException)
342         m_transaction->abortDueToFailedRequest(DOMException::create(AbortError, "IDBTransaction will abort due to uncaught exception in an event handler"_s));
343     else if (!event.defaultPrevented() && event.type() == eventNames().errorEvent && !m_transaction->isFinishedOrFinishing()) {
344         ASSERT(m_domError);
345         m_transaction->abortDueToFailedRequest(*m_domError);
346     }
347
348     m_transaction->finishedDispatchEventForRequest(*this);
349 }
350
351 void IDBRequest::uncaughtExceptionInEventHandler()
352 {
353     LOG(IndexedDB, "IDBRequest::uncaughtExceptionInEventHandler");
354
355     ASSERT(&originThread() == &Thread::current());
356
357     if (m_dispatchingEvent) {
358         ASSERT(!m_hasUncaughtException);
359         m_hasUncaughtException = true;
360         return;
361     }
362     if (m_transaction && m_idbError.code() != AbortError)
363         m_transaction->abortDueToFailedRequest(DOMException::create(AbortError, "IDBTransaction will abort due to uncaught exception in an event handler"_s));
364 }
365
366 void IDBRequest::setResult(const IDBKeyData& keyData)
367 {
368     ASSERT(&originThread() == &Thread::current());
369
370     auto* context = scriptExecutionContext();
371     if (!context)
372         return;
373
374     VM& vm = context->vm();
375     JSLockHolder lock(vm);
376     m_result = keyData;
377     m_resultWrapper = { };
378 }
379
380 void IDBRequest::setResult(const Vector<IDBKeyData>& keyDatas)
381 {
382     ASSERT(&originThread() == &Thread::current());
383
384     auto* context = scriptExecutionContext();
385     if (!context)
386         return;
387
388     VM& vm = context->vm();
389     JSLockHolder lock(vm);
390     m_result = keyDatas;
391     m_resultWrapper = { };
392 }
393
394 void IDBRequest::setResult(const IDBGetAllResult& result)
395 {
396     ASSERT(&originThread() == &Thread::current());
397
398     auto* context = scriptExecutionContext();
399     if (!context)
400         return;
401
402     VM& vm = context->vm();
403     JSLockHolder lock(vm);
404     m_result = result;
405     m_resultWrapper = { };
406 }
407
408 void IDBRequest::setResult(uint64_t number)
409 {
410     ASSERT(&originThread() == &Thread::current());
411
412     auto* context = scriptExecutionContext();
413     if (!context)
414         return;
415
416     VM& vm = context->vm();
417     JSLockHolder lock(vm);
418     m_result = number;
419     m_resultWrapper = { };
420 }
421
422 void IDBRequest::setResultToStructuredClone(const IDBGetResult& result)
423 {
424     ASSERT(&originThread() == &Thread::current());
425
426     LOG(IndexedDB, "IDBRequest::setResultToStructuredClone");
427
428     auto* context = scriptExecutionContext();
429     if (!context)
430         return;
431
432     VM& vm = context->vm();
433     JSLockHolder lock(vm);
434     m_result = result;
435     m_resultWrapper = { };
436 }
437
438 void IDBRequest::setResultToUndefined()
439 {
440     ASSERT(&originThread() == &Thread::current());
441
442     auto* context = scriptExecutionContext();
443     if (!context)
444         return;
445     
446     VM& vm = context->vm();
447     JSLockHolder lock(vm);
448     m_result = NullResultType::Undefined;
449     m_resultWrapper = { };
450 }
451
452 IDBCursor* IDBRequest::resultCursor()
453 {
454     ASSERT(&originThread() == &Thread::current());
455
456     return WTF::switchOn(m_result,
457         [] (const RefPtr<IDBCursor>& cursor) -> IDBCursor* { return cursor.get(); },
458         [] (const auto&) -> IDBCursor* { return nullptr; }
459     );
460 }
461
462 void IDBRequest::willIterateCursor(IDBCursor& cursor)
463 {
464     ASSERT(&originThread() == &Thread::current());
465     ASSERT(isDone());
466     ASSERT(scriptExecutionContext());
467     ASSERT(m_transaction);
468     ASSERT(!m_pendingCursor);
469     ASSERT(&cursor == resultCursor());
470
471     m_pendingCursor = &cursor;
472     m_hasPendingActivity = true;
473     m_result = NullResultType::Empty;
474
475     auto* context = scriptExecutionContext();
476     if (!context)
477         return;
478
479     VM& vm = context->vm();
480     JSLockHolder lock(vm);
481
482     if (m_resultWrapper)
483         m_cursorWrapper = m_resultWrapper;
484     m_resultWrapper = { };
485     m_readyState = ReadyState::Pending;
486     m_domError = nullptr;
487     m_idbError = IDBError { };
488 }
489
490 void IDBRequest::didOpenOrIterateCursor(const IDBResultData& resultData)
491 {
492     ASSERT(&originThread() == &Thread::current());
493     ASSERT(m_pendingCursor);
494
495     auto* context = scriptExecutionContext();
496     if (!context)
497         return;
498
499     VM& vm = context->vm();
500     JSLockHolder lock(vm);
501
502     m_result = NullResultType::Empty;
503     m_resultWrapper = { };
504
505     if (resultData.type() == IDBResultType::IterateCursorSuccess || resultData.type() == IDBResultType::OpenCursorSuccess) {
506         if (m_pendingCursor->setGetResult(*this, resultData.getResult()) && m_cursorWrapper)
507             m_resultWrapper = m_cursorWrapper;
508         if (resultData.getResult().isDefined())
509             m_result = m_pendingCursor;
510     }
511
512     m_pendingCursor = nullptr;
513
514     completeRequestAndDispatchEvent(resultData);
515 }
516
517 void IDBRequest::completeRequestAndDispatchEvent(const IDBResultData& resultData)
518 {
519     ASSERT(&originThread() == &Thread::current());
520
521     m_readyState = ReadyState::Done;
522
523     m_idbError = resultData.error();
524     if (!m_idbError.isNull())
525         onError();
526     else
527         onSuccess();
528 }
529
530 void IDBRequest::onError()
531 {
532     LOG(IndexedDB, "IDBRequest::onError");
533
534     ASSERT(&originThread() == &Thread::current());
535     ASSERT(!m_idbError.isNull());
536
537     m_domError = m_idbError.toDOMException();
538     enqueueEvent(Event::create(eventNames().errorEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
539 }
540
541 void IDBRequest::onSuccess()
542 {
543     LOG(IndexedDB, "IDBRequest::onSuccess");
544     ASSERT(&originThread() == &Thread::current());
545     enqueueEvent(Event::create(eventNames().successEvent, Event::CanBubble::No, Event::IsCancelable::No));
546 }
547
548 void IDBRequest::setResult(Ref<IDBDatabase>&& database)
549 {
550     ASSERT(&originThread() == &Thread::current());
551
552     auto* context = scriptExecutionContext();
553     if (!context)
554         return;
555
556     VM& vm = context->vm();
557     JSLockHolder lock(vm);
558
559     m_result = RefPtr<IDBDatabase> { WTFMove(database) };
560     m_resultWrapper = { };
561 }
562
563 void IDBRequest::clearWrappers()
564 {
565     auto* context = scriptExecutionContext();
566     if (!context)
567         return;
568     VM& vm = context->vm();
569     JSLockHolder lock(vm);
570     
571     m_resultWrapper.clear();
572     m_cursorWrapper.clear();
573     
574     WTF::switchOn(m_result,
575         [] (RefPtr<IDBCursor>& cursor) { cursor->clearWrappers(); },
576         [] (const auto&) { }
577     );
578 }
579
580
581 } // namespace WebCore
582
583 #endif // ENABLE(INDEXED_DATABASE)