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
56namespace WebCore {
57using namespace JSC;
58
59WTF_MAKE_ISO_ALLOCATED_IMPL(IDBRequest);
60
61Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBTransaction& transaction)
62{
63 return adoptRef(*new IDBRequest(context, objectStore, transaction));
64}
65
66Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBCursor& cursor, IDBTransaction& transaction)
67{
68 return adoptRef(*new IDBRequest(context, cursor, transaction));
69}
70
71Ref<IDBRequest> IDBRequest::create(ScriptExecutionContext& context, IDBIndex& index, IDBTransaction& transaction)
72{
73 return adoptRef(*new IDBRequest(context, index, transaction));
74}
75
76Ref<IDBRequest> IDBRequest::createObjectStoreGet(ScriptExecutionContext& context, IDBObjectStore& objectStore, IndexedDB::ObjectStoreRecordType type, IDBTransaction& transaction)
77{
78 return adoptRef(*new IDBRequest(context, objectStore, type, transaction));
79}
80
81Ref<IDBRequest> IDBRequest::createIndexGet(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType requestedRecordType, IDBTransaction& transaction)
82{
83 return adoptRef(*new IDBRequest(context, index, requestedRecordType, transaction));
84}
85
86IDBRequest::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
95IDBRequest::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
106IDBRequest::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
123IDBRequest::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
134IDBRequest::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
146IDBRequest::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
153IDBRequest::~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
163ExceptionOr<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
171ExceptionOr<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
181void IDBRequest::setSource(IDBCursor& cursor)
182{
183 ASSERT(&originThread() == &Thread::current());
184
185 m_source = Source { &cursor };
186}
187
188void 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
198RefPtr<WebCore::IDBTransaction> IDBRequest::transaction() const
199{
200 ASSERT(&originThread() == &Thread::current());
201 return m_shouldExposeTransactionToDOM ? m_transaction : nullptr;
202}
203
204uint64_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
218uint64_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
232IndexedDB::ObjectStoreRecordType IDBRequest::requestedObjectStoreRecordType() const
233{
234 ASSERT(&originThread() == &Thread::current());
235
236 return m_requestedObjectStoreRecordType;
237}
238
239IndexedDB::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
248EventTargetInterface IDBRequest::eventTargetInterface() const
249{
250 ASSERT(&originThread() == &Thread::current());
251
252 return IDBRequestEventTargetInterfaceType;
253}
254
255const char* IDBRequest::activeDOMObjectName() const
256{
257 ASSERT(&originThread() == &Thread::current());
258
259 return "IDBRequest";
260}
261
262bool IDBRequest::canSuspendForDocumentSuspension() const
263{
264 ASSERT(&originThread() == &Thread::current());
265 return false;
266}
267
268bool IDBRequest::hasPendingActivity() const
269{
270 ASSERT(&originThread() == &Thread::current() || Thread::mayBeGCThread());
271 return !m_contextStopped && m_hasPendingActivity;
272}
273
274void IDBRequest::stop()
275{
276 ASSERT(&originThread() == &Thread::current());
277 ASSERT(!m_contextStopped);
278
279 cancelForStop();
280
281 removeAllEventListeners();
282
283 clearWrappers();
284
285 m_contextStopped = true;
286}
287
288void IDBRequest::cancelForStop()
289{
290 // The base IDBRequest class has nothing additional to do here.
291}
292
293void IDBRequest::enqueueEvent(Ref<Event>&& event)
294{
295 ASSERT(&originThread() == &Thread::current());
296 if (m_contextStopped)
297 return;
298
299 event->setTarget(this);
300 scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
301}
302
303void IDBRequest::dispatchEvent(Event& event)
304{
305 LOG(IndexedDB, "IDBRequest::dispatchEvent - %s (%p)", event.type().string().utf8().data(), this);
306
307 ASSERT(&originThread() == &Thread::current());
308 ASSERT(m_hasPendingActivity);
309 ASSERT(!m_contextStopped);
310
311 auto protectedThis = makeRef(*this);
312 m_dispatchingEvent = true;
313
314 if (event.type() != eventNames().blockedEvent)
315 m_readyState = ReadyState::Done;
316
317 Vector<EventTarget*> targets { this };
318
319 if (&event == m_openDatabaseSuccessEvent)
320 m_openDatabaseSuccessEvent = nullptr;
321 else if (m_transaction && !m_transaction->didDispatchAbortOrCommit())
322 targets = { this, m_transaction.get(), &m_transaction->database() };
323
324 m_hasPendingActivity = false;
325
326 {
327 TransactionActivator activator(m_transaction.get());
328 EventDispatcher::dispatchEvent(targets, event);
329 }
330
331 // Dispatching the event might have set the pending activity flag back to true, suggesting the request will be reused.
332 // We might also re-use the request if this event was the upgradeneeded event for an IDBOpenDBRequest.
333 if (!m_hasPendingActivity)
334 m_hasPendingActivity = isOpenDBRequest() && (event.type() == eventNames().upgradeneededEvent || event.type() == eventNames().blockedEvent);
335
336 m_dispatchingEvent = false;
337 if (!m_transaction)
338 return;
339
340 // 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.
341 if (!m_pendingCursor && event.type() != eventNames().blockedEvent)
342 m_transaction->removeRequest(*this);
343
344 if (m_hasUncaughtException)
345 m_transaction->abortDueToFailedRequest(DOMException::create(AbortError, "IDBTransaction will abort due to uncaught exception in an event handler"_s));
346 else if (!event.defaultPrevented() && event.type() == eventNames().errorEvent && !m_transaction->isFinishedOrFinishing()) {
347 ASSERT(m_domError);
348 m_transaction->abortDueToFailedRequest(*m_domError);
349 }
350
351 m_transaction->finishedDispatchEventForRequest(*this);
352}
353
354void IDBRequest::uncaughtExceptionInEventHandler()
355{
356 LOG(IndexedDB, "IDBRequest::uncaughtExceptionInEventHandler");
357
358 ASSERT(&originThread() == &Thread::current());
359
360 if (m_dispatchingEvent) {
361 ASSERT(!m_hasUncaughtException);
362 m_hasUncaughtException = true;
363 return;
364 }
365 if (m_transaction && m_idbError.code() != AbortError)
366 m_transaction->abortDueToFailedRequest(DOMException::create(AbortError, "IDBTransaction will abort due to uncaught exception in an event handler"_s));
367}
368
369void IDBRequest::setResult(const IDBKeyData& keyData)
370{
371 ASSERT(&originThread() == &Thread::current());
372
373 auto* context = scriptExecutionContext();
374 if (!context)
375 return;
376
377 VM& vm = context->vm();
378 JSLockHolder lock(vm);
379 m_result = keyData;
380 m_resultWrapper = { };
381}
382
383void IDBRequest::setResult(const Vector<IDBKeyData>& keyDatas)
384{
385 ASSERT(&originThread() == &Thread::current());
386
387 auto* context = scriptExecutionContext();
388 if (!context)
389 return;
390
391 VM& vm = context->vm();
392 JSLockHolder lock(vm);
393 m_result = keyDatas;
394 m_resultWrapper = { };
395}
396
397void IDBRequest::setResult(const IDBGetAllResult& result)
398{
399 ASSERT(&originThread() == &Thread::current());
400
401 auto* context = scriptExecutionContext();
402 if (!context)
403 return;
404
405 VM& vm = context->vm();
406 JSLockHolder lock(vm);
407 m_result = result;
408 m_resultWrapper = { };
409}
410
411void IDBRequest::setResult(uint64_t number)
412{
413 ASSERT(&originThread() == &Thread::current());
414
415 auto* context = scriptExecutionContext();
416 if (!context)
417 return;
418
419 VM& vm = context->vm();
420 JSLockHolder lock(vm);
421 m_result = number;
422 m_resultWrapper = { };
423}
424
425void IDBRequest::setResultToStructuredClone(const IDBGetResult& result)
426{
427 ASSERT(&originThread() == &Thread::current());
428
429 LOG(IndexedDB, "IDBRequest::setResultToStructuredClone");
430
431 auto* context = scriptExecutionContext();
432 if (!context)
433 return;
434
435 VM& vm = context->vm();
436 JSLockHolder lock(vm);
437 m_result = result;
438 m_resultWrapper = { };
439}
440
441void IDBRequest::setResultToUndefined()
442{
443 ASSERT(&originThread() == &Thread::current());
444
445 auto* context = scriptExecutionContext();
446 if (!context)
447 return;
448
449 VM& vm = context->vm();
450 JSLockHolder lock(vm);
451 m_result = NullResultType::Undefined;
452 m_resultWrapper = { };
453}
454
455IDBCursor* IDBRequest::resultCursor()
456{
457 ASSERT(&originThread() == &Thread::current());
458
459 return WTF::switchOn(m_result,
460 [] (const RefPtr<IDBCursor>& cursor) -> IDBCursor* { return cursor.get(); },
461 [] (const auto&) -> IDBCursor* { return nullptr; }
462 );
463}
464
465void IDBRequest::willIterateCursor(IDBCursor& cursor)
466{
467 ASSERT(&originThread() == &Thread::current());
468 ASSERT(isDone());
469 ASSERT(scriptExecutionContext());
470 ASSERT(m_transaction);
471 ASSERT(!m_pendingCursor);
472 ASSERT(&cursor == resultCursor());
473
474 m_pendingCursor = &cursor;
475 m_hasPendingActivity = true;
476 m_result = NullResultType::Empty;
477
478 auto* context = scriptExecutionContext();
479 if (!context)
480 return;
481
482 VM& vm = context->vm();
483 JSLockHolder lock(vm);
484
485 if (m_resultWrapper)
486 m_cursorWrapper = m_resultWrapper;
487 m_resultWrapper = { };
488 m_readyState = ReadyState::Pending;
489 m_domError = nullptr;
490 m_idbError = IDBError { };
491}
492
493void IDBRequest::didOpenOrIterateCursor(const IDBResultData& resultData)
494{
495 ASSERT(&originThread() == &Thread::current());
496 ASSERT(m_pendingCursor);
497
498 auto* context = scriptExecutionContext();
499 if (!context)
500 return;
501
502 VM& vm = context->vm();
503 JSLockHolder lock(vm);
504
505 m_result = NullResultType::Empty;
506 m_resultWrapper = { };
507
508 if (resultData.type() == IDBResultType::IterateCursorSuccess || resultData.type() == IDBResultType::OpenCursorSuccess) {
509 if (m_pendingCursor->setGetResult(*this, resultData.getResult()) && m_cursorWrapper)
510 m_resultWrapper = m_cursorWrapper;
511 if (resultData.getResult().isDefined())
512 m_result = m_pendingCursor;
513 }
514
515 m_pendingCursor = nullptr;
516
517 completeRequestAndDispatchEvent(resultData);
518}
519
520void IDBRequest::completeRequestAndDispatchEvent(const IDBResultData& resultData)
521{
522 ASSERT(&originThread() == &Thread::current());
523
524 m_readyState = ReadyState::Done;
525
526 m_idbError = resultData.error();
527 if (!m_idbError.isNull())
528 onError();
529 else
530 onSuccess();
531}
532
533void IDBRequest::onError()
534{
535 LOG(IndexedDB, "IDBRequest::onError");
536
537 ASSERT(&originThread() == &Thread::current());
538 ASSERT(!m_idbError.isNull());
539
540 m_domError = m_idbError.toDOMException();
541 enqueueEvent(Event::create(eventNames().errorEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
542}
543
544void IDBRequest::onSuccess()
545{
546 LOG(IndexedDB, "IDBRequest::onSuccess");
547 ASSERT(&originThread() == &Thread::current());
548 enqueueEvent(Event::create(eventNames().successEvent, Event::CanBubble::No, Event::IsCancelable::No));
549}
550
551void IDBRequest::setResult(Ref<IDBDatabase>&& database)
552{
553 ASSERT(&originThread() == &Thread::current());
554
555 auto* context = scriptExecutionContext();
556 if (!context)
557 return;
558
559 VM& vm = context->vm();
560 JSLockHolder lock(vm);
561
562 m_result = RefPtr<IDBDatabase> { WTFMove(database) };
563 m_resultWrapper = { };
564}
565
566void IDBRequest::clearWrappers()
567{
568 auto* context = scriptExecutionContext();
569 if (!context)
570 return;
571 VM& vm = context->vm();
572 JSLockHolder lock(vm);
573
574 m_resultWrapper.clear();
575 m_cursorWrapper.clear();
576
577 WTF::switchOn(m_result,
578 [] (RefPtr<IDBCursor>& cursor) { cursor->clearWrappers(); },
579 [] (const auto&) { }
580 );
581}
582
583
584} // namespace WebCore
585
586#endif // ENABLE(INDEXED_DATABASE)
587