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 "IDBTransaction.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "DOMException.h"
32#include "DOMStringList.h"
33#include "DOMWindow.h"
34#include "Event.h"
35#include "EventDispatcher.h"
36#include "EventNames.h"
37#include "EventQueue.h"
38#include "IDBCursorWithValue.h"
39#include "IDBDatabase.h"
40#include "IDBError.h"
41#include "IDBGetRecordData.h"
42#include "IDBIndex.h"
43#include "IDBIterateCursorData.h"
44#include "IDBKeyData.h"
45#include "IDBKeyRangeData.h"
46#include "IDBObjectStore.h"
47#include "IDBOpenDBRequest.h"
48#include "IDBRequest.h"
49#include "IDBResultData.h"
50#include "IDBValue.h"
51#include "JSDOMWindowBase.h"
52#include "Logging.h"
53#include "ScriptExecutionContext.h"
54#include "ScriptState.h"
55#include "SerializedScriptValue.h"
56#include "TransactionOperation.h"
57#include <wtf/CompletionHandler.h>
58#include <wtf/IsoMallocInlines.h>
59
60namespace WebCore {
61using namespace JSC;
62
63WTF_MAKE_ISO_ALLOCATED_IMPL(IDBTransaction);
64
65std::atomic<unsigned> IDBTransaction::numberOfIDBTransactions { 0 };
66
67Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info)
68{
69 return adoptRef(*new IDBTransaction(database, info, nullptr));
70}
71
72Ref<IDBTransaction> IDBTransaction::create(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest& request)
73{
74 return adoptRef(*new IDBTransaction(database, info, &request));
75}
76
77IDBTransaction::IDBTransaction(IDBDatabase& database, const IDBTransactionInfo& info, IDBOpenDBRequest* request)
78 : IDBActiveDOMObject(database.scriptExecutionContext())
79 , m_database(database)
80 , m_info(info)
81 , m_pendingOperationTimer(*this, &IDBTransaction::pendingOperationTimerFired)
82 , m_completedOperationTimer(*this, &IDBTransaction::completedOperationTimerFired)
83 , m_openDBRequest(request)
84 , m_currentlyCompletingRequest(request)
85
86{
87 LOG(IndexedDB, "IDBTransaction::IDBTransaction - %s", m_info.loggingString().utf8().data());
88 ASSERT(&m_database->originThread() == &Thread::current());
89
90 ++numberOfIDBTransactions;
91
92 if (m_info.mode() == IDBTransactionMode::Versionchange) {
93 ASSERT(m_openDBRequest);
94 m_openDBRequest->setVersionChangeTransaction(*this);
95 m_startedOnServer = true;
96 } else {
97 activate();
98
99 auto* context = scriptExecutionContext();
100 ASSERT(context);
101
102 JSC::VM& vm = context->vm();
103 vm.whenIdle([protectedThis = makeRef(*this)]() {
104 protectedThis->deactivate();
105 });
106
107 establishOnServer();
108 }
109
110 suspendIfNeeded();
111}
112
113IDBTransaction::~IDBTransaction()
114{
115 --numberOfIDBTransactions;
116 ASSERT(&m_database->originThread() == &Thread::current());
117}
118
119IDBClient::IDBConnectionProxy& IDBTransaction::connectionProxy()
120{
121 return m_database->connectionProxy();
122}
123
124Ref<DOMStringList> IDBTransaction::objectStoreNames() const
125{
126 ASSERT(&m_database->originThread() == &Thread::current());
127
128 const Vector<String> names = isVersionChange() ? m_database->info().objectStoreNames() : m_info.objectStores();
129
130 Ref<DOMStringList> objectStoreNames = DOMStringList::create();
131 for (auto& name : names)
132 objectStoreNames->append(name);
133
134 objectStoreNames->sort();
135 return objectStoreNames;
136}
137
138IDBDatabase* IDBTransaction::db()
139{
140 ASSERT(&m_database->originThread() == &Thread::current());
141 return m_database.ptr();
142}
143
144DOMException* IDBTransaction::error() const
145{
146 ASSERT(&m_database->originThread() == &Thread::current());
147 return m_domError.get();
148}
149
150ExceptionOr<Ref<IDBObjectStore>> IDBTransaction::objectStore(const String& objectStoreName)
151{
152 LOG(IndexedDB, "IDBTransaction::objectStore");
153 ASSERT(&m_database->originThread() == &Thread::current());
154
155 if (!scriptExecutionContext())
156 return Exception { InvalidStateError };
157
158 if (isFinishedOrFinishing())
159 return Exception { InvalidStateError, "Failed to execute 'objectStore' on 'IDBTransaction': The transaction finished."_s };
160
161 Locker<Lock> locker(m_referencedObjectStoreLock);
162
163 if (auto* store = m_referencedObjectStores.get(objectStoreName))
164 return makeRef(*store);
165
166 bool found = false;
167 for (auto& objectStore : m_info.objectStores()) {
168 if (objectStore == objectStoreName) {
169 found = true;
170 break;
171 }
172 }
173
174 auto* info = m_database->info().infoForExistingObjectStore(objectStoreName);
175 if (!info)
176 return Exception { NotFoundError, "Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found."_s };
177
178 // Version change transactions are scoped to every object store in the database.
179 if (!info || (!found && !isVersionChange()))
180 return Exception { NotFoundError, "Failed to execute 'objectStore' on 'IDBTransaction': The specified object store was not found."_s };
181
182 auto objectStore = std::make_unique<IDBObjectStore>(*scriptExecutionContext(), *info, *this);
183 auto* rawObjectStore = objectStore.get();
184 m_referencedObjectStores.set(objectStoreName, WTFMove(objectStore));
185
186 return Ref<IDBObjectStore>(*rawObjectStore);
187}
188
189
190void IDBTransaction::abortDueToFailedRequest(DOMException& error)
191{
192 LOG(IndexedDB, "IDBTransaction::abortDueToFailedRequest");
193 ASSERT(&m_database->originThread() == &Thread::current());
194
195 if (isFinishedOrFinishing())
196 return;
197
198 m_domError = &error;
199 internalAbort();
200}
201
202void IDBTransaction::transitionedToFinishing(IndexedDB::TransactionState state)
203{
204 ASSERT(&m_database->originThread() == &Thread::current());
205
206 ASSERT(!isFinishedOrFinishing());
207 m_state = state;
208 ASSERT(isFinishedOrFinishing());
209}
210
211ExceptionOr<void> IDBTransaction::abort()
212{
213 LOG(IndexedDB, "IDBTransaction::abort");
214 ASSERT(&m_database->originThread() == &Thread::current());
215
216 if (isFinishedOrFinishing())
217 return Exception { InvalidStateError, "Failed to execute 'abort' on 'IDBTransaction': The transaction is inactive or finished."_s };
218
219 internalAbort();
220
221 return { };
222}
223
224void IDBTransaction::internalAbort()
225{
226 LOG(IndexedDB, "IDBTransaction::internalAbort");
227 ASSERT(&m_database->originThread() == &Thread::current());
228 ASSERT(!isFinishedOrFinishing());
229
230 m_database->willAbortTransaction(*this);
231
232 if (isVersionChange()) {
233 Locker<Lock> locker(m_referencedObjectStoreLock);
234
235 auto& info = m_database->info();
236 Vector<uint64_t> identifiersToRemove;
237 for (auto& iterator : m_deletedObjectStores) {
238 if (info.infoForExistingObjectStore(iterator.key)) {
239 auto name = iterator.value->info().name();
240 m_referencedObjectStores.set(name, WTFMove(iterator.value));
241 identifiersToRemove.append(iterator.key);
242 }
243 }
244
245 for (auto identifier : identifiersToRemove)
246 m_deletedObjectStores.remove(identifier);
247
248 for (auto& objectStore : m_referencedObjectStores.values())
249 objectStore->rollbackForVersionChangeAbort();
250 }
251
252 transitionedToFinishing(IndexedDB::TransactionState::Aborting);
253
254 m_abortQueue.swap(m_pendingTransactionOperationQueue);
255
256 LOG(IndexedDBOperations, "IDB abort-on-server operation: Transaction %s", info().identifier().loggingString().utf8().data());
257 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, nullptr, [protectedThis = makeRef(*this)] (auto& operation) {
258 protectedThis->abortOnServerAndCancelRequests(operation);
259 }));
260}
261
262void IDBTransaction::abortInProgressOperations(const IDBError& error)
263{
264 LOG(IndexedDB, "IDBTransaction::abortInProgressOperations");
265
266 Vector<RefPtr<IDBClient::TransactionOperation>> inProgressAbortVector;
267 inProgressAbortVector.reserveInitialCapacity(m_transactionOperationsInProgressQueue.size());
268 while (!m_transactionOperationsInProgressQueue.isEmpty())
269 inProgressAbortVector.uncheckedAppend(m_transactionOperationsInProgressQueue.takeFirst());
270
271 for (auto& operation : inProgressAbortVector) {
272 m_transactionOperationsInProgressQueue.append(operation.get());
273 m_currentlyCompletingRequest = nullptr;
274 operation->doComplete(IDBResultData::error(operation->identifier(), error));
275 }
276
277 Vector<RefPtr<IDBClient::TransactionOperation>> completedOnServerAbortVector;
278 completedOnServerAbortVector.reserveInitialCapacity(m_completedOnServerQueue.size());
279 while (!m_completedOnServerQueue.isEmpty())
280 completedOnServerAbortVector.uncheckedAppend(m_completedOnServerQueue.takeFirst().first);
281
282 for (auto& operation : completedOnServerAbortVector) {
283 m_currentlyCompletingRequest = nullptr;
284 operation->doComplete(IDBResultData::error(operation->identifier(), error));
285 }
286
287 connectionProxy().forgetActiveOperations(inProgressAbortVector);
288}
289
290void IDBTransaction::abortOnServerAndCancelRequests(IDBClient::TransactionOperation& operation)
291{
292 LOG(IndexedDB, "IDBTransaction::abortOnServerAndCancelRequests");
293 ASSERT(&m_database->originThread() == &Thread::current());
294 ASSERT(m_pendingTransactionOperationQueue.isEmpty());
295
296 m_database->connectionProxy().abortTransaction(*this);
297
298 ASSERT(m_transactionOperationMap.contains(operation.identifier()));
299 ASSERT(m_transactionOperationsInProgressQueue.last() == &operation);
300 m_transactionOperationMap.remove(operation.identifier());
301 m_transactionOperationsInProgressQueue.removeLast();
302
303 m_currentlyCompletingRequest = nullptr;
304
305 IDBError error(AbortError);
306
307 abortInProgressOperations(error);
308
309 for (auto& operation : m_abortQueue) {
310 m_transactionOperationsInProgressQueue.append(operation.get());
311 operation->doComplete(IDBResultData::error(operation->identifier(), error));
312 m_currentlyCompletingRequest = nullptr;
313 }
314
315 m_abortQueue.clear();
316 // Since we're aborting, it should be impossible to have queued any further operations.
317 ASSERT(m_pendingTransactionOperationQueue.isEmpty());
318}
319
320const char* IDBTransaction::activeDOMObjectName() const
321{
322 ASSERT(&m_database->originThread() == &Thread::current());
323 return "IDBTransaction";
324}
325
326bool IDBTransaction::canSuspendForDocumentSuspension() const
327{
328 ASSERT(&m_database->originThread() == &Thread::current());
329 return false;
330}
331
332bool IDBTransaction::hasPendingActivity() const
333{
334 ASSERT(&m_database->originThread() == &Thread::current() || Thread::mayBeGCThread());
335 return !m_contextStopped && m_state != IndexedDB::TransactionState::Finished;
336}
337
338void IDBTransaction::stop()
339{
340 LOG(IndexedDB, "IDBTransaction::stop - %s", m_info.loggingString().utf8().data());
341 ASSERT(&m_database->originThread() == &Thread::current());
342
343 // IDBDatabase::stop() calls IDBTransaction::stop() for each of its active transactions.
344 // Since the order of calling ActiveDOMObject::stop() is random, we might already have been stopped.
345 if (m_contextStopped)
346 return;
347
348 removeAllEventListeners();
349
350 m_contextStopped = true;
351
352 if (isVersionChange())
353 m_openDBRequest = nullptr;
354
355 if (isFinishedOrFinishing())
356 return;
357
358 internalAbort();
359}
360
361bool IDBTransaction::isActive() const
362{
363 ASSERT(&m_database->originThread() == &Thread::current());
364 return m_state == IndexedDB::TransactionState::Active;
365}
366
367bool IDBTransaction::isFinishedOrFinishing() const
368{
369 ASSERT(&m_database->originThread() == &Thread::current());
370
371 return m_state == IndexedDB::TransactionState::Committing
372 || m_state == IndexedDB::TransactionState::Aborting
373 || m_state == IndexedDB::TransactionState::Finished;
374}
375
376void IDBTransaction::addRequest(IDBRequest& request)
377{
378 ASSERT(&m_database->originThread() == &Thread::current());
379 m_openRequests.add(&request);
380}
381
382void IDBTransaction::removeRequest(IDBRequest& request)
383{
384 ASSERT(&m_database->originThread() == &Thread::current());
385 m_openRequests.remove(&request);
386}
387
388void IDBTransaction::scheduleOperation(Ref<IDBClient::TransactionOperation>&& operation)
389{
390 ASSERT(!m_transactionOperationMap.contains(operation->identifier()));
391 ASSERT(&m_database->originThread() == &Thread::current());
392
393 auto identifier = operation->identifier();
394 m_pendingTransactionOperationQueue.append(operation.copyRef());
395 m_transactionOperationMap.set(identifier, WTFMove(operation));
396
397 schedulePendingOperationTimer();
398}
399
400void IDBTransaction::schedulePendingOperationTimer()
401{
402 ASSERT(&m_database->originThread() == &Thread::current());
403
404 if (!m_pendingOperationTimer.isActive())
405 m_pendingOperationTimer.startOneShot(0_s);
406}
407
408void IDBTransaction::pendingOperationTimerFired()
409{
410 LOG(IndexedDB, "IDBTransaction::pendingOperationTimerFired (%p)", this);
411 ASSERT(&m_database->originThread() == &Thread::current());
412
413 if (!m_startedOnServer)
414 return;
415
416 // If the last in-progress operation we've sent to the server is not an IDBRequest operation,
417 // then we have to wait until it completes before sending any more.
418 if (!m_transactionOperationsInProgressQueue.isEmpty() && !m_transactionOperationsInProgressQueue.last()->nextRequestCanGoToServer())
419 return;
420
421 // We want to batch operations together without spinning the runloop for performance,
422 // but don't want to affect responsiveness of the main thread.
423 // This number is a good compromise in ad-hoc testing.
424 static const size_t operationBatchLimit = 128;
425
426 for (size_t iterations = 0; !m_pendingTransactionOperationQueue.isEmpty() && iterations < operationBatchLimit; ++iterations) {
427 auto operation = m_pendingTransactionOperationQueue.takeFirst();
428 m_transactionOperationsInProgressQueue.append(operation.get());
429 operation->perform();
430
431 if (!operation->nextRequestCanGoToServer())
432 break;
433
434 }
435
436 if (!m_transactionOperationMap.isEmpty() || !m_openRequests.isEmpty())
437 return;
438
439 if (!isFinishedOrFinishing())
440 commit();
441}
442
443void IDBTransaction::operationCompletedOnServer(const IDBResultData& data, IDBClient::TransactionOperation& operation)
444{
445 ASSERT(&m_database->originThread() == &Thread::current());
446 ASSERT(&operation.originThread() == &Thread::current());
447
448 m_completedOnServerQueue.append({ &operation, data });
449 scheduleCompletedOperationTimer();
450}
451
452void IDBTransaction::scheduleCompletedOperationTimer()
453{
454 ASSERT(&m_database->originThread() == &Thread::current());
455
456 if (!m_completedOperationTimer.isActive())
457 m_completedOperationTimer.startOneShot(0_s);
458}
459
460void IDBTransaction::completedOperationTimerFired()
461{
462 LOG(IndexedDB, "IDBTransaction::completedOperationTimerFired (%p)", this);
463 ASSERT(&m_database->originThread() == &Thread::current());
464
465 if (m_completedOnServerQueue.isEmpty() || m_currentlyCompletingRequest)
466 return;
467
468 auto iterator = m_completedOnServerQueue.takeFirst();
469 iterator.first->doComplete(iterator.second);
470
471 if (!m_completedOnServerQueue.isEmpty() && !m_currentlyCompletingRequest)
472 scheduleCompletedOperationTimer();
473}
474
475void IDBTransaction::completeNoncursorRequest(IDBRequest& request, const IDBResultData& result)
476{
477 ASSERT(!m_currentlyCompletingRequest);
478
479 request.completeRequestAndDispatchEvent(result);
480
481 m_currentlyCompletingRequest = &request;
482}
483
484void IDBTransaction::completeCursorRequest(IDBRequest& request, const IDBResultData& result)
485{
486 ASSERT(!m_currentlyCompletingRequest);
487
488 request.didOpenOrIterateCursor(result);
489
490 m_currentlyCompletingRequest = &request;
491}
492
493void IDBTransaction::finishedDispatchEventForRequest(IDBRequest& request)
494{
495 if (isFinishedOrFinishing())
496 return;
497
498 ASSERT_UNUSED(request, !m_currentlyCompletingRequest || m_currentlyCompletingRequest == &request);
499
500 m_currentlyCompletingRequest = nullptr;
501 scheduleCompletedOperationTimer();
502}
503
504void IDBTransaction::commit()
505{
506 LOG(IndexedDB, "IDBTransaction::commit");
507 ASSERT(&m_database->originThread() == &Thread::current());
508 ASSERT(!isFinishedOrFinishing());
509
510 transitionedToFinishing(IndexedDB::TransactionState::Committing);
511 m_database->willCommitTransaction(*this);
512
513 LOG(IndexedDBOperations, "IDB commit operation: Transaction %s", info().identifier().loggingString().utf8().data());
514 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, nullptr, [protectedThis = makeRef(*this)] (auto& operation) {
515 protectedThis->commitOnServer(operation);
516 }));
517}
518
519void IDBTransaction::commitOnServer(IDBClient::TransactionOperation& operation)
520{
521 LOG(IndexedDB, "IDBTransaction::commitOnServer");
522 ASSERT(&m_database->originThread() == &Thread::current());
523
524 m_database->connectionProxy().commitTransaction(*this);
525
526 ASSERT(!m_transactionOperationsInProgressQueue.isEmpty());
527 ASSERT(m_transactionOperationsInProgressQueue.last() == &operation);
528 m_transactionOperationsInProgressQueue.removeLast();
529
530 ASSERT(m_transactionOperationMap.contains(operation.identifier()));
531 m_transactionOperationMap.remove(operation.identifier());
532}
533
534void IDBTransaction::finishAbortOrCommit()
535{
536 ASSERT(m_state != IndexedDB::TransactionState::Finished);
537 ASSERT(&m_database->originThread() == &Thread::current());
538
539 m_state = IndexedDB::TransactionState::Finished;
540}
541
542void IDBTransaction::didStart(const IDBError& error)
543{
544 LOG(IndexedDB, "IDBTransaction::didStart");
545 ASSERT(&m_database->originThread() == &Thread::current());
546
547 m_database->didStartTransaction(*this);
548
549 m_startedOnServer = true;
550
551 // It's possible the transaction failed to start on the server.
552 // That equates to an abort.
553 if (!error.isNull()) {
554 didAbort(error);
555 return;
556 }
557
558 schedulePendingOperationTimer();
559}
560
561void IDBTransaction::notifyDidAbort(const IDBError& error)
562{
563 ASSERT(&m_database->originThread() == &Thread::current());
564
565 m_database->didAbortTransaction(*this);
566 m_idbError = error;
567 fireOnAbort();
568
569 if (isVersionChange() && !m_contextStopped) {
570 ASSERT(m_openDBRequest);
571 m_openDBRequest->fireErrorAfterVersionChangeCompletion();
572 }
573}
574
575void IDBTransaction::didAbort(const IDBError& error)
576{
577 LOG(IndexedDB, "IDBTransaction::didAbort");
578 ASSERT(&m_database->originThread() == &Thread::current());
579
580 if (m_state == IndexedDB::TransactionState::Finished)
581 return;
582
583 notifyDidAbort(error);
584
585 finishAbortOrCommit();
586}
587
588void IDBTransaction::didCommit(const IDBError& error)
589{
590 LOG(IndexedDB, "IDBTransaction::didCommit");
591 ASSERT(&m_database->originThread() == &Thread::current());
592 ASSERT(m_state == IndexedDB::TransactionState::Committing);
593
594 if (error.isNull()) {
595 m_database->didCommitTransaction(*this);
596 fireOnComplete();
597 } else {
598 m_database->willAbortTransaction(*this);
599 notifyDidAbort(error);
600 }
601
602 finishAbortOrCommit();
603}
604
605void IDBTransaction::fireOnComplete()
606{
607 LOG(IndexedDB, "IDBTransaction::fireOnComplete");
608 ASSERT(&m_database->originThread() == &Thread::current());
609 enqueueEvent(Event::create(eventNames().completeEvent, Event::CanBubble::No, Event::IsCancelable::No));
610}
611
612void IDBTransaction::fireOnAbort()
613{
614 LOG(IndexedDB, "IDBTransaction::fireOnAbort");
615 ASSERT(&m_database->originThread() == &Thread::current());
616 enqueueEvent(Event::create(eventNames().abortEvent, Event::CanBubble::Yes, Event::IsCancelable::No));
617}
618
619void IDBTransaction::enqueueEvent(Ref<Event>&& event)
620{
621 ASSERT(m_state != IndexedDB::TransactionState::Finished);
622 ASSERT(&m_database->originThread() == &Thread::current());
623
624 if (!scriptExecutionContext() || m_contextStopped)
625 return;
626
627 event->setTarget(this);
628 scriptExecutionContext()->eventQueue().enqueueEvent(WTFMove(event));
629}
630
631void IDBTransaction::dispatchEvent(Event& event)
632{
633 LOG(IndexedDB, "IDBTransaction::dispatchEvent");
634
635 ASSERT(&m_database->originThread() == &Thread::current());
636 ASSERT(scriptExecutionContext());
637 ASSERT(!m_contextStopped);
638 ASSERT(event.target() == this);
639 ASSERT(event.type() == eventNames().completeEvent || event.type() == eventNames().abortEvent);
640
641 auto protectedThis = makeRef(*this);
642
643 EventDispatcher::dispatchEvent({ this, m_database.ptr() }, event);
644 m_didDispatchAbortOrCommit = true;
645
646 if (isVersionChange()) {
647 ASSERT(m_openDBRequest);
648 m_openDBRequest->versionChangeTransactionDidFinish();
649
650 if (event.type() == eventNames().completeEvent) {
651 if (m_database->isClosingOrClosed())
652 m_openDBRequest->fireErrorAfterVersionChangeCompletion();
653 else
654 m_openDBRequest->fireSuccessAfterVersionChangeCommit();
655 }
656
657 m_openDBRequest = nullptr;
658 }
659}
660
661Ref<IDBObjectStore> IDBTransaction::createObjectStore(const IDBObjectStoreInfo& info)
662{
663 LOG(IndexedDB, "IDBTransaction::createObjectStore");
664 ASSERT(isVersionChange());
665 ASSERT(scriptExecutionContext());
666 ASSERT(&m_database->originThread() == &Thread::current());
667
668 Locker<Lock> locker(m_referencedObjectStoreLock);
669
670 auto objectStore = std::make_unique<IDBObjectStore>(*scriptExecutionContext(), info, *this);
671 auto* rawObjectStore = objectStore.get();
672 m_referencedObjectStores.set(info.name(), WTFMove(objectStore));
673
674 LOG(IndexedDBOperations, "IDB create object store operation: %s", info.condensedLoggingString().utf8().data());
675 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, [protectedThis = makeRef(*this)] (const auto& result) {
676 protectedThis->didCreateObjectStoreOnServer(result);
677 }, [protectedThis = makeRef(*this), info = info.isolatedCopy()] (auto& operation) {
678 protectedThis->createObjectStoreOnServer(operation, info);
679 }));
680
681 return *rawObjectStore;
682}
683
684void IDBTransaction::createObjectStoreOnServer(IDBClient::TransactionOperation& operation, const IDBObjectStoreInfo& info)
685{
686 LOG(IndexedDB, "IDBTransaction::createObjectStoreOnServer");
687 ASSERT(&m_database->originThread() == &Thread::current());
688 ASSERT(isVersionChange());
689
690 m_database->connectionProxy().createObjectStore(operation, info);
691}
692
693void IDBTransaction::didCreateObjectStoreOnServer(const IDBResultData& resultData)
694{
695 LOG(IndexedDB, "IDBTransaction::didCreateObjectStoreOnServer");
696 ASSERT(&m_database->originThread() == &Thread::current());
697 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::CreateObjectStoreSuccess || resultData.type() == IDBResultType::Error);
698}
699
700void IDBTransaction::renameObjectStore(IDBObjectStore& objectStore, const String& newName)
701{
702 LOG(IndexedDB, "IDBTransaction::renameObjectStore");
703
704 Locker<Lock> locker(m_referencedObjectStoreLock);
705
706 ASSERT(isVersionChange());
707 ASSERT(scriptExecutionContext());
708 ASSERT(&m_database->originThread() == &Thread::current());
709
710 ASSERT(m_referencedObjectStores.contains(objectStore.info().name()));
711 ASSERT(!m_referencedObjectStores.contains(newName));
712 ASSERT(m_referencedObjectStores.get(objectStore.info().name()) == &objectStore);
713
714 uint64_t objectStoreIdentifier = objectStore.info().identifier();
715
716 LOG(IndexedDBOperations, "IDB rename object store operation: %s to %s", objectStore.info().condensedLoggingString().utf8().data(), newName.utf8().data());
717 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, [protectedThis = makeRef(*this)] (const auto& result) {
718 protectedThis->didRenameObjectStoreOnServer(result);
719 }, [protectedThis = makeRef(*this), objectStoreIdentifier, newName = newName.isolatedCopy()] (auto& operation) {
720 protectedThis->renameObjectStoreOnServer(operation, objectStoreIdentifier, newName);
721 }));
722
723 m_referencedObjectStores.set(newName, m_referencedObjectStores.take(objectStore.info().name()));
724}
725
726void IDBTransaction::renameObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& newName)
727{
728 LOG(IndexedDB, "IDBTransaction::renameObjectStoreOnServer");
729 ASSERT(&m_database->originThread() == &Thread::current());
730 ASSERT(isVersionChange());
731
732 m_database->connectionProxy().renameObjectStore(operation, objectStoreIdentifier, newName);
733}
734
735void IDBTransaction::didRenameObjectStoreOnServer(const IDBResultData& resultData)
736{
737 LOG(IndexedDB, "IDBTransaction::didRenameObjectStoreOnServer");
738 ASSERT(&m_database->originThread() == &Thread::current());
739 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::RenameObjectStoreSuccess || resultData.type() == IDBResultType::Error);
740}
741
742std::unique_ptr<IDBIndex> IDBTransaction::createIndex(IDBObjectStore& objectStore, const IDBIndexInfo& info)
743{
744 LOG(IndexedDB, "IDBTransaction::createIndex");
745 ASSERT(isVersionChange());
746 ASSERT(&m_database->originThread() == &Thread::current());
747
748 if (!scriptExecutionContext())
749 return nullptr;
750
751 LOG(IndexedDBOperations, "IDB create index operation: %s under object store %s", info.condensedLoggingString().utf8().data(), objectStore.info().condensedLoggingString().utf8().data());
752 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, [protectedThis = makeRef(*this)] (const auto& result) {
753 protectedThis->didCreateIndexOnServer(result);
754 }, [protectedThis = makeRef(*this), info = info.isolatedCopy()] (auto& operation) {
755 protectedThis->createIndexOnServer(operation, info);
756 }));
757
758 return std::make_unique<IDBIndex>(*scriptExecutionContext(), info, objectStore);
759}
760
761void IDBTransaction::createIndexOnServer(IDBClient::TransactionOperation& operation, const IDBIndexInfo& info)
762{
763 LOG(IndexedDB, "IDBTransaction::createIndexOnServer");
764 ASSERT(&m_database->originThread() == &Thread::current());
765 ASSERT(isVersionChange());
766
767 m_database->connectionProxy().createIndex(operation, info);
768}
769
770void IDBTransaction::didCreateIndexOnServer(const IDBResultData& resultData)
771{
772 LOG(IndexedDB, "IDBTransaction::didCreateIndexOnServer");
773 ASSERT(&m_database->originThread() == &Thread::current());
774
775 if (resultData.type() == IDBResultType::CreateIndexSuccess)
776 return;
777
778 ASSERT(resultData.type() == IDBResultType::Error);
779
780 // This operation might have failed because the transaction is already aborting.
781 if (m_state == IndexedDB::TransactionState::Aborting)
782 return;
783
784 // Otherwise, failure to create an index forced abortion of the transaction.
785 abortDueToFailedRequest(DOMException::create(resultData.error().message(), resultData.error().name()));
786}
787
788void IDBTransaction::renameIndex(IDBIndex& index, const String& newName)
789{
790 LOG(IndexedDB, "IDBTransaction::renameIndex");
791 Locker<Lock> locker(m_referencedObjectStoreLock);
792
793 ASSERT(isVersionChange());
794 ASSERT(scriptExecutionContext());
795 ASSERT(&m_database->originThread() == &Thread::current());
796
797 ASSERT(m_referencedObjectStores.contains(index.objectStore().info().name()));
798 ASSERT(m_referencedObjectStores.get(index.objectStore().info().name()) == &index.objectStore());
799
800 index.objectStore().renameReferencedIndex(index, newName);
801
802 uint64_t objectStoreIdentifier = index.objectStore().info().identifier();
803 uint64_t indexIdentifier = index.info().identifier();
804
805 LOG(IndexedDBOperations, "IDB rename index operation: %s to %s under object store %" PRIu64, index.info().condensedLoggingString().utf8().data(), newName.utf8().data(), index.info().objectStoreIdentifier());
806 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, [protectedThis = makeRef(*this)] (const auto& result) {
807 protectedThis->didRenameIndexOnServer(result);
808 }, [protectedThis = makeRef(*this), objectStoreIdentifier, indexIdentifier, newName = newName.isolatedCopy()] (auto& operation) {
809 protectedThis->renameIndexOnServer(operation, objectStoreIdentifier, indexIdentifier, newName);
810 }));
811}
812
813void IDBTransaction::renameIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const uint64_t& indexIdentifier, const String& newName)
814{
815 LOG(IndexedDB, "IDBTransaction::renameIndexOnServer");
816 ASSERT(&m_database->originThread() == &Thread::current());
817 ASSERT(isVersionChange());
818
819 m_database->connectionProxy().renameIndex(operation, objectStoreIdentifier, indexIdentifier, newName);
820}
821
822void IDBTransaction::didRenameIndexOnServer(const IDBResultData& resultData)
823{
824 LOG(IndexedDB, "IDBTransaction::didRenameIndexOnServer");
825 ASSERT(&m_database->originThread() == &Thread::current());
826 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::RenameIndexSuccess || resultData.type() == IDBResultType::Error);
827}
828
829Ref<IDBRequest> IDBTransaction::requestOpenCursor(ExecState& state, IDBObjectStore& objectStore, const IDBCursorInfo& info)
830{
831 LOG(IndexedDB, "IDBTransaction::requestOpenCursor");
832 ASSERT(&m_database->originThread() == &Thread::current());
833
834 if (info.cursorType() == IndexedDB::CursorType::KeyOnly)
835 return doRequestOpenCursor(state, IDBCursor::create(objectStore, info));
836
837 return doRequestOpenCursor(state, IDBCursorWithValue::create(objectStore, info));
838}
839
840Ref<IDBRequest> IDBTransaction::requestOpenCursor(ExecState& state, IDBIndex& index, const IDBCursorInfo& info)
841{
842 LOG(IndexedDB, "IDBTransaction::requestOpenCursor");
843 ASSERT(&m_database->originThread() == &Thread::current());
844
845 if (info.cursorType() == IndexedDB::CursorType::KeyOnly)
846 return doRequestOpenCursor(state, IDBCursor::create(index, info));
847
848 return doRequestOpenCursor(state, IDBCursorWithValue::create(index, info));
849}
850
851Ref<IDBRequest> IDBTransaction::doRequestOpenCursor(ExecState& state, Ref<IDBCursor>&& cursor)
852{
853 ASSERT(isActive());
854 ASSERT(&m_database->originThread() == &Thread::current());
855
856 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
857
858 auto request = IDBRequest::create(*scriptExecutionContext(), cursor.get(), *this);
859 addRequest(request.get());
860
861 LOG(IndexedDBOperations, "IDB open cursor operation: %s", cursor->info().loggingString().utf8().data());
862 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
863 protectedThis->didOpenCursorOnServer(request.get(), result);
864 }, [protectedThis = makeRef(*this), info = cursor->info().isolatedCopy()] (auto& operation) {
865 protectedThis->openCursorOnServer(operation, info);
866 }));
867
868 return request;
869}
870
871void IDBTransaction::openCursorOnServer(IDBClient::TransactionOperation& operation, const IDBCursorInfo& info)
872{
873 LOG(IndexedDB, "IDBTransaction::openCursorOnServer");
874 ASSERT(&m_database->originThread() == &Thread::current());
875
876 m_database->connectionProxy().openCursor(operation, info);
877}
878
879void IDBTransaction::didOpenCursorOnServer(IDBRequest& request, const IDBResultData& resultData)
880{
881 LOG(IndexedDB, "IDBTransaction::didOpenCursorOnServer");
882 ASSERT(&m_database->originThread() == &Thread::current());
883
884 completeCursorRequest(request, resultData);
885}
886
887void IDBTransaction::iterateCursor(IDBCursor& cursor, const IDBIterateCursorData& data)
888{
889 LOG(IndexedDB, "IDBTransaction::iterateCursor");
890 ASSERT(isActive());
891 ASSERT(cursor.request());
892 ASSERT(&m_database->originThread() == &Thread::current());
893
894 addRequest(*cursor.request());
895
896 LOG(IndexedDBOperations, "IDB iterate cursor operation: %s %s", cursor.info().loggingString().utf8().data(), data.loggingString().utf8().data());
897 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, *cursor.request(), [protectedThis = makeRef(*this), request = makeRef(*cursor.request())] (const auto& result) {
898 protectedThis->didIterateCursorOnServer(request.get(), result);
899 }, [protectedThis = makeRef(*this), data = data.isolatedCopy()] (auto& operation) {
900 protectedThis->iterateCursorOnServer(operation, data);
901 }));
902}
903
904// FIXME: changes here
905void IDBTransaction::iterateCursorOnServer(IDBClient::TransactionOperation& operation, const IDBIterateCursorData& data)
906{
907 LOG(IndexedDB, "IDBTransaction::iterateCursorOnServer");
908 ASSERT(&m_database->originThread() == &Thread::current());
909
910 m_database->connectionProxy().iterateCursor(operation, data);
911}
912
913void IDBTransaction::didIterateCursorOnServer(IDBRequest& request, const IDBResultData& resultData)
914{
915 LOG(IndexedDB, "IDBTransaction::didIterateCursorOnServer");
916 ASSERT(&m_database->originThread() == &Thread::current());
917
918 completeCursorRequest(request, resultData);
919}
920
921Ref<IDBRequest> IDBTransaction::requestGetAllObjectStoreRecords(JSC::ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, Optional<uint32_t> count)
922{
923 LOG(IndexedDB, "IDBTransaction::requestGetAllObjectStoreRecords");
924 ASSERT(isActive());
925 ASSERT(&m_database->originThread() == &Thread::current());
926
927 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
928
929 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
930 addRequest(request.get());
931
932 IDBGetAllRecordsData getAllRecordsData { keyRangeData, getAllType, count, objectStore.info().identifier(), 0 };
933
934 LOG(IndexedDBOperations, "IDB get all object store records operation: %s", getAllRecordsData.loggingString().utf8().data());
935 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
936 protectedThis->didGetAllRecordsOnServer(request.get(), result);
937 }, [protectedThis = makeRef(*this), getAllRecordsData = getAllRecordsData.isolatedCopy()] (auto& operation) {
938 protectedThis->getAllRecordsOnServer(operation, getAllRecordsData);
939 }));
940
941 return request;
942}
943
944Ref<IDBRequest> IDBTransaction::requestGetAllIndexRecords(JSC::ExecState& state, IDBIndex& index, const IDBKeyRangeData& keyRangeData, IndexedDB::GetAllType getAllType, Optional<uint32_t> count)
945{
946 LOG(IndexedDB, "IDBTransaction::requestGetAllIndexRecords");
947 ASSERT(isActive());
948 ASSERT(&m_database->originThread() == &Thread::current());
949
950 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
951
952 auto request = IDBRequest::create(*scriptExecutionContext(), index, *this);
953 addRequest(request.get());
954
955 IDBGetAllRecordsData getAllRecordsData { keyRangeData, getAllType, count, index.objectStore().info().identifier(), index.info().identifier() };
956
957 LOG(IndexedDBOperations, "IDB get all index records operation: %s", getAllRecordsData.loggingString().utf8().data());
958 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
959 protectedThis->didGetAllRecordsOnServer(request.get(), result);
960 }, [protectedThis = makeRef(*this), getAllRecordsData = getAllRecordsData.isolatedCopy()] (auto& operation) {
961 protectedThis->getAllRecordsOnServer(operation, getAllRecordsData);
962 }));
963
964 return request;
965}
966
967void IDBTransaction::getAllRecordsOnServer(IDBClient::TransactionOperation& operation, const IDBGetAllRecordsData& getAllRecordsData)
968{
969 LOG(IndexedDB, "IDBTransaction::getAllRecordsOnServer");
970 ASSERT(&m_database->originThread() == &Thread::current());
971
972 m_database->connectionProxy().getAllRecords(operation, getAllRecordsData);
973}
974
975void IDBTransaction::didGetAllRecordsOnServer(IDBRequest& request, const IDBResultData& resultData)
976{
977 LOG(IndexedDB, "IDBTransaction::didGetAllRecordsOnServer");
978 ASSERT(&m_database->originThread() == &Thread::current());
979
980 if (resultData.type() == IDBResultType::Error) {
981 completeNoncursorRequest(request, resultData);
982 return;
983 }
984
985 ASSERT(resultData.type() == IDBResultType::GetAllRecordsSuccess);
986
987 auto& getAllResult = resultData.getAllResult();
988 switch (getAllResult.type()) {
989 case IndexedDB::GetAllType::Keys:
990 request.setResult(getAllResult.keys());
991 break;
992 case IndexedDB::GetAllType::Values:
993 request.setResult(getAllResult);
994 break;
995 }
996
997 completeNoncursorRequest(request, resultData);
998}
999
1000Ref<IDBRequest> IDBTransaction::requestGetRecord(ExecState& state, IDBObjectStore& objectStore, const IDBGetRecordData& getRecordData)
1001{
1002 LOG(IndexedDB, "IDBTransaction::requestGetRecord");
1003 ASSERT(isActive());
1004 ASSERT(!getRecordData.keyRangeData.isNull);
1005 ASSERT(&m_database->originThread() == &Thread::current());
1006
1007 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1008
1009 IndexedDB::ObjectStoreRecordType type = getRecordData.type == IDBGetRecordDataType::KeyAndValue ? IndexedDB::ObjectStoreRecordType::ValueOnly : IndexedDB::ObjectStoreRecordType::KeyOnly;
1010
1011 auto request = IDBRequest::createObjectStoreGet(*scriptExecutionContext(), objectStore, type, *this);
1012 addRequest(request.get());
1013
1014 LOG(IndexedDBOperations, "IDB get record operation: %s %s", objectStore.info().condensedLoggingString().utf8().data(), getRecordData.loggingString().utf8().data());
1015 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
1016 protectedThis->didGetRecordOnServer(request.get(), result);
1017 }, [protectedThis = makeRef(*this), getRecordData = getRecordData.isolatedCopy()] (auto& operation) {
1018 protectedThis->getRecordOnServer(operation, getRecordData);
1019 }));
1020
1021 return request;
1022}
1023
1024Ref<IDBRequest> IDBTransaction::requestGetValue(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
1025{
1026 LOG(IndexedDB, "IDBTransaction::requestGetValue");
1027 ASSERT(&m_database->originThread() == &Thread::current());
1028
1029 return requestIndexRecord(state, index, IndexedDB::IndexRecordType::Value, range);
1030}
1031
1032Ref<IDBRequest> IDBTransaction::requestGetKey(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
1033{
1034 LOG(IndexedDB, "IDBTransaction::requestGetValue");
1035 ASSERT(&m_database->originThread() == &Thread::current());
1036
1037 return requestIndexRecord(state, index, IndexedDB::IndexRecordType::Key, range);
1038}
1039
1040Ref<IDBRequest> IDBTransaction::requestIndexRecord(ExecState& state, IDBIndex& index, IndexedDB::IndexRecordType type, const IDBKeyRangeData& range)
1041{
1042 LOG(IndexedDB, "IDBTransaction::requestGetValue");
1043 ASSERT(isActive());
1044 ASSERT(!range.isNull);
1045 ASSERT(&m_database->originThread() == &Thread::current());
1046
1047 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1048
1049 auto request = IDBRequest::createIndexGet(*scriptExecutionContext(), index, type, *this);
1050 addRequest(request.get());
1051
1052 IDBGetRecordData getRecordData = { range, IDBGetRecordDataType::KeyAndValue };
1053
1054 LOG(IndexedDBOperations, "IDB get index record operation: %s %s", index.info().condensedLoggingString().utf8().data(), getRecordData.loggingString().utf8().data());
1055 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
1056 protectedThis->didGetRecordOnServer(request.get(), result);
1057 }, [protectedThis = makeRef(*this), getRecordData = getRecordData.isolatedCopy()] (auto& operation) {
1058 protectedThis->getRecordOnServer(operation, getRecordData);
1059 }));
1060
1061 return request;
1062}
1063
1064void IDBTransaction::getRecordOnServer(IDBClient::TransactionOperation& operation, const IDBGetRecordData& getRecordData)
1065{
1066 LOG(IndexedDB, "IDBTransaction::getRecordOnServer");
1067 ASSERT(&m_database->originThread() == &Thread::current());
1068
1069 m_database->connectionProxy().getRecord(operation, getRecordData);
1070}
1071
1072void IDBTransaction::didGetRecordOnServer(IDBRequest& request, const IDBResultData& resultData)
1073{
1074 LOG(IndexedDB, "IDBTransaction::didGetRecordOnServer");
1075 ASSERT(&m_database->originThread() == &Thread::current());
1076
1077 if (resultData.type() == IDBResultType::Error) {
1078 completeNoncursorRequest(request, resultData);
1079 return;
1080 }
1081
1082 ASSERT(resultData.type() == IDBResultType::GetRecordSuccess);
1083
1084 bool useResultKey = request.sourceIndexIdentifier() && request.requestedIndexRecordType() == IndexedDB::IndexRecordType::Key;
1085 if (!useResultKey)
1086 useResultKey = request.requestedObjectStoreRecordType() == IndexedDB::ObjectStoreRecordType::KeyOnly;
1087
1088 const IDBGetResult& result = resultData.getResult();
1089
1090 if (useResultKey) {
1091 if (!result.keyData().isNull())
1092 request.setResult(result.keyData());
1093 else
1094 request.setResultToUndefined();
1095 } else {
1096 if (resultData.getResult().value().data().data())
1097 request.setResultToStructuredClone(resultData.getResult());
1098 else
1099 request.setResultToUndefined();
1100 }
1101
1102 completeNoncursorRequest(request, resultData);
1103}
1104
1105Ref<IDBRequest> IDBTransaction::requestCount(ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& range)
1106{
1107 LOG(IndexedDB, "IDBTransaction::requestCount (IDBObjectStore)");
1108 ASSERT(isActive());
1109 ASSERT(!range.isNull);
1110 ASSERT(&m_database->originThread() == &Thread::current());
1111
1112 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1113
1114 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
1115 addRequest(request.get());
1116
1117 LOG(IndexedDBOperations, "IDB object store count operation: %s, range %s", objectStore.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data());
1118 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
1119 protectedThis->didGetCountOnServer(request.get(), result);
1120 }, [protectedThis = makeRef(*this), range = range.isolatedCopy()] (auto& operation) {
1121 protectedThis->getCountOnServer(operation, range);
1122 }));
1123
1124 return request;
1125}
1126
1127Ref<IDBRequest> IDBTransaction::requestCount(ExecState& state, IDBIndex& index, const IDBKeyRangeData& range)
1128{
1129 LOG(IndexedDB, "IDBTransaction::requestCount (IDBIndex)");
1130 ASSERT(isActive());
1131 ASSERT(!range.isNull);
1132 ASSERT(&m_database->originThread() == &Thread::current());
1133
1134 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1135
1136 auto request = IDBRequest::create(*scriptExecutionContext(), index, *this);
1137 addRequest(request.get());
1138
1139 LOG(IndexedDBOperations, "IDB index count operation: %s, range %s", index.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data());
1140 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
1141 protectedThis->didGetCountOnServer(request.get(), result);
1142 }, [protectedThis = makeRef(*this), range = range.isolatedCopy()] (auto& operation) {
1143 protectedThis->getCountOnServer(operation, range);
1144 }));
1145
1146 return request;
1147}
1148
1149void IDBTransaction::getCountOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
1150{
1151 LOG(IndexedDB, "IDBTransaction::getCountOnServer");
1152 ASSERT(&m_database->originThread() == &Thread::current());
1153
1154 m_database->connectionProxy().getCount(operation, keyRange);
1155}
1156
1157void IDBTransaction::didGetCountOnServer(IDBRequest& request, const IDBResultData& resultData)
1158{
1159 LOG(IndexedDB, "IDBTransaction::didGetCountOnServer");
1160 ASSERT(&m_database->originThread() == &Thread::current());
1161
1162 request.setResult(resultData.resultInteger());
1163 completeNoncursorRequest(request, resultData);
1164}
1165
1166Ref<IDBRequest> IDBTransaction::requestDeleteRecord(ExecState& state, IDBObjectStore& objectStore, const IDBKeyRangeData& range)
1167{
1168 LOG(IndexedDB, "IDBTransaction::requestDeleteRecord");
1169 ASSERT(isActive());
1170 ASSERT(!range.isNull);
1171 ASSERT(&m_database->originThread() == &Thread::current());
1172
1173 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1174
1175 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
1176 addRequest(request.get());
1177
1178 LOG(IndexedDBOperations, "IDB delete record operation: %s, range %s", objectStore.info().condensedLoggingString().utf8().data(), range.loggingString().utf8().data());
1179 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
1180 protectedThis->didDeleteRecordOnServer(request.get(), result);
1181 }, [protectedThis = makeRef(*this), range = range.isolatedCopy()] (auto& operation) {
1182 protectedThis->deleteRecordOnServer(operation, range);
1183 }));
1184 return request;
1185}
1186
1187void IDBTransaction::deleteRecordOnServer(IDBClient::TransactionOperation& operation, const IDBKeyRangeData& keyRange)
1188{
1189 LOG(IndexedDB, "IDBTransaction::deleteRecordOnServer");
1190 ASSERT(&m_database->originThread() == &Thread::current());
1191
1192 m_database->connectionProxy().deleteRecord(operation, keyRange);
1193}
1194
1195void IDBTransaction::didDeleteRecordOnServer(IDBRequest& request, const IDBResultData& resultData)
1196{
1197 LOG(IndexedDB, "IDBTransaction::didDeleteRecordOnServer");
1198 ASSERT(&m_database->originThread() == &Thread::current());
1199
1200 request.setResultToUndefined();
1201 completeNoncursorRequest(request, resultData);
1202}
1203
1204Ref<IDBRequest> IDBTransaction::requestClearObjectStore(ExecState& state, IDBObjectStore& objectStore)
1205{
1206 LOG(IndexedDB, "IDBTransaction::requestClearObjectStore");
1207 ASSERT(isActive());
1208 ASSERT(&m_database->originThread() == &Thread::current());
1209
1210 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1211
1212 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
1213 addRequest(request.get());
1214
1215 uint64_t objectStoreIdentifier = objectStore.info().identifier();
1216
1217 LOG(IndexedDBOperations, "IDB clear object store operation: %s", objectStore.info().condensedLoggingString().utf8().data());
1218 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
1219 protectedThis->didClearObjectStoreOnServer(request.get(), result);
1220 }, [protectedThis = makeRef(*this), objectStoreIdentifier] (auto& operation) {
1221 protectedThis->clearObjectStoreOnServer(operation, objectStoreIdentifier);
1222 }));
1223
1224 return request;
1225}
1226
1227void IDBTransaction::clearObjectStoreOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier)
1228{
1229 LOG(IndexedDB, "IDBTransaction::clearObjectStoreOnServer");
1230 ASSERT(&m_database->originThread() == &Thread::current());
1231
1232 m_database->connectionProxy().clearObjectStore(operation, objectStoreIdentifier);
1233}
1234
1235void IDBTransaction::didClearObjectStoreOnServer(IDBRequest& request, const IDBResultData& resultData)
1236{
1237 LOG(IndexedDB, "IDBTransaction::didClearObjectStoreOnServer");
1238 ASSERT(&m_database->originThread() == &Thread::current());
1239
1240 request.setResultToUndefined();
1241 completeNoncursorRequest(request, resultData);
1242}
1243
1244Ref<IDBRequest> IDBTransaction::requestPutOrAdd(ExecState& state, IDBObjectStore& objectStore, RefPtr<IDBKey>&& key, SerializedScriptValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode)
1245{
1246 LOG(IndexedDB, "IDBTransaction::requestPutOrAdd");
1247 ASSERT(isActive());
1248 ASSERT(!isReadOnly());
1249 ASSERT(objectStore.info().autoIncrement() || key);
1250 ASSERT(&m_database->originThread() == &Thread::current());
1251
1252 ASSERT_UNUSED(state, scriptExecutionContext() == scriptExecutionContextFromExecState(&state));
1253
1254 auto request = IDBRequest::create(*scriptExecutionContext(), objectStore, *this);
1255 addRequest(request.get());
1256
1257 LOG(IndexedDBOperations, "IDB putOrAdd operation: %s key: %s", objectStore.info().condensedLoggingString().utf8().data(), key ? key->loggingString().utf8().data() : "<null key>");
1258 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, request.get(), [protectedThis = makeRef(*this), request = request.copyRef()] (const auto& result) {
1259 protectedThis->didPutOrAddOnServer(request.get(), result);
1260 }, [protectedThis = makeRef(*this), key, value = makeRef(value), overwriteMode] (auto& operation) {
1261 protectedThis->putOrAddOnServer(operation, key.get(), value.ptr(), overwriteMode);
1262 }));
1263
1264 return request;
1265}
1266
1267void IDBTransaction::putOrAddOnServer(IDBClient::TransactionOperation& operation, RefPtr<IDBKey> key, RefPtr<SerializedScriptValue> value, const IndexedDB::ObjectStoreOverwriteMode& overwriteMode)
1268{
1269 LOG(IndexedDB, "IDBTransaction::putOrAddOnServer");
1270 ASSERT(&originThread() == &Thread::current());
1271 ASSERT(!isReadOnly());
1272 ASSERT(value);
1273
1274 if (!value->hasBlobURLs()) {
1275 m_database->connectionProxy().putOrAdd(operation, key.get(), *value, overwriteMode);
1276 return;
1277 }
1278
1279 // Due to current limitations on our ability to post tasks back to a worker thread,
1280 // workers currently write blobs to disk synchronously.
1281 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=157958 - Make this asynchronous after refactoring allows it.
1282 if (!isMainThread()) {
1283 auto idbValue = value->writeBlobsToDiskForIndexedDBSynchronously();
1284 if (idbValue.data().data())
1285 m_database->connectionProxy().putOrAdd(operation, key.get(), idbValue, overwriteMode);
1286 else {
1287 // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
1288 // In that case, we cannot successfully store this record, so we callback with an error.
1289 RefPtr<IDBClient::TransactionOperation> protectedOperation(&operation);
1290 auto result = IDBResultData::error(operation.identifier(), IDBError { UnknownError, "Error preparing Blob/File data to be stored in object store"_s });
1291 scriptExecutionContext()->postTask([protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)](ScriptExecutionContext&) {
1292 protectedOperation->doComplete(result);
1293 });
1294 }
1295 return;
1296 }
1297
1298 // Since this request won't actually go to the server until the blob writes are complete,
1299 // stop future requests from going to the server ahead of it.
1300 operation.setNextRequestCanGoToServer(false);
1301
1302 value->writeBlobsToDiskForIndexedDB([protectedThis = makeRef(*this), this, protectedOperation = Ref<IDBClient::TransactionOperation>(operation), keyData = IDBKeyData(key.get()).isolatedCopy(), overwriteMode](IDBValue&& idbValue) mutable {
1303 ASSERT(&originThread() == &Thread::current());
1304 ASSERT(isMainThread());
1305 if (idbValue.data().data()) {
1306 m_database->connectionProxy().putOrAdd(protectedOperation.get(), WTFMove(keyData), idbValue, overwriteMode);
1307 return;
1308 }
1309
1310 // If the IDBValue doesn't have any data, then something went wrong writing the blobs to disk.
1311 // In that case, we cannot successfully store this record, so we callback with an error.
1312 auto result = IDBResultData::error(protectedOperation->identifier(), IDBError { UnknownError, "Error preparing Blob/File data to be stored in object store"_s });
1313 callOnMainThread([protectedThis = WTFMove(protectedThis), protectedOperation = WTFMove(protectedOperation), result = WTFMove(result)]() mutable {
1314 protectedOperation->doComplete(result);
1315 });
1316 });
1317}
1318
1319void IDBTransaction::didPutOrAddOnServer(IDBRequest& request, const IDBResultData& resultData)
1320{
1321 LOG(IndexedDB, "IDBTransaction::didPutOrAddOnServer");
1322 ASSERT(&m_database->originThread() == &Thread::current());
1323
1324 if (auto* result = resultData.resultKey())
1325 request.setResult(*result);
1326 else
1327 request.setResultToUndefined();
1328 completeNoncursorRequest(request, resultData);
1329}
1330
1331void IDBTransaction::deleteObjectStore(const String& objectStoreName)
1332{
1333 LOG(IndexedDB, "IDBTransaction::deleteObjectStore");
1334 ASSERT(&m_database->originThread() == &Thread::current());
1335 ASSERT(isVersionChange());
1336
1337 Locker<Lock> locker(m_referencedObjectStoreLock);
1338
1339 if (auto objectStore = m_referencedObjectStores.take(objectStoreName)) {
1340 objectStore->markAsDeleted();
1341 auto identifier = objectStore->info().identifier();
1342 m_deletedObjectStores.set(identifier, WTFMove(objectStore));
1343 }
1344
1345 LOG(IndexedDBOperations, "IDB delete object store operation: %s", objectStoreName.utf8().data());
1346 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, [protectedThis = makeRef(*this)] (const auto& result) {
1347 protectedThis->didDeleteObjectStoreOnServer(result);
1348 }, [protectedThis = makeRef(*this), objectStoreName = objectStoreName.isolatedCopy()] (auto& operation) {
1349 protectedThis->deleteObjectStoreOnServer(operation, objectStoreName);
1350 }));
1351}
1352
1353void IDBTransaction::deleteObjectStoreOnServer(IDBClient::TransactionOperation& operation, const String& objectStoreName)
1354{
1355 LOG(IndexedDB, "IDBTransaction::deleteObjectStoreOnServer");
1356 ASSERT(isVersionChange());
1357 ASSERT(&m_database->originThread() == &Thread::current());
1358
1359 m_database->connectionProxy().deleteObjectStore(operation, objectStoreName);
1360}
1361
1362void IDBTransaction::didDeleteObjectStoreOnServer(const IDBResultData& resultData)
1363{
1364 LOG(IndexedDB, "IDBTransaction::didDeleteObjectStoreOnServer");
1365 ASSERT(&m_database->originThread() == &Thread::current());
1366 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteObjectStoreSuccess || resultData.type() == IDBResultType::Error);
1367}
1368
1369void IDBTransaction::deleteIndex(uint64_t objectStoreIdentifier, const String& indexName)
1370{
1371 LOG(IndexedDB, "IDBTransaction::deleteIndex");
1372 ASSERT(&m_database->originThread() == &Thread::current());
1373 ASSERT(isVersionChange());
1374
1375 LOG(IndexedDBOperations, "IDB delete index operation: %s (%" PRIu64 ")", indexName.utf8().data(), objectStoreIdentifier);
1376 scheduleOperation(IDBClient::TransactionOperationImpl::create(*this, [protectedThis = makeRef(*this)] (const auto& result) {
1377 protectedThis->didDeleteIndexOnServer(result);
1378 }, [protectedThis = makeRef(*this), objectStoreIdentifier, indexName = indexName.isolatedCopy()] (auto& operation) {
1379 protectedThis->deleteIndexOnServer(operation, objectStoreIdentifier, indexName);
1380 }));
1381}
1382
1383void IDBTransaction::deleteIndexOnServer(IDBClient::TransactionOperation& operation, const uint64_t& objectStoreIdentifier, const String& indexName)
1384{
1385 LOG(IndexedDB, "IDBTransaction::deleteIndexOnServer");
1386 ASSERT(isVersionChange());
1387 ASSERT(&m_database->originThread() == &Thread::current());
1388
1389 m_database->connectionProxy().deleteIndex(operation, objectStoreIdentifier, indexName);
1390}
1391
1392void IDBTransaction::didDeleteIndexOnServer(const IDBResultData& resultData)
1393{
1394 LOG(IndexedDB, "IDBTransaction::didDeleteIndexOnServer");
1395 ASSERT(&m_database->originThread() == &Thread::current());
1396 ASSERT_UNUSED(resultData, resultData.type() == IDBResultType::DeleteIndexSuccess || resultData.type() == IDBResultType::Error);
1397}
1398
1399void IDBTransaction::operationCompletedOnClient(IDBClient::TransactionOperation& operation)
1400{
1401 LOG(IndexedDB, "IDBTransaction::operationCompletedOnClient");
1402
1403 ASSERT(&m_database->originThread() == &Thread::current());
1404 ASSERT(&operation.originThread() == &Thread::current());
1405 ASSERT(m_transactionOperationMap.get(operation.identifier()) == &operation);
1406 ASSERT(m_transactionOperationsInProgressQueue.first() == &operation);
1407
1408 m_transactionOperationMap.remove(operation.identifier());
1409 m_transactionOperationsInProgressQueue.removeFirst();
1410
1411 schedulePendingOperationTimer();
1412}
1413
1414void IDBTransaction::establishOnServer()
1415{
1416 LOG(IndexedDB, "IDBTransaction::establishOnServer");
1417 ASSERT(&m_database->originThread() == &Thread::current());
1418
1419 m_database->connectionProxy().establishTransaction(*this);
1420}
1421
1422void IDBTransaction::activate()
1423{
1424 ASSERT(&m_database->originThread() == &Thread::current());
1425
1426 if (isFinishedOrFinishing())
1427 return;
1428
1429 m_state = IndexedDB::TransactionState::Active;
1430}
1431
1432void IDBTransaction::deactivate()
1433{
1434 ASSERT(&m_database->originThread() == &Thread::current());
1435
1436 if (m_state == IndexedDB::TransactionState::Active)
1437 m_state = IndexedDB::TransactionState::Inactive;
1438
1439 schedulePendingOperationTimer();
1440}
1441
1442void IDBTransaction::connectionClosedFromServer(const IDBError& error)
1443{
1444 LOG(IndexedDB, "IDBTransaction::connectionClosedFromServer - %s", error.message().utf8().data());
1445
1446 m_database->willAbortTransaction(*this);
1447 m_state = IndexedDB::TransactionState::Aborting;
1448
1449 abortInProgressOperations(error);
1450
1451 auto operations = copyToVector(m_transactionOperationMap.values());
1452 for (auto& operation : operations) {
1453 m_currentlyCompletingRequest = nullptr;
1454 m_transactionOperationsInProgressQueue.append(operation.get());
1455 ASSERT(m_transactionOperationsInProgressQueue.first() == operation.get());
1456 operation->doComplete(IDBResultData::error(operation->identifier(), error));
1457 }
1458 m_currentlyCompletingRequest = nullptr;
1459
1460 connectionProxy().forgetActiveOperations(operations);
1461 connectionProxy().forgetTransaction(*this);
1462
1463 m_pendingTransactionOperationQueue.clear();
1464 m_abortQueue.clear();
1465 m_transactionOperationMap.clear();
1466
1467 m_idbError = error;
1468 m_domError = error.toDOMException();
1469 m_database->didAbortTransaction(*this);
1470 fireOnAbort();
1471}
1472
1473void IDBTransaction::visitReferencedObjectStores(JSC::SlotVisitor& visitor) const
1474{
1475 Locker<Lock> locker(m_referencedObjectStoreLock);
1476 for (auto& objectStore : m_referencedObjectStores.values())
1477 visitor.addOpaqueRoot(objectStore.get());
1478 for (auto& objectStore : m_deletedObjectStores.values())
1479 visitor.addOpaqueRoot(objectStore.get());
1480}
1481
1482} // namespace WebCore
1483
1484#endif // ENABLE(INDEXED_DATABASE)
1485