e8371d7b5a8658f575821110d9ae684ac4fa0b48
[WebKit-https.git] / Source / WebCore / Modules / indexeddb / IDBTransaction.cpp
1 /*
2  * Copyright (C) 2015 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 "IDBTransaction.h"
28
29 #if ENABLE(INDEXED_DATABASE)
30
31 #include "DOMError.h"
32 #include "DOMWindow.h"
33 #include "Event.h"
34 #include "EventNames.h"
35 #include "EventQueue.h"
36 #include "IDBCursorWithValue.h"
37 #include "IDBDatabase.h"
38 #include "IDBDatabaseException.h"
39 #include "IDBError.h"
40 #include "IDBEventDispatcher.h"
41 #include "IDBIndex.h"
42 #include "IDBKeyData.h"
43 #include "IDBKeyRangeData.h"
44 #include "IDBObjectStore.h"
45 #include "IDBOpenDBRequest.h"
46 #include "IDBRequest.h"
47 #include "IDBResultData.h"
48 #include "IDBValue.h"
49 #include "JSDOMWindowBase.h"
50 #include "Logging.h"
51 #include "ScriptExecutionContext.h"
52 #include "TransactionOperation.h"
53 #include <wtf/NeverDestroyed.h>
54
55 namespace WebCore {
56
57 const AtomicString& IDBTransaction::modeReadOnly()
58 {
59     static NeverDestroyed<AtomicString> readonly("readonly", AtomicString::ConstructFromLiteral);
60     return readonly;
61 }
62
63 const AtomicString& IDBTransaction::modeReadWrite()
64 {
65     static NeverDestroyed<AtomicString> readwrite("readwrite", AtomicString::ConstructFromLiteral);
66     return readwrite;
67 }
68
69 const AtomicString& IDBTransaction::modeVersionChange()
70 {
71     static NeverDestroyed<AtomicString> versionchange("versionchange", AtomicString::ConstructFromLiteral);
72     return versionchange;
73 }
74
75 const AtomicString& IDBTransaction::modeReadOnlyLegacy()
76 {
77     static NeverDestroyed<AtomicString> readonly("0", AtomicString::ConstructFromLiteral);
78     return readonly;
79 }
80
81 const AtomicString& IDBTransaction::modeReadWriteLegacy()
82 {
83     static NeverDestroyed<AtomicString> readwrite("1", AtomicString::ConstructFromLiteral);
84     return readwrite;
85 }
86
87 IndexedDB::TransactionMode IDBTransaction::stringToMode(const String& modeString, ExceptionCode& ec)
88 {
89     if (modeString.isNull()
90         || modeString == IDBTransaction::modeReadOnly())
91         return IndexedDB::TransactionMode::ReadOnly;
92     if (modeString == IDBTransaction::modeReadWrite())
93         return IndexedDB::TransactionMode::ReadWrite;
94
95     ec = TypeError;
96     return IndexedDB::TransactionMode::ReadOnly;
97 }
98
99 const AtomicString& IDBTransaction::modeToString(IndexedDB::TransactionMode mode)
100 {
101     switch (mode) {
102     case IndexedDB::TransactionMode::ReadOnly:
103         return IDBTransaction::modeReadOnly();
104
105     case IndexedDB::TransactionMode::ReadWrite:
106         return IDBTransaction::modeReadWrite();
107
108     case IndexedDB::TransactionMode::VersionChange:
109         return IDBTransaction::modeVersionChange();
110     }
111
112     ASSERT_NOT_REACHED();
113     return IDBTransaction::modeReadOnly();
114 }
115
116 Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info)
117 {
118     return adoptRef(*new IDBTransaction(database, info, nullptr));
119 }
120
121 Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest& request)
122 {
123     return adoptRef(*new IDBTransaction(database, info, &request));
124 }
125
126 IDBTransaction::IDBTransaction(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest* request)
127     : IDBActiveDOMObject(database.scriptExecutionContext())
128     , m_database(database)
129     , m_info(info)
130     , m_operationTimer(*this, &IDBTransaction::operationTimerFired)
131     , m_openDBRequest(request)
132
133 {
134     LOG(IndexedDB, "IDBTransaction::IDBTransaction - %s", m_info.loggingString().utf8().data());
135     ASSERT(currentThread() == m_database->originThreadID());
136
137     if (m_info.mode() == IndexedDB::TransactionMode::VersionChange) {
138         ASSERT(m_openDBRequest);
139         m_openDBRequest->setVersionChangeTransaction(*this);
140         m_startedOnServer = true;
141     } else {
142         activate();
143
144         auto* context = scriptExecutionContext();
145         ASSERT(context);
146
147         RefPtr<IDBTransaction> self;
148         JSC::VM& vm = context->vm();
149         vm.whenIdle([self, this]() {
150             deactivate();
151         });
152
153         establishOnServer();
154     }
155
156     suspendIfNeeded();
157 }
158
159 IDBTransaction::~IDBTransaction()
160 {
161     ASSERT(currentThread() == m_database->originThreadID());
162 }
163
164 IDBClient::IDBConnectionProxy& IDBTransaction::connectionProxy()
165 {
166     return m_database->connectionProxy();
167 }
168
169 const String& IDBTransaction::mode() const
170 {
171     ASSERT(currentThread() == m_database->originThreadID());
172
173     switch (m_info.mode()) {
174     case IndexedDB::TransactionMode::ReadOnly:
175         return IDBTransaction::modeReadOnly();
176     case IndexedDB::TransactionMode::ReadWrite:
177         return IDBTransaction::modeReadWrite();
178     case IndexedDB::TransactionMode::VersionChange:
179         return IDBTransaction::modeVersionChange();
180     }
181
182     RELEASE_ASSERT_NOT_REACHED();
183 }
184
185 WebCore::IDBDatabase* IDBTransaction::db()
186 {
187     ASSERT(currentThread() == m_database->originThreadID());
188     return &m_database.get();
189 }
190
191 RefPtr<DOMError> IDBTransaction::error() const
192 {
193     ASSERT(currentThread() == m_database->originThreadID());
194     return m_domError;
195 }
196
197 RefPtr<WebCore::IDBObjectStore> IDBTransaction::objectStore(const String& objectStoreName, ExceptionCodeWithMessage& ec)
198 {
199     LOG(IndexedDB, "IDBTransaction::objectStore");
200     ASSERT(currentThread() == m_database->originThreadID());
201
202     if (!scriptExecutionContext())
203         return nullptr;
204
205     if (isFinishedOrFinishing()) {
206         ec.code = IDBDatabaseException::InvalidStateError;
207         ec.message = ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The transaction finished.");
208         return nullptr;
209     }
210
211     auto iterator = m_referencedObjectStores.find(objectStoreName);
212     if (iterator != m_referencedObjectStores.end())
213         return iterator->value;
214
215     bool found = false;
216     for (auto& objectStore : m_info.objectStores()) {
217         if (objectStore == objectStoreName) {
218             found = true;
219             break;
220         }
221     }
222
223     auto* info = m_database->info().infoForExistingObjectStore(objectStoreName);
224     if (!info) {
225         ec.code = IDBDatabaseException::NotFoundError;
226         ec.message = ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found.");
227         return nullptr;
228     }
229
230     // Version change transactions are scoped to every object store in the database.
231     if (!info || (!found && !isVersionChange())) {
232         ec.code = IDBDatabaseException::NotFoundError;
233         ec.message = ASCIILiteral("Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found.");
234         return nullptr;
235     }
236
237     auto objectStore = IDBObjectStore::create(*scriptExecutionContext(), *info, *this);
238     m_referencedObjectStores.set(objectStoreName, &objectStore.get());
239
240     return adoptRef(&objectStore.leakRef());
241 }
242
243
244 void IDBTransaction::abortDueToFailedRequest(DOMError& error)
245 {
246     LOG(IndexedDB, "IDBTransaction::abortDueToFailedRequest");
247     ASSERT(currentThread() == m_database->originThreadID());
248
249     if (isFinishedOrFinishing())
250         return;
251
252     m_domError = &error;
253     ExceptionCodeWithMessage ec;
254     abort(ec);
255 }
256
257 void IDBTransaction::transitionedToFinishing(IndexedDB::TransactionState state)
258 {
259     ASSERT(currentThread() == m_database->originThreadID());
260
261     ASSERT(!isFinishedOrFinishing());
262     m_state = state;
263     ASSERT(isFinishedOrFinishing());
264     m_referencedObjectStores.clear();
265 }
266
267 void IDBTransaction::abort(ExceptionCodeWithMessage& ec)
268 {
269     LOG(IndexedDB, "IDBTransaction::abort");
270     ASSERT(currentThread() == m_database->originThreadID());
271
272     if (isFinishedOrFinishing()) {
273         ec.code = IDBDatabaseException::InvalidStateError;
274         ec.message = ASCIILiteral("Failed to execute 'abort' on 'IDBTransaction': The transaction is inactive or finished.");
275         return;
276     }
277
278     m_database->willAbortTransaction(*this);
279
280     if (isVersionChange()) {
281         for (auto& objectStore : m_referencedObjectStores.values())
282             objectStore->rollbackInfoForVersionChangeAbort();
283     }
284
285     transitionedToFinishing(IndexedDB::TransactionState::Aborting);
286     
287     m_abortQueue.swap(m_transactionOperationQueue);
288
289     auto operation = IDBClient::createTransactionOperation(*this, nullptr, &IDBTransaction::abortOnServerAndCancelRequests);
290     scheduleOperation(WTFMove(operation));
291 }
292
293 void IDBTransaction::abortOnServerAndCancelRequests(IDBClient::TransactionOperation& operation)
294 {
295     LOG(IndexedDB, "IDBTransaction::abortOnServerAndCancelRequests");
296     ASSERT(currentThread() == m_database->originThreadID());
297     ASSERT(m_transactionOperationQueue.isEmpty());
298
299     m_database->connectionProxy().abortTransaction(*this);
300
301     ASSERT(m_transactionOperationMap.contains(operation.identifier()));
302     m_transactionOperationMap.remove(operation.identifier());
303
304     IDBError error(IDBDatabaseException::AbortError);
305     for (auto& operation : m_abortQueue)
306         operation->completed(IDBResultData::error(operation->identifier(), error));
307
308     // Since we're aborting, it should be impossible to have queued any further operations.
309     ASSERT(m_transactionOperationQueue.isEmpty());
310 }
311
312 const char* IDBTransaction::activeDOMObjectName() const
313 {
314     ASSERT(currentThread() == m_database->originThreadID());
315     return "IDBTransaction";
316 }
317
318 bool IDBTransaction::canSuspendForDocumentSuspension() const
319 {
320     ASSERT(currentThread() == m_database->originThreadID());
321     return false;
322 }
323
324 bool IDBTransaction::hasPendingActivity() const
325 {
326     ASSERT(currentThread() == m_database->originThreadID());
327     return !m_contextStopped && m_state != IndexedDB::TransactionState::Finished;
328 }
329
330 void IDBTransaction::stop()
331 {
332     LOG(IndexedDB, "IDBTransaction::stop - %s", m_info.loggingString().utf8().data());
333     ASSERT(currentThread() == m_database->originThreadID());
334
335     // IDBDatabase::stop() calls IDBTransaction::stop() for each of its active transactions.
336     // Since the order of calling ActiveDOMObject::stop() is random, we might already have been stopped.
337     if (m_contextStopped)
338         return;
339
340     removeAllEventListeners();
341
342     m_contextStopped = true;
343
344     if (isFinishedOrFinishing())
345         return;
346
347     ExceptionCodeWithMessage ec;
348     abort(ec);
349 }
350
351 bool IDBTransaction::isActive() const
352 {
353     ASSERT(currentThread() == m_database->originThreadID());
354     return m_state == IndexedDB::TransactionState::Active;
355 }
356
357 bool IDBTransaction::isFinishedOrFinishing() const
358 {
359     ASSERT(currentThread() == m_database->originThreadID());
360
361     return m_state == IndexedDB::TransactionState::Committing
362         || m_state == IndexedDB::TransactionState::Aborting
363         || m_state == IndexedDB::TransactionState::Finished;
364 }
365
366 void IDBTransaction::addRequest(IDBRequest& request)
367 {
368     ASSERT(currentThread() == m_database->originThreadID());
369     m_openRequests.add(&request);
370 }
371
372 void IDBTransaction::removeRequest(IDBRequest& request)
373 {
374     ASSERT(currentThread() == m_database->originThreadID());
375     ASSERT(m_openRequests.contains(&request));
376     m_openRequests.remove(&request);
377 }
378
379 void IDBTransaction::scheduleOperation(RefPtr<IDBClient::TransactionOperation>&& operation)
380 {
381     ASSERT(!m_transactionOperationMap.contains(operation->identifier()));
382     ASSERT(currentThread() == m_database->originThreadID());
383
384     m_transactionOperationQueue.append(operation);
385     m_transactionOperationMap.set(operation->identifier(), WTFMove(operation));
386
387     scheduleOperationTimer();
388 }
389
390 void IDBTransaction::scheduleOperationTimer()
391 {
392     ASSERT(currentThread() == m_database->originThreadID());
393
394     if (!m_operationTimer.isActive())
395         m_operationTimer.startOneShot(0);
396 }
397
398 void IDBTransaction::operationTimerFired()
399 {
400     LOG(IndexedDB, "IDBTransaction::operationTimerFired (%p)", this);
401     ASSERT(currentThread() == m_database->originThreadID());
402
403     if (!m_startedOnServer)
404         return;
405
406     if (!m_transactionOperationQueue.isEmpty()) {
407         auto operation = m_transactionOperationQueue.takeFirst();
408         operation->perform();
409
410         return;
411     }
412
413     if (!m_transactionOperationMap.isEmpty() || !m_openRequests.isEmpty())
414         return;
415
416     if (!isFinishedOrFinishing())
417         commit();
418 }
419
420 void IDBTransaction::commit()
421 {
422     LOG(IndexedDB, "IDBTransaction::commit");
423     ASSERT(currentThread() == m_database->originThreadID());
424     ASSERT(!isFinishedOrFinishing());
425
426     transitionedToFinishing(IndexedDB::TransactionState::Committing);
427     m_database->willCommitTransaction(*this);
428
429     auto operation = IDBClient::createTransactionOperation(*this, nullptr, &IDBTransaction::commitOnServer);
430     scheduleOperation(WTFMove(operation));
431 }
432
433 void IDBTransaction::commitOnServer(IDBClient::TransactionOperation& operation)
434 {
435     LOG(IndexedDB, "IDBTransaction::commitOnServer");
436     ASSERT(currentThread() == m_database->originThreadID());
437
438     m_database->connectionProxy().commitTransaction(*this);
439
440     ASSERT(m_transactionOperationMap.contains(operation.identifier()));
441     m_transactionOperationMap.remove(operation.identifier());
442 }
443
444 void IDBTransaction::finishAbortOrCommit()
445 {
446     ASSERT(m_state != IndexedDB::TransactionState::Finished);
447     ASSERT(currentThread() == m_database->originThreadID());
448
449     m_state = IndexedDB::TransactionState::Finished;
450 }
451
452 void IDBTransaction::didStart(const IDBError& error)
453 {
454     LOG(IndexedDB, "IDBTransaction::didStart");
455     ASSERT(currentThread() == m_database->originThreadID());
456
457     m_database->didStartTransaction(*this);
458
459     m_startedOnServer = true;
460
461     // It's possible the transaction failed to start on the server.
462     // That equates to an abort.
463     if (!error.isNull()) {
464         didAbort(error);
465         return;
466     }
467
468     scheduleOperationTimer();
469 }
470
471 void IDBTransaction::notifyDidAbort(const IDBError& error)
472 {
473     ASSERT(currentThread() == m_database->originThreadID());
474
475     m_database->didAbortTransaction(*this);
476     m_idbError = error;
477     fireOnAbort();
478
479     if (isVersionChange()) {
480         ASSERT(m_openDBRequest);
481         m_openDBRequest->fireErrorAfterVersionChangeCompletion();
482     }
483 }
484
485 void IDBTransaction::didAbort(const IDBError& error)
486 {
487     LOG(IndexedDB, "IDBTransaction::didAbort");
488     ASSERT(currentThread() == m_database->originThreadID());
489
490     if (m_state == IndexedDB::TransactionState::Finished)
491         return;
492
493     notifyDidAbort(error);
494
495     finishAbortOrCommit();
496 }
497
498 void IDBTransaction::didCommit(const IDBError& error)
499 {
500     LOG(IndexedDB, "IDBTransaction::didCommit");
501     ASSERT(currentThread() == m_database->originThreadID());
502     ASSERT(m_state == IndexedDB::TransactionState::Committing);
503
504     if (error.isNull()) {
505         m_database->didCommitTransaction(*this);
506         fireOnComplete();
507     } else {
508         m_database->willAbortTransaction(*this);
509         notifyDidAbort(error);
510     }
511
512     finishAbortOrCommit();
513 }
514
515 void IDBTransaction::fireOnComplete()
516 {
517     LOG(IndexedDB, "IDBTransaction::fireOnComplete");
518     ASSERT(currentThread() == m_database->originThreadID());
519     enqueueEvent(Event::create(eventNames().completeEvent, false, false));
520 }
521
522 void IDBTransaction::fireOnAbort()
523 {
524     LOG(IndexedDB, "IDBTransaction::fireOnAbort");
525     ASSERT(currentThread() == m_database->originThreadID());
526     enqueueEvent(Event::create(eventNames().abortEvent, true, false));
527 }
528
529 void IDBTransaction::enqueueEvent(Ref<Event>&& event)
530 {
531     ASSERT(m_state != IndexedDB::TransactionState::Finished);
532     ASSERT(currentThread() == m_database->originThreadID());
533
534     if (!scriptExecutionContext() || m_contextStopped)
535         return;
536
537     event->setTarget(this);
538     scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
539 }
540
541 bool IDBTransaction::dispatchEvent(Event& event)
542 {
543     LOG(IndexedDB, "IDBTransaction::dispatchEvent");
544
545     ASSERT(currentThread() == m_database->originThreadID());
546     ASSERT(scriptExecutionContext());
547     ASSERT(!m_contextStopped);
548     ASSERT(event.target() == this);
549     ASSERT(event.type() == eventNames().completeEvent || event.type() == eventNames().abortEvent);
550
551     Vector<RefPtr<EventTarget>> targets;
552     targets.append(this);
553     targets.append(db());
554
555     bool result = IDBEventDispatcher::dispatch(event, targets);
556
557     if (isVersionChange()) {
558         ASSERT(m_openDBRequest);
559         m_openDBRequest->versionChangeTransactionDidFinish();
560
561         if (event.type() == eventNames().completeEvent) {
562             if (m_database->isClosingOrClosed())
563                 m_openDBRequest->fireErrorAfterVersionChangeCompletion();
564             else
565                 m_openDBRequest->fireSuccessAfterVersionChangeCommit();
566         }
567
568         m_openDBRequest = nullptr;
569     }
570
571     return result;
572 }
573
574 Ref<IDBObjectStore> IDBTransaction::createObjectStore(const IDBObjectStoreInfo& info)
575 {
576     LOG(IndexedDB, "IDBTransaction::createObjectStore");
577     ASSERT(isVersionChange());
578     ASSERT(scriptExecutionContext());
579     ASSERT(currentThread() == m_database->originThreadID());
580
581     Ref<IDBObjectStore> objectStore = IDBObjectStore::create(*scriptExecutionContext(), info, *this);
582     m_referencedObjectStores.set(info.name(), &objectStore.get());
583
584     auto operation = IDBClient::createTransactionOperation(*this, &IDBTransaction::didCreateObjectStoreOnServer, &IDBTransaction::createObjectStoreOnServer, info);
585     scheduleOperation(WTFMove(operation));
586
587     return objectStore;
588 }
589
590 void IDBTransaction::createObjectStoreOnServer(IDBClient::TransactionOperation& operation, const IDBObjectStoreInfo& info)
591 {
592     LOG(IndexedDB, "IDBTransaction::createObjectStoreOnServer");
593     ASSERT(currentThread() == m_database->originThreadID());
594     ASSERT(isVersionChange());
595
596     m_database->connectionProxy().createObjectStore(operation, info);
597 }
598
599 void IDBTransaction::didCreateObjectStoreOnServer(const IDBResultData& resultData)
600 {
601     LOG(IndexedDB, "IDBTransaction::didCreateObjectStoreOnServer");
602     ASSERT(currentThread() == m_database->originThreadID());
603     ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess || resultData.type() == IDBResultType::Error);
604 }
605
606 std::unique_ptr<IDBIndex> IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info)
607 {
608     LOG(IndexedDB, "IDBTransaction::createIndex");
609     ASSERT(isVersionChange());
610     ASSERT(currentThread() == m_database->originThreadID());
611
612     if (!scriptExecutionContext())
613         return nullptr;
614
615     auto operation = IDBClient::createTransactionOperation(*this, &IDBTransaction::didCreateIndexOnServer, &IDBTransaction::createIndexOnServer, info);
616     scheduleOperation(WTFMove(operation));
617
618     return std::make_unique<IDBIndex>(*scriptExecutionContext(), info, objectStore);
619 }
620
621 void IDBTransaction::createIndexOnServer(IDBClient::TransactionOperation& operation, const IDBIndexInfo& info)
622 {
623     LOG(IndexedDB, "IDBTransaction::createIndexOnServer");
624     ASSERT(currentThread() == m_database->originThreadID());
625     ASSERT(isVersionChange());
626
627     m_database->connectionProxy().createIndex(operation, info);
628 }
629
630 void IDBTransaction::didCreateIndexOnServer(const IDBResultData& resultData)
631 {
632     LOG(IndexedDB, "IDBTransaction::didCreateIndexOnServer");
633     ASSERT(currentThread() == m_database->originThreadID());
634
635     if (resultData.type() == IDBResultType::CreateIndexSuccess)
636         return;
637
638     ASSERT(resultData.type() == IDBResultType::Error);
639
640     // This operation might have failed because the transaction is already aborting.
641     if (m_state == IndexedDB::TransactionState::Aborting)
642         return;
643
644     // Otherwise, failure to create an index forced abortion of the transaction.
645     abortDueToFailedRequest(DOMError::create(IDBDatabaseException::getErrorName(resultData.error().code()), resultData.error().message()));
646 }
647
648 Ref<IDBRequest> IDBTransaction::requestOpenCursor(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBCursorInfo& info)
649 {
650     LOG(IndexedDB, "IDBTransaction::requestOpenCursor");
651     ASSERT(currentThread() == m_database->originThreadID());
652
653     return doRequestOpenCursor(context, IDBCursorWithValue::create(*this, objectStore, info));
654 }
655
656 Ref<IDBRequest> IDBTransaction::requestOpenCursor(ScriptExecutionContext& context, IDBIndex& index, const IDBCursorInfo& info)
657 {
658     LOG(IndexedDB, "IDBTransaction::requestOpenCursor");
659     ASSERT(currentThread() == m_database->originThreadID());
660
661     if (info.cursorType() == IndexedDB::CursorType::KeyOnly)
662         return doRequestOpenCursor(context, IDBCursor::create(*this, index, info));
663
664     return doRequestOpenCursor(context, IDBCursorWithValue::create(*this, index, info));
665 }
666
667 Ref<IDBRequest> IDBTransaction::doRequestOpenCursor(ScriptExecutionContext& context, Ref<IDBCursor>&& cursor)
668 {
669     ASSERT(isActive());
670     ASSERT(currentThread() == m_database->originThreadID());
671
672     Ref<IDBRequest> request = IDBRequest::create(context, cursor.get(), *this);
673     addRequest(request.get());
674
675     auto operation = IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didOpenCursorOnServer, &IDBTransaction::openCursorOnServer, cursor->info());
676     scheduleOperation(WTFMove(operation));
677
678     return request;
679 }
680
681 void IDBTransaction::openCursorOnServer(IDBClient::TransactionOperation& operation, const IDBCursorInfo& info)
682 {
683     LOG(IndexedDB, "IDBTransaction::openCursorOnServer");
684     ASSERT(currentThread() == m_database->originThreadID());
685
686     m_database->connectionProxy().openCursor(operation, info);
687 }
688
689 void IDBTransaction::didOpenCursorOnServer(IDBRequest& request, const IDBResultData& resultData)
690 {
691     LOG(IndexedDB, "IDBTransaction::didOpenCursorOnServer");
692     ASSERT(currentThread() == m_database->originThreadID());
693
694     request.didOpenOrIterateCursor(resultData);
695 }
696
697 void IDBTransaction::iterateCursor(IDBCursor& cursor, const IDBKeyData& key, unsigned long count)
698 {
699     LOG(IndexedDB, "IDBTransaction::iterateCursor");
700     ASSERT(isActive());
701     ASSERT(cursor.request());
702     ASSERT(currentThread() == m_database->originThreadID());
703
704     addRequest(*cursor.request());
705
706     auto operation = IDBClient::createTransactionOperation(*this, *cursor.request(), &IDBTransaction::didIterateCursorOnServer, &IDBTransaction::iterateCursorOnServer, key, count);
707     scheduleOperation(WTFMove(operation));
708 }
709
710 void IDBTransaction::iterateCursorOnServer(IDBClient::TransactionOperation& operation, const IDBKeyData& key, const unsigned long& count)
711 {
712     LOG(IndexedDB, "IDBTransaction::iterateCursorOnServer");
713     ASSERT(currentThread() == m_database->originThreadID());
714
715     m_database->connectionProxy().iterateCursor(operation, key, count);
716 }
717
718 void IDBTransaction::didIterateCursorOnServer(IDBRequest& request, const IDBResultData& resultData)
719 {
720     LOG(IndexedDB, "IDBTransaction::didIterateCursorOnServer");
721     ASSERT(currentThread() == m_database->originThreadID());
722
723     request.didOpenOrIterateCursor(resultData);
724 }
725
726 Ref<IDBRequest> IDBTransaction::requestGetRecord(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBKeyRangeData& keyRangeData)
727 {
728     LOG(IndexedDB, "IDBTransaction::requestGetRecord");
729     ASSERT(isActive());
730     ASSERT(!keyRangeData.isNull);
731     ASSERT(currentThread() == m_database->originThreadID());
732
733     Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this);
734     addRequest(request.get());
735
736     auto operation = IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, keyRangeData);
737     scheduleOperation(WTFMove(operation));
738
739     return request;
740 }
741
742 Ref<IDBRequest> IDBTransaction::requestGetValue(ScriptExecutionContext& context, IDBIndex& index, const IDBKeyRangeData& range)
743 {
744     LOG(IndexedDB, "IDBTransaction::requestGetValue");
745     ASSERT(currentThread() == m_database->originThreadID());
746
747     return requestIndexRecord(context, index, IndexedDB::IndexRecordType::Value, range);
748 }
749
750 Ref<IDBRequest> IDBTransaction::requestGetKey(ScriptExecutionContext& context, IDBIndex& index, const IDBKeyRangeData& range)
751 {
752     LOG(IndexedDB, "IDBTransaction::requestGetValue");
753     ASSERT(currentThread() == m_database->originThreadID());
754
755     return requestIndexRecord(context, index, IndexedDB::IndexRecordType::Key, range);
756 }
757
758 Ref<IDBRequest> IDBTransaction::requestIndexRecord(ScriptExecutionContext& context, IDBIndex& index, IndexedDB::IndexRecordType type, const IDBKeyRangeData&range)
759 {
760     LOG(IndexedDB, "IDBTransaction::requestGetValue");
761     ASSERT(isActive());
762     ASSERT(!range.isNull);
763     ASSERT(currentThread() == m_database->originThreadID());
764
765     Ref<IDBRequest> request = IDBRequest::createGet(context, index, type, *this);
766     addRequest(request.get());
767
768     auto operation = IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetRecordOnServer, &IDBTransaction::getRecordOnServer, range);
769     scheduleOperation(WTFMove(operation));
770
771     return request;
772 }
773
774 void IDBTransaction::getRecordOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
775 {
776     LOG(IndexedDB, "IDBTransaction::getRecordOnServer");
777     ASSERT(currentThread() == m_database->originThreadID());
778
779     m_database->connectionProxy().getRecord(operation, keyRange);
780 }
781
782 void IDBTransaction::didGetRecordOnServer(IDBRequest& request, const IDBResultData& resultData)
783 {
784     LOG(IndexedDB, "IDBTransaction::didGetRecordOnServer");
785     ASSERT(currentThread() == m_database->originThreadID());
786
787     if (resultData.type() == IDBResultType::Error) {
788         request.requestCompleted(resultData);
789         return;
790     }
791
792     ASSERT(resultData.type() == IDBResultType::GetRecordSuccess);
793
794     const IDBGetResult& result = resultData.getResult();
795
796     if (request.sourceIndexIdentifier() && request.requestedIndexRecordType() == IndexedDB::IndexRecordType::Key) {
797         if (!result.keyData().isNull())
798             request.setResult(result.keyData());
799         else
800             request.setResultToUndefined();
801     } else {
802         if (resultData.getResult().value().data().data())
803             request.setResultToStructuredClone(resultData.getResult().value());
804         else
805             request.setResultToUndefined();
806     }
807
808     request.requestCompleted(resultData);
809 }
810
811 Ref<IDBRequest> IDBTransaction::requestCount(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBKeyRangeData& range)
812 {
813     LOG(IndexedDB, "IDBTransaction::requestCount (IDBObjectStore)");
814     ASSERT(isActive());
815     ASSERT(!range.isNull);
816     ASSERT(currentThread() == m_database->originThreadID());
817
818     Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this);
819     addRequest(request.get());
820
821     scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range));
822
823     return request;
824 }
825
826 Ref<IDBRequest> IDBTransaction::requestCount(ScriptExecutionContext& context, IDBIndex& index, const IDBKeyRangeData& range)
827 {
828     LOG(IndexedDB, "IDBTransaction::requestCount (IDBIndex)");
829     ASSERT(isActive());
830     ASSERT(!range.isNull);
831     ASSERT(currentThread() == m_database->originThreadID());
832
833     Ref<IDBRequest> request = IDBRequest::createCount(context, index, *this);
834     addRequest(request.get());
835
836     scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didGetCountOnServer, &IDBTransaction::getCountOnServer, range));
837
838     return request;
839 }
840
841 void IDBTransaction::getCountOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
842 {
843     LOG(IndexedDB, "IDBTransaction::getCountOnServer");
844     ASSERT(currentThread() == m_database->originThreadID());
845
846     m_database->connectionProxy().getCount(operation, keyRange);
847 }
848
849 void IDBTransaction::didGetCountOnServer(IDBRequest& request, const IDBResultData& resultData)
850 {
851     LOG(IndexedDB, "IDBTransaction::didGetCountOnServer");
852     ASSERT(currentThread() == m_database->originThreadID());
853
854     request.setResult(resultData.resultInteger());
855     request.requestCompleted(resultData);
856 }
857
858 Ref<IDBRequest> IDBTransaction::requestDeleteRecord(ScriptExecutionContext& context, IDBObjectStore& objectStore, const IDBKeyRangeData& range)
859 {
860     LOG(IndexedDB, "IDBTransaction::requestDeleteRecord");
861     ASSERT(isActive());
862     ASSERT(!range.isNull);
863     ASSERT(currentThread() == m_database->originThreadID());
864
865     Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this);
866     addRequest(request.get());
867
868     scheduleOperation(IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didDeleteRecordOnServer, &IDBTransaction::deleteRecordOnServer, range));
869     return request;
870 }
871
872 void IDBTransaction::deleteRecordOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
873 {
874     LOG(IndexedDB, "IDBTransaction::deleteRecordOnServer");
875     ASSERT(currentThread() == m_database->originThreadID());
876
877     m_database->connectionProxy().deleteRecord(operation, keyRange);
878 }
879
880 void IDBTransaction::didDeleteRecordOnServer(IDBRequest& request, const IDBResultData& resultData)
881 {
882     LOG(IndexedDB, "IDBTransaction::didDeleteRecordOnServer");
883     ASSERT(currentThread() == m_database->originThreadID());
884
885     request.setResultToUndefined();
886     request.requestCompleted(resultData);
887 }
888
889 Ref<IDBRequest> IDBTransaction::requestClearObjectStore(ScriptExecutionContext& context, IDBObjectStore& objectStore)
890 {
891     LOG(IndexedDB, "IDBTransaction::requestClearObjectStore");
892     ASSERT(isActive());
893     ASSERT(currentThread() == m_database->originThreadID());
894
895     Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this);
896     addRequest(request.get());
897
898     uint64_t objectStoreIdentifier = objectStore.info().identifier();
899     auto operation = IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didClearObjectStoreOnServer, &IDBTransaction::clearObjectStoreOnServer, objectStoreIdentifier);
900     scheduleOperation(WTFMove(operation));
901
902     return request;
903 }
904
905 void IDBTransaction::clearObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier)
906 {
907     LOG(IndexedDB, "IDBTransaction::clearObjectStoreOnServer");
908     ASSERT(currentThread() == m_database->originThreadID());
909
910     m_database->connectionProxy().clearObjectStore(operation, objectStoreIdentifier);
911 }
912
913 void IDBTransaction::didClearObjectStoreOnServer(IDBRequest& request, const IDBResultData& resultData)
914 {
915     LOG(IndexedDB, "IDBTransaction::didClearObjectStoreOnServer");
916     ASSERT(currentThread() == m_database->originThreadID());
917
918     request.setResultToUndefined();
919     request.requestCompleted(resultData);
920 }
921
922 Ref<IDBRequest> IDBTransaction::requestPutOrAdd(ScriptExecutionContext& context, IDBObjectStore& objectStore, IDBKey* key, SerializedScriptValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
923 {
924     LOG(IndexedDB, "IDBTransaction::requestPutOrAdd");
925     ASSERT(isActive());
926     ASSERT(!isReadOnly());
927     ASSERT(objectStore.info().autoIncrement() || key);
928     ASSERT(currentThread() == m_database->originThreadID());
929
930     Ref<IDBRequest> request = IDBRequest::create(context, objectStore, *this);
931     addRequest(request.get());
932
933     auto operation = IDBClient::createTransactionOperation(*this, request.get(), &IDBTransaction::didPutOrAddOnServer, &IDBTransaction::putOrAddOnServer, key, &value, overwriteMode);
934     scheduleOperation(WTFMove(operation));
935
936     return request;
937 }
938
939 void IDBTransaction::putOrAddOnServer(IDBClient::TransactionOperation& operation, RefPtr<IDBKey> key, RefPtr<SerializedScriptValue> value, const IndexedDB::ObjectStoreOverwriteMode& overwriteMode)
940 {
941     LOG(IndexedDB, "IDBTransaction::putOrAddOnServer");
942     ASSERT(currentThread() == originThreadID());
943     ASSERT(!isReadOnly());
944     ASSERT(value);
945
946     if (!value->hasBlobURLs()) {
947         m_database->connectionProxy().putOrAdd(operation, key.get(), *value, overwriteMode);
948         return;
949     }
950
951     // Due to current limitations on our ability to post tasks back to a worker thread,
952     // workers currently write blobs to disk synchronously.
953     // FIXME: https://bugs.webkit.org/show_bug.cgi?id=157958 - Make this asynchronous after refactoring allows it.
954     if (!isMainThread()) {
955         auto idbValue = value->writeBlobsToDiskForIndexedDBSynchronously();
956         if (idbValue.data().data())
957             m_database->connectionProxy().putOrAdd(operation, key.get(), idbValue, overwriteMode);
958         else {
959             // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
960             // In that case, we cannot successfully store this record, so we callback with an error.
961             RefPtr<IDBClient::TransactionOperation> protectedOperation(&operation);
962             auto result = IDBResultData::error(operation.identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") });
963             scriptExecutionContext()->postTask([protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)](ScriptExecutionContext&) {
964                 protectedOperation->completed(result);
965             });
966         }
967         return;
968     }
969
970     value->writeBlobsToDiskForIndexedDB([protectedThis = makeRef(*this), this, protectedOperation = Ref<IDBClient::TransactionOperation>(operation), keyData = IDBKeyData(key.get()).isolatedCopy(), overwriteMode](const IDBValue& idbValue) mutable {
971         ASSERT(currentThread() == originThreadID());
972         ASSERT(isMainThread());
973         if (idbValue.data().data()) {
974             m_database->connectionProxy().putOrAdd(protectedOperation.get(), WTFMove(keyData), idbValue, overwriteMode);
975             return;
976         }
977
978         // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
979         // In that case, we cannot successfully store this record, so we callback with an error.
980         auto result = IDBResultData::error(protectedOperation->identifier(), { IDBDatabaseException::UnknownError, ASCIILiteral("Error preparing Blob/File data to be stored in object store") });
981         callOnMainThread([protectedThis = WTFMove(protectedThis), protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)]() mutable {
982             protectedOperation->completed(result);
983         });
984     });
985 }
986
987 void IDBTransaction::didPutOrAddOnServer(IDBRequest& request, const IDBResultData& resultData)
988 {
989     LOG(IndexedDB, "IDBTransaction::didPutOrAddOnServer");
990     ASSERT(currentThread() == m_database->originThreadID());
991
992     if (auto* result = resultData.resultKey())
993         request.setResult(*result);
994     else
995         request.setResultToUndefined();
996     request.requestCompleted(resultData);
997 }
998
999 void IDBTransaction::deleteObjectStore(const String& objectStoreName)
1000 {
1001     LOG(IndexedDB, "IDBTransaction::deleteObjectStore");
1002     ASSERT(currentThread() == m_database->originThreadID());
1003     ASSERT(isVersionChange());
1004
1005     if (auto objectStore = m_referencedObjectStores.take(objectStoreName))
1006         objectStore->markAsDeleted();
1007
1008     auto operation = IDBClient::createTransactionOperation(*this, &IDBTransaction::didDeleteObjectStoreOnServer, &IDBTransaction::deleteObjectStoreOnServer, objectStoreName);
1009     scheduleOperation(WTFMove(operation));
1010 }
1011
1012 void IDBTransaction::deleteObjectStoreOnServer(IDBClient::TransactionOperation& operation, const String& objectStoreName)
1013 {
1014     LOG(IndexedDB, "IDBTransaction::deleteObjectStoreOnServer");
1015     ASSERT(isVersionChange());
1016     ASSERT(currentThread() == m_database->originThreadID());
1017
1018     m_database->connectionProxy().deleteObjectStore(operation, objectStoreName);
1019 }
1020
1021 void IDBTransaction::didDeleteObjectStoreOnServer(const IDBResultData& resultData)
1022 {
1023     LOG(IndexedDB, "IDBTransaction::didDeleteObjectStoreOnServer");
1024     ASSERT(currentThread() == m_database->originThreadID());
1025     ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteObjectStoreSuccess || resultData.type() == IDBResultType::Error);
1026 }
1027
1028 void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& indexName)
1029 {
1030     LOG(IndexedDB, "IDBTransaction::deleteIndex");
1031     ASSERT(currentThread() == m_database->originThreadID());
1032     ASSERT(isVersionChange());
1033
1034     auto operation = IDBClient::createTransactionOperation(*this, &IDBTransaction::didDeleteIndexOnServer, &IDBTransaction::deleteIndexOnServer, objectStoreIdentifier, indexName);
1035     scheduleOperation(WTFMove(operation));
1036 }
1037
1038 void IDBTransaction::deleteIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& indexName)
1039 {
1040     LOG(IndexedDB, "IDBTransaction::deleteIndexOnServer");
1041     ASSERT(isVersionChange());
1042     ASSERT(currentThread() == m_database->originThreadID());
1043
1044     m_database->connectionProxy().deleteIndex(operation, objectStoreIdentifier, indexName);
1045 }
1046
1047 void IDBTransaction::didDeleteIndexOnServer(const IDBResultData& resultData)
1048 {
1049     LOG(IndexedDB, "IDBTransaction::didDeleteIndexOnServer");
1050     ASSERT(currentThread() == m_database->originThreadID());
1051     ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteIndexSuccess || resultData.type() == IDBResultType::Error);
1052 }
1053
1054 void IDBTransaction::operationDidComplete(IDBClient::TransactionOperation& operation)
1055 {
1056     ASSERT(m_transactionOperationMap.get(operation.identifier()) == &operation);
1057     ASSERT(currentThread() == m_database->originThreadID());
1058     ASSERT(currentThread() == operation.originThreadID());
1059
1060     m_transactionOperationMap.remove(operation.identifier());
1061
1062     scheduleOperationTimer();
1063 }
1064
1065 void IDBTransaction::establishOnServer()
1066 {
1067     LOG(IndexedDB, "IDBTransaction::establishOnServer");
1068     ASSERT(currentThread() == m_database->originThreadID());
1069
1070     m_database->connectionProxy().establishTransaction(*this);
1071 }
1072
1073 void IDBTransaction::activate()
1074 {
1075     ASSERT(currentThread() == m_database->originThreadID());
1076
1077     if (isFinishedOrFinishing())
1078         return;
1079
1080     m_state = IndexedDB::TransactionState::Active;
1081 }
1082
1083 void IDBTransaction::deactivate()
1084 {
1085     ASSERT(currentThread() == m_database->originThreadID());
1086
1087     if (m_state == IndexedDB::TransactionState::Active)
1088         m_state = IndexedDB::TransactionState::Inactive;
1089
1090     scheduleOperationTimer();
1091 }
1092
1093 void IDBTransaction::connectionClosedFromServer(const IDBError& error)
1094 {
1095     LOG(IndexedDB, "IDBTransaction::connectionClosedFromServer - %s", error.message().utf8().data());
1096
1097     m_state = IndexedDB::TransactionState::Aborting;
1098
1099     Vector<RefPtr<IDBClient::TransactionOperation>> operations;
1100     copyValuesToVector(m_transactionOperationMap, operations);
1101
1102     for (auto& operation : operations)
1103         operation->completed(IDBResultData::error(operation->identifier(), error));
1104
1105     connectionProxy().forgetActiveOperations(operations);
1106
1107     m_transactionOperationQueue.clear();
1108     m_abortQueue.clear();
1109     m_transactionOperationMap.clear();
1110
1111     m_idbError = error;
1112     m_domError = error.toDOMError();
1113     fireOnAbort();
1114 }
1115
1116 } // namespace WebCore
1117
1118 #endif // ENABLE(INDEXED_DATABASE)