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