Bug 1842773 - Part 5: Add ArrayBuffer.prototype.{maxByteLength,resizable} getters...
[gecko.git] / dom / indexedDB / ActorsChild.cpp
blob2a5ad3a70140a4c6c36f14bf84d402473d360494
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include <type_traits>
9 #include "ActorsChild.h"
10 #include "BackgroundChildImpl.h"
11 #include "IDBDatabase.h"
12 #include "IDBEvents.h"
13 #include "IDBFactory.h"
14 #include "IDBIndex.h"
15 #include "IDBObjectStore.h"
16 #include "IDBRequest.h"
17 #include "IDBTransaction.h"
18 #include "IndexedDatabase.h"
19 #include "IndexedDatabaseInlines.h"
20 #include "IndexedDBCommon.h"
21 #include "js/Array.h" // JS::NewArrayObject, JS::SetArrayLength
22 #include "js/Date.h" // JS::NewDateObject, JS::TimeClip
23 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineProperty
24 #include <mozIRemoteLazyInputStream.h>
25 #include "mozilla/ArrayAlgorithm.h"
26 #include "mozilla/BasicEvents.h"
27 #include "mozilla/CycleCollectedJSRuntime.h"
28 #include "mozilla/Maybe.h"
29 #include "mozilla/ResultExtensions.h"
30 #include "mozilla/dom/BlobImpl.h"
31 #include "mozilla/dom/Element.h"
32 #include "mozilla/dom/Event.h"
33 #include "mozilla/dom/PermissionMessageUtils.h"
34 #include "mozilla/dom/BrowserChild.h"
35 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
36 #include "mozilla/dom/IPCBlobUtils.h"
37 #include "mozilla/dom/WorkerPrivate.h"
38 #include "mozilla/dom/WorkerRunnable.h"
39 #include "mozilla/dom/quota/ResultExtensions.h"
40 #include "mozilla/Encoding.h"
41 #include "mozilla/ipc/BackgroundUtils.h"
42 #include "mozilla/ProfilerLabels.h"
43 #include "mozilla/TaskQueue.h"
44 #include "nsCOMPtr.h"
45 #include "nsContentUtils.h"
46 #include "nsIAsyncInputStream.h"
47 #include "nsIEventTarget.h"
48 #include "nsIFileStreams.h"
49 #include "nsNetCID.h"
50 #include "nsPIDOMWindow.h"
51 #include "nsThreadUtils.h"
52 #include "nsTraceRefcnt.h"
53 #include "ProfilerHelpers.h"
54 #include "ReportInternalError.h"
55 #include "ThreadLocal.h"
57 #ifdef DEBUG
58 # include "IndexedDatabaseManager.h"
59 #endif
61 #define GC_ON_IPC_MESSAGES 0
63 #if defined(DEBUG) || GC_ON_IPC_MESSAGES
65 # include "js/GCAPI.h"
66 # include "nsJSEnvironment.h"
68 # define BUILD_GC_ON_IPC_MESSAGES
70 #endif // DEBUG || GC_ON_IPC_MESSAGES
72 namespace mozilla {
74 using ipc::PrincipalInfo;
76 namespace dom::indexedDB {
78 /*******************************************************************************
79 * ThreadLocal
80 ******************************************************************************/
82 ThreadLocal::ThreadLocal(const nsID& aBackgroundChildLoggingId)
83 : mLoggingInfo(aBackgroundChildLoggingId, 1, -1, 1),
84 mLoggingIdString(aBackgroundChildLoggingId) {
85 MOZ_COUNT_CTOR(mozilla::dom::indexedDB::ThreadLocal);
88 ThreadLocal::~ThreadLocal() {
89 MOZ_COUNT_DTOR(mozilla::dom::indexedDB::ThreadLocal);
92 /*******************************************************************************
93 * Helpers
94 ******************************************************************************/
96 namespace {
98 void MaybeCollectGarbageOnIPCMessage() {
99 #ifdef BUILD_GC_ON_IPC_MESSAGES
100 static const bool kCollectGarbageOnIPCMessages =
101 # if GC_ON_IPC_MESSAGES
102 true;
103 # else
104 false;
105 # endif // GC_ON_IPC_MESSAGES
107 if (!kCollectGarbageOnIPCMessages) {
108 return;
111 static bool haveWarnedAboutGC = false;
112 static bool haveWarnedAboutNonMainThread = false;
114 if (!haveWarnedAboutGC) {
115 haveWarnedAboutGC = true;
116 NS_WARNING("IndexedDB child actor GC debugging enabled!");
119 if (!NS_IsMainThread()) {
120 if (!haveWarnedAboutNonMainThread) {
121 haveWarnedAboutNonMainThread = true;
122 NS_WARNING("Don't know how to GC on a non-main thread yet.");
124 return;
127 nsJSContext::GarbageCollectNow(JS::GCReason::DOM_IPC);
128 nsJSContext::CycleCollectNow(CCReason::API);
129 #endif // BUILD_GC_ON_IPC_MESSAGES
132 class MOZ_STACK_CLASS AutoSetCurrentTransaction final {
133 using BackgroundChildImpl = mozilla::ipc::BackgroundChildImpl;
135 Maybe<IDBTransaction&> const mTransaction;
136 Maybe<IDBTransaction&> mPreviousTransaction;
137 ThreadLocal* mThreadLocal;
139 public:
140 AutoSetCurrentTransaction(const AutoSetCurrentTransaction&) = delete;
141 AutoSetCurrentTransaction(AutoSetCurrentTransaction&&) = delete;
142 AutoSetCurrentTransaction& operator=(const AutoSetCurrentTransaction&) =
143 delete;
144 AutoSetCurrentTransaction& operator=(AutoSetCurrentTransaction&&) = delete;
146 explicit AutoSetCurrentTransaction(Maybe<IDBTransaction&> aTransaction)
147 : mTransaction(aTransaction), mThreadLocal(nullptr) {
148 if (aTransaction) {
149 BackgroundChildImpl::ThreadLocal* threadLocal =
150 BackgroundChildImpl::GetThreadLocalForCurrentThread();
151 MOZ_ASSERT(threadLocal);
153 // Hang onto this for resetting later.
154 mThreadLocal = threadLocal->mIndexedDBThreadLocal.get();
155 MOZ_ASSERT(mThreadLocal);
157 // Save the current value.
158 mPreviousTransaction = mThreadLocal->MaybeCurrentTransactionRef();
160 // Set the new value.
161 mThreadLocal->SetCurrentTransaction(aTransaction);
165 ~AutoSetCurrentTransaction() {
166 MOZ_ASSERT_IF(mThreadLocal, mTransaction);
167 MOZ_ASSERT_IF(mThreadLocal,
168 ReferenceEquals(mThreadLocal->MaybeCurrentTransactionRef(),
169 mTransaction));
171 if (mThreadLocal) {
172 // Reset old value.
173 mThreadLocal->SetCurrentTransaction(mPreviousTransaction);
178 template <typename T>
179 void SetResultAndDispatchSuccessEvent(
180 const NotNull<RefPtr<IDBRequest>>& aRequest,
181 const SafeRefPtr<IDBTransaction>& aTransaction, T& aPtr,
182 RefPtr<Event> aEvent = nullptr);
184 namespace detail {
185 void DispatchSuccessEvent(const NotNull<RefPtr<IDBRequest>>& aRequest,
186 const SafeRefPtr<IDBTransaction>& aTransaction,
187 const RefPtr<Event>& aEvent);
189 template <class T>
190 std::enable_if_t<std::is_same_v<T, IDBDatabase> || std::is_same_v<T, IDBCursor>,
191 nsresult>
192 GetResult(JSContext* aCx, T* aDOMObject, JS::MutableHandle<JS::Value> aResult) {
193 if (!aDOMObject) {
194 aResult.setNull();
195 return NS_OK;
198 const bool ok = GetOrCreateDOMReflector(aCx, aDOMObject, aResult);
199 if (NS_WARN_IF(!ok)) {
200 IDB_REPORT_INTERNAL_ERR();
201 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
204 return NS_OK;
207 nsresult GetResult(JSContext* aCx, const JS::Handle<JS::Value>* aValue,
208 JS::MutableHandle<JS::Value> aResult) {
209 aResult.set(*aValue);
210 return NS_OK;
213 nsresult GetResult(JSContext* aCx, const uint64_t* aValue,
214 JS::MutableHandle<JS::Value> aResult) {
215 aResult.set(JS::NumberValue(*aValue));
216 return NS_OK;
219 nsresult GetResult(JSContext* aCx, StructuredCloneReadInfoChild&& aCloneInfo,
220 JS::MutableHandle<JS::Value> aResult) {
221 const bool ok =
222 IDBObjectStore::DeserializeValue(aCx, std::move(aCloneInfo), aResult);
224 if (NS_WARN_IF(!ok)) {
225 return NS_ERROR_DOM_DATA_CLONE_ERR;
228 return NS_OK;
231 nsresult GetResult(JSContext* aCx, StructuredCloneReadInfoChild* aCloneInfo,
232 JS::MutableHandle<JS::Value> aResult) {
233 return GetResult(aCx, std::move(*aCloneInfo), aResult);
236 nsresult GetResult(JSContext* aCx,
237 nsTArray<StructuredCloneReadInfoChild>* aCloneInfos,
238 JS::MutableHandle<JS::Value> aResult) {
239 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
240 if (NS_WARN_IF(!array)) {
241 IDB_REPORT_INTERNAL_ERR();
242 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
245 if (!aCloneInfos->IsEmpty()) {
246 const uint32_t count = aCloneInfos->Length();
248 if (NS_WARN_IF(!JS::SetArrayLength(aCx, array, count))) {
249 IDB_REPORT_INTERNAL_ERR();
250 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
253 for (uint32_t index = 0; index < count; index++) {
254 auto& cloneInfo = aCloneInfos->ElementAt(index);
256 JS::Rooted<JS::Value> value(aCx);
258 const nsresult rv = GetResult(aCx, std::move(cloneInfo), &value);
259 if (NS_WARN_IF(NS_FAILED(rv))) {
260 return rv;
263 if (NS_WARN_IF(
264 !JS_DefineElement(aCx, array, index, value, JSPROP_ENUMERATE))) {
265 IDB_REPORT_INTERNAL_ERR();
266 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
271 aResult.setObject(*array);
272 return NS_OK;
275 nsresult GetResult(JSContext* aCx, const Key* aKey,
276 JS::MutableHandle<JS::Value> aResult) {
277 const nsresult rv = aKey->ToJSVal(aCx, aResult);
278 if (NS_WARN_IF(NS_FAILED(rv))) {
279 return rv;
281 return NS_OK;
284 nsresult GetResult(JSContext* aCx, const nsTArray<Key>* aKeys,
285 JS::MutableHandle<JS::Value> aResult) {
286 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
287 if (NS_WARN_IF(!array)) {
288 IDB_REPORT_INTERNAL_ERR();
289 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
292 if (!aKeys->IsEmpty()) {
293 const uint32_t count = aKeys->Length();
295 if (NS_WARN_IF(!JS::SetArrayLength(aCx, array, count))) {
296 IDB_REPORT_INTERNAL_ERR();
297 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
300 for (uint32_t index = 0; index < count; index++) {
301 const Key& key = aKeys->ElementAt(index);
302 MOZ_ASSERT(!key.IsUnset());
304 JS::Rooted<JS::Value> value(aCx);
306 const nsresult rv = GetResult(aCx, &key, &value);
307 if (NS_WARN_IF(NS_FAILED(rv))) {
308 return rv;
311 if (NS_WARN_IF(
312 !JS_DefineElement(aCx, array, index, value, JSPROP_ENUMERATE))) {
313 IDB_REPORT_INTERNAL_ERR();
314 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
319 aResult.setObject(*array);
320 return NS_OK;
322 } // namespace detail
324 auto DeserializeStructuredCloneFiles(
325 IDBDatabase* aDatabase,
326 const nsTArray<SerializedStructuredCloneFile>& aSerializedFiles,
327 bool aForPreprocess) {
328 MOZ_ASSERT_IF(aForPreprocess, aSerializedFiles.Length() == 1);
330 return TransformIntoNewArray(
331 aSerializedFiles,
332 [aForPreprocess, &database = *aDatabase](
333 const auto& serializedFile) -> StructuredCloneFileChild {
334 MOZ_ASSERT_IF(
335 aForPreprocess,
336 serializedFile.type() == StructuredCloneFileBase::eStructuredClone);
338 const NullableBlob& blob = serializedFile.file();
340 switch (serializedFile.type()) {
341 case StructuredCloneFileBase::eBlob: {
342 MOZ_ASSERT(blob.type() == NullableBlob::TIPCBlob);
344 const IPCBlob& ipcBlob = blob.get_IPCBlob();
346 const RefPtr<BlobImpl> blobImpl =
347 IPCBlobUtils::Deserialize(ipcBlob);
348 MOZ_ASSERT(blobImpl);
350 RefPtr<Blob> blob =
351 Blob::Create(database.GetOwnerGlobal(), blobImpl);
352 MOZ_ASSERT(blob);
354 return {StructuredCloneFileBase::eBlob, std::move(blob)};
357 case StructuredCloneFileBase::eStructuredClone: {
358 if (aForPreprocess) {
359 MOZ_ASSERT(blob.type() == NullableBlob::TIPCBlob);
361 const IPCBlob& ipcBlob = blob.get_IPCBlob();
363 const RefPtr<BlobImpl> blobImpl =
364 IPCBlobUtils::Deserialize(ipcBlob);
365 MOZ_ASSERT(blobImpl);
367 RefPtr<Blob> blob =
368 Blob::Create(database.GetOwnerGlobal(), blobImpl);
369 MOZ_ASSERT(blob);
371 return {StructuredCloneFileBase::eStructuredClone,
372 std::move(blob)};
374 MOZ_ASSERT(blob.type() == NullableBlob::Tnull_t);
376 return StructuredCloneFileChild{
377 StructuredCloneFileBase::eStructuredClone};
380 case StructuredCloneFileBase::eMutableFile:
381 case StructuredCloneFileBase::eWasmBytecode:
382 case StructuredCloneFileBase::eWasmCompiled: {
383 MOZ_ASSERT(blob.type() == NullableBlob::Tnull_t);
385 return StructuredCloneFileChild{serializedFile.type()};
387 // Don't set mBlob, support for storing WebAssembly.Modules has been
388 // removed in bug 1469395. Support for de-serialization of
389 // WebAssembly.Modules has been removed in bug 1561876. Support for
390 // MutableFile has been removed in bug 1500343. Full removal is
391 // tracked in bug 1487479.
394 default:
395 MOZ_CRASH("Should never get here!");
400 JSStructuredCloneData PreprocessingNotSupported() {
401 MOZ_CRASH("Preprocessing not (yet) supported!");
404 template <typename PreprocessInfoAccessor>
405 StructuredCloneReadInfoChild DeserializeStructuredCloneReadInfo(
406 SerializedStructuredCloneReadInfo&& aSerialized,
407 IDBDatabase* const aDatabase,
408 PreprocessInfoAccessor preprocessInfoAccessor) {
409 // XXX Make this a class invariant of SerializedStructuredCloneReadInfo.
410 MOZ_ASSERT_IF(aSerialized.hasPreprocessInfo(),
411 0 == aSerialized.data().data.Size());
412 return {aSerialized.hasPreprocessInfo() ? preprocessInfoAccessor()
413 : std::move(aSerialized.data().data),
414 DeserializeStructuredCloneFiles(aDatabase, aSerialized.files(),
415 /* aForPreprocess */ false),
416 aDatabase};
419 // TODO: Remove duplication between DispatchErrorEvent and DispatchSucessEvent.
421 void DispatchErrorEvent(
422 MovingNotNull<RefPtr<IDBRequest>> aRequest, nsresult aErrorCode,
423 const SafeRefPtr<IDBTransaction>& aTransaction = nullptr,
424 RefPtr<Event> aEvent = nullptr) {
425 const RefPtr<IDBRequest> request = std::move(aRequest);
427 request->AssertIsOnOwningThread();
428 MOZ_ASSERT(NS_FAILED(aErrorCode));
429 MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB);
431 AUTO_PROFILER_LABEL("IndexedDB:DispatchErrorEvent", DOM);
433 request->SetError(aErrorCode);
435 if (!aEvent) {
436 // Make an error event and fire it at the target.
437 aEvent = CreateGenericEvent(request, nsDependentString(kErrorEventType),
438 eDoesBubble, eCancelable);
440 MOZ_ASSERT(aEvent);
442 // XXX This is redundant if we are called from
443 // DispatchSuccessEvent.
444 Maybe<AutoSetCurrentTransaction> asct;
445 if (aTransaction) {
446 asct.emplace(SomeRef(*aTransaction));
449 if (aTransaction && aTransaction->IsInactive()) {
450 aTransaction->TransitionToActive();
453 if (aTransaction) {
454 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
455 "Firing %s event with error 0x%x", "%s (0x%" PRIx32 ")",
456 aTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(),
457 IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
458 static_cast<uint32_t>(aErrorCode));
459 } else {
460 IDB_LOG_MARK_CHILD_REQUEST("Firing %s event with error 0x%x",
461 "%s (0x%" PRIx32 ")",
462 request->LoggingSerialNumber(),
463 IDB_LOG_STRINGIFY(aEvent, kErrorEventType),
464 static_cast<uint32_t>(aErrorCode));
467 IgnoredErrorResult rv;
468 const bool doDefault =
469 request->DispatchEvent(*aEvent, CallerType::System, rv);
470 if (NS_WARN_IF(rv.Failed())) {
471 return;
474 MOZ_ASSERT(!aTransaction || aTransaction->IsActive() ||
475 aTransaction->IsAborted() ||
476 aTransaction->WasExplicitlyCommitted());
478 if (aTransaction && aTransaction->IsActive()) {
479 aTransaction->TransitionToInactive();
481 // Do not abort the transaction here if this request is failed due to the
482 // abortion of its transaction to ensure that the correct error cause of
483 // the abort event be set in IDBTransaction::FireCompleteOrAbortEvents()
484 // later.
485 if (aErrorCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) {
486 WidgetEvent* const internalEvent = aEvent->WidgetEventPtr();
487 MOZ_ASSERT(internalEvent);
489 if (internalEvent->mFlags.mExceptionWasRaised) {
490 aTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
491 } else if (doDefault) {
492 aTransaction->Abort(request);
498 template <typename T>
499 void SetResultAndDispatchSuccessEvent(
500 const NotNull<RefPtr<IDBRequest>>& aRequest,
501 const SafeRefPtr<IDBTransaction>& aTransaction, T& aPtr,
502 RefPtr<Event> aEvent) {
503 const auto autoTransaction =
504 AutoSetCurrentTransaction{aTransaction.maybeDeref()};
506 AUTO_PROFILER_LABEL("IndexedDB:SetResultAndDispatchSuccessEvent", DOM);
508 aRequest->AssertIsOnOwningThread();
510 if (aTransaction && aTransaction->IsAborted()) {
511 DispatchErrorEvent(aRequest, aTransaction->AbortCode(), aTransaction);
512 return;
515 if (!aEvent) {
516 aEvent =
517 CreateGenericEvent(aRequest.get(), nsDependentString(kSuccessEventType),
518 eDoesNotBubble, eNotCancelable);
520 MOZ_ASSERT(aEvent);
522 aRequest->SetResult(
523 [&aPtr](JSContext* aCx, JS::MutableHandle<JS::Value> aResult) {
524 MOZ_ASSERT(aCx);
525 return detail::GetResult(aCx, &aPtr, aResult);
528 detail::DispatchSuccessEvent(aRequest, aTransaction, aEvent);
531 namespace detail {
532 void DispatchSuccessEvent(const NotNull<RefPtr<IDBRequest>>& aRequest,
533 const SafeRefPtr<IDBTransaction>& aTransaction,
534 const RefPtr<Event>& aEvent) {
535 if (aTransaction && aTransaction->IsInactive()) {
536 aTransaction->TransitionToActive();
539 if (aTransaction) {
540 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
541 "Firing %s event", "%s", aTransaction->LoggingSerialNumber(),
542 aRequest->LoggingSerialNumber(),
543 IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
544 } else {
545 IDB_LOG_MARK_CHILD_REQUEST("Firing %s event", "%s",
546 aRequest->LoggingSerialNumber(),
547 IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
550 MOZ_ASSERT_IF(aTransaction && !aTransaction->WasExplicitlyCommitted(),
551 aTransaction->IsActive() && !aTransaction->IsAborted());
553 IgnoredErrorResult rv;
554 aRequest->DispatchEvent(*aEvent, rv);
555 if (NS_WARN_IF(rv.Failed())) {
556 return;
559 WidgetEvent* const internalEvent = aEvent->WidgetEventPtr();
560 MOZ_ASSERT(internalEvent);
562 if (aTransaction && aTransaction->IsActive()) {
563 aTransaction->TransitionToInactive();
565 if (internalEvent->mFlags.mExceptionWasRaised) {
566 aTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
567 } else {
568 // To handle upgrade transaction.
569 aTransaction->CommitIfNotStarted();
573 } // namespace detail
575 PRFileDesc* GetFileDescriptorFromStream(nsIInputStream* aStream) {
576 MOZ_ASSERT(aStream);
578 const nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
579 if (NS_WARN_IF(!fileMetadata)) {
580 return nullptr;
583 PRFileDesc* fileDesc;
584 const nsresult rv = fileMetadata->GetFileDescriptor(&fileDesc);
585 if (NS_WARN_IF(NS_FAILED(rv))) {
586 return nullptr;
589 MOZ_ASSERT(fileDesc);
591 return fileDesc;
594 auto GetKeyOperator(const IDBCursorDirection aDirection) {
595 switch (aDirection) {
596 case IDBCursorDirection::Next:
597 case IDBCursorDirection::Nextunique:
598 return &Key::operator>=;
599 case IDBCursorDirection::Prev:
600 case IDBCursorDirection::Prevunique:
601 return &Key::operator<=;
602 default:
603 MOZ_CRASH("Should never get here.");
607 // Does not need to be threadsafe since this only runs on one thread, but
608 // inheriting from CancelableRunnable is easy.
609 template <typename T>
610 class DelayedActionRunnable final : public CancelableRunnable {
611 using ActionFunc = void (T::*)();
613 SafeRefPtr<T> mActor;
614 RefPtr<IDBRequest> mRequest;
615 ActionFunc mActionFunc;
617 public:
618 explicit DelayedActionRunnable(SafeRefPtr<T> aActor, ActionFunc aActionFunc)
619 : CancelableRunnable("indexedDB::DelayedActionRunnable"),
620 mActor(std::move(aActor)),
621 mRequest(mActor->GetRequest()),
622 mActionFunc(aActionFunc) {
623 MOZ_ASSERT(mActor);
624 mActor->AssertIsOnOwningThread();
625 MOZ_ASSERT(mRequest);
626 MOZ_ASSERT(mActionFunc);
629 private:
630 ~DelayedActionRunnable() = default;
632 NS_DECL_NSIRUNNABLE
633 nsresult Cancel() override;
636 } // namespace
638 /*******************************************************************************
639 * Actor class declarations
640 ******************************************************************************/
642 // DiscardableRunnable is used to make workers happy.
643 class BackgroundRequestChild::PreprocessHelper final
644 : public DiscardableRunnable,
645 public nsIInputStreamCallback,
646 public nsIFileMetadataCallback {
647 enum class State {
648 // Just created on the owning thread, dispatched to the thread pool. Next
649 // step is either Finishing if stream was ready to be read or
650 // WaitingForStreamReady if the stream is not ready.
651 Initial,
653 // Waiting for stream to be ready on a thread pool thread. Next state is
654 // Finishing.
655 WaitingForStreamReady,
657 // Waiting to finish/finishing on the owning thread. Next step is Completed.
658 Finishing,
660 // All done.
661 Completed
664 const nsCOMPtr<nsIEventTarget> mOwningEventTarget;
665 RefPtr<TaskQueue> mTaskQueue;
666 nsCOMPtr<nsIInputStream> mStream;
667 UniquePtr<JSStructuredCloneData> mCloneData;
668 BackgroundRequestChild* mActor;
669 const uint32_t mCloneDataIndex;
670 nsresult mResultCode;
671 State mState;
673 public:
674 PreprocessHelper(uint32_t aCloneDataIndex, BackgroundRequestChild* aActor)
675 : DiscardableRunnable(
676 "indexedDB::BackgroundRequestChild::PreprocessHelper"),
677 mOwningEventTarget(aActor->GetActorEventTarget()),
678 mActor(aActor),
679 mCloneDataIndex(aCloneDataIndex),
680 mResultCode(NS_OK),
681 mState(State::Initial) {
682 AssertIsOnOwningThread();
683 MOZ_ASSERT(aActor);
684 aActor->AssertIsOnOwningThread();
687 bool IsOnOwningThread() const {
688 MOZ_ASSERT(mOwningEventTarget);
690 bool current;
691 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
692 current;
695 void AssertIsOnOwningThread() const { MOZ_ASSERT(IsOnOwningThread()); }
697 void ClearActor() {
698 AssertIsOnOwningThread();
700 mActor = nullptr;
703 nsresult Init(const StructuredCloneFileChild& aFile);
705 nsresult Dispatch();
707 private:
708 ~PreprocessHelper() {
709 MOZ_ASSERT(mState == State::Initial || mState == State::Completed);
711 if (mTaskQueue) {
712 mTaskQueue->BeginShutdown();
716 nsresult Start();
718 nsresult ProcessStream();
720 void Finish();
722 NS_DECL_ISUPPORTS_INHERITED
723 NS_DECL_NSIRUNNABLE
724 NS_DECL_NSIINPUTSTREAMCALLBACK
725 NS_DECL_NSIFILEMETADATACALLBACK
728 /*******************************************************************************
729 * BackgroundRequestChildBase
730 ******************************************************************************/
732 BackgroundRequestChildBase::BackgroundRequestChildBase(
733 MovingNotNull<RefPtr<IDBRequest>> aRequest)
734 : mRequest(std::move(aRequest)) {
735 mRequest->AssertIsOnOwningThread();
737 MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase);
740 BackgroundRequestChildBase::~BackgroundRequestChildBase() {
741 AssertIsOnOwningThread();
743 MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase);
746 #ifdef DEBUG
748 void BackgroundRequestChildBase::AssertIsOnOwningThread() const {
749 mRequest->AssertIsOnOwningThread();
752 #endif // DEBUG
754 /*******************************************************************************
755 * BackgroundFactoryChild
756 ******************************************************************************/
758 BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory& aFactory)
759 : mFactory(&aFactory) {
760 AssertIsOnOwningThread();
761 mFactory->AssertIsOnOwningThread();
763 MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild);
766 BackgroundFactoryChild::~BackgroundFactoryChild() {
767 MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild);
770 void BackgroundFactoryChild::SendDeleteMeInternal() {
771 AssertIsOnOwningThread();
773 if (mFactory) {
774 mFactory->ClearBackgroundActor();
775 mFactory = nullptr;
777 MOZ_ALWAYS_TRUE(PBackgroundIDBFactoryChild::SendDeleteMe());
781 void BackgroundFactoryChild::ActorDestroy(ActorDestroyReason aWhy) {
782 AssertIsOnOwningThread();
784 MaybeCollectGarbageOnIPCMessage();
786 if (mFactory) {
787 mFactory->ClearBackgroundActor();
788 #ifdef DEBUG
789 mFactory = nullptr;
790 #endif
794 PBackgroundIDBFactoryRequestChild*
795 BackgroundFactoryChild::AllocPBackgroundIDBFactoryRequestChild(
796 const FactoryRequestParams& aParams) {
797 MOZ_CRASH(
798 "PBackgroundIDBFactoryRequestChild actors should be manually "
799 "constructed!");
802 bool BackgroundFactoryChild::DeallocPBackgroundIDBFactoryRequestChild(
803 PBackgroundIDBFactoryRequestChild* aActor) {
804 MOZ_ASSERT(aActor);
806 delete static_cast<BackgroundFactoryRequestChild*>(aActor);
807 return true;
810 already_AddRefed<PBackgroundIDBDatabaseChild>
811 BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild(
812 const DatabaseSpec& aSpec,
813 PBackgroundIDBFactoryRequestChild* aRequest) const {
814 AssertIsOnOwningThread();
816 auto* const request = static_cast<BackgroundFactoryRequestChild*>(aRequest);
817 MOZ_ASSERT(request);
819 RefPtr<BackgroundDatabaseChild> actor =
820 new BackgroundDatabaseChild(aSpec, request);
821 return actor.forget();
824 mozilla::ipc::IPCResult
825 BackgroundFactoryChild::RecvPBackgroundIDBDatabaseConstructor(
826 PBackgroundIDBDatabaseChild* aActor, const DatabaseSpec& aSpec,
827 NotNull<PBackgroundIDBFactoryRequestChild*> aRequest) {
828 AssertIsOnOwningThread();
829 MOZ_ASSERT(aActor);
831 return IPC_OK();
834 /*******************************************************************************
835 * BackgroundFactoryRequestChild
836 ******************************************************************************/
838 BackgroundFactoryRequestChild::BackgroundFactoryRequestChild(
839 SafeRefPtr<IDBFactory> aFactory,
840 MovingNotNull<RefPtr<IDBOpenDBRequest>> aOpenRequest, bool aIsDeleteOp,
841 uint64_t aRequestedVersion)
842 : BackgroundRequestChildBase(std::move(aOpenRequest)),
843 mFactory(std::move(aFactory)),
844 mDatabaseActor(nullptr),
845 mRequestedVersion(aRequestedVersion),
846 mIsDeleteOp(aIsDeleteOp) {
847 // Can't assert owning thread here because IPDL has not yet set our manager!
848 MOZ_ASSERT(mFactory);
849 mFactory->AssertIsOnOwningThread();
851 MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild);
854 BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild() {
855 MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild);
858 NotNull<IDBOpenDBRequest*> BackgroundFactoryRequestChild::GetOpenDBRequest()
859 const {
860 AssertIsOnOwningThread();
862 // XXX NotNull might provide something to encapsulate this
863 return WrapNotNullUnchecked(
864 static_cast<IDBOpenDBRequest*>(mRequest.get().get()));
867 void BackgroundFactoryRequestChild::SetDatabaseActor(
868 BackgroundDatabaseChild* aActor) {
869 AssertIsOnOwningThread();
870 MOZ_ASSERT(!aActor || !mDatabaseActor);
872 mDatabaseActor = aActor;
875 void BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) {
876 AssertIsOnOwningThread();
877 MOZ_ASSERT(NS_FAILED(aResponse));
878 MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
880 mRequest->Reset();
882 DispatchErrorEvent(mRequest, aResponse);
884 if (mDatabaseActor) {
885 mDatabaseActor->ReleaseDOMObject();
886 MOZ_ASSERT(!mDatabaseActor);
890 void BackgroundFactoryRequestChild::HandleResponse(
891 const OpenDatabaseRequestResponse& aResponse) {
892 AssertIsOnOwningThread();
894 mRequest->Reset();
896 auto* databaseActor = static_cast<BackgroundDatabaseChild*>(
897 aResponse.database().AsChild().get());
898 MOZ_ASSERT(databaseActor);
900 IDBDatabase* const database = [this, databaseActor]() -> IDBDatabase* {
901 IDBDatabase* database = databaseActor->GetDOMObject();
902 if (!database) {
903 Unused << this;
905 if (NS_WARN_IF(!databaseActor->EnsureDOMObject())) {
906 return nullptr;
908 MOZ_ASSERT(mDatabaseActor);
910 database = databaseActor->GetDOMObject();
911 MOZ_ASSERT(database);
913 MOZ_ASSERT(!database->IsClosed());
916 return database;
917 }();
919 if (!database || database->IsClosed()) {
920 // If the database was closed already, which is only possible if we fired an
921 // "upgradeneeded" event, then we shouldn't fire a "success" event here.
922 // Instead we fire an error event with AbortErr.
923 DispatchErrorEvent(mRequest, NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
924 } else {
925 SetResultAndDispatchSuccessEvent(mRequest, nullptr, *database);
928 if (database) {
929 MOZ_ASSERT(mDatabaseActor == databaseActor);
931 databaseActor->ReleaseDOMObject();
932 } else {
933 databaseActor->SendDeleteMeInternal();
935 MOZ_ASSERT(!mDatabaseActor);
938 void BackgroundFactoryRequestChild::HandleResponse(
939 const DeleteDatabaseRequestResponse& aResponse) {
940 AssertIsOnOwningThread();
942 RefPtr<Event> successEvent = IDBVersionChangeEvent::Create(
943 mRequest.get(), nsDependentString(kSuccessEventType),
944 aResponse.previousVersion());
945 MOZ_ASSERT(successEvent);
947 SetResultAndDispatchSuccessEvent(mRequest, nullptr, JS::UndefinedHandleValue,
948 std::move(successEvent));
950 MOZ_ASSERT(!mDatabaseActor);
953 void BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
954 AssertIsOnOwningThread();
956 MaybeCollectGarbageOnIPCMessage();
958 if (aWhy != Deletion) {
959 GetOpenDBRequest()->NoteComplete();
963 mozilla::ipc::IPCResult BackgroundFactoryRequestChild::Recv__delete__(
964 const FactoryRequestResponse& aResponse) {
965 AssertIsOnOwningThread();
967 MaybeCollectGarbageOnIPCMessage();
969 switch (aResponse.type()) {
970 case FactoryRequestResponse::Tnsresult:
971 HandleResponse(aResponse.get_nsresult());
972 break;
974 case FactoryRequestResponse::TOpenDatabaseRequestResponse:
975 HandleResponse(aResponse.get_OpenDatabaseRequestResponse());
976 break;
978 case FactoryRequestResponse::TDeleteDatabaseRequestResponse:
979 HandleResponse(aResponse.get_DeleteDatabaseRequestResponse());
980 break;
982 default:
983 return IPC_FAIL(this, "Unknown response type!");
986 auto request = GetOpenDBRequest();
987 request->NoteComplete();
989 return IPC_OK();
992 mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvBlocked(
993 const uint64_t aCurrentVersion) {
994 AssertIsOnOwningThread();
996 MaybeCollectGarbageOnIPCMessage();
998 const nsDependentString type(kBlockedEventType);
1000 RefPtr<Event> blockedEvent;
1001 if (mIsDeleteOp) {
1002 blockedEvent =
1003 IDBVersionChangeEvent::Create(mRequest.get(), type, aCurrentVersion);
1004 MOZ_ASSERT(blockedEvent);
1005 } else {
1006 blockedEvent = IDBVersionChangeEvent::Create(
1007 mRequest.get(), type, aCurrentVersion, mRequestedVersion);
1008 MOZ_ASSERT(blockedEvent);
1011 RefPtr<IDBRequest> kungFuDeathGrip = mRequest;
1013 IDB_LOG_MARK_CHILD_REQUEST("Firing \"blocked\" event", "\"blocked\"",
1014 kungFuDeathGrip->LoggingSerialNumber());
1016 IgnoredErrorResult rv;
1017 kungFuDeathGrip->DispatchEvent(*blockedEvent, rv);
1018 if (rv.Failed()) {
1019 NS_WARNING("Failed to dispatch event!");
1022 return IPC_OK();
1025 /*******************************************************************************
1026 * BackgroundDatabaseChild
1027 ******************************************************************************/
1029 BackgroundDatabaseChild::BackgroundDatabaseChild(
1030 const DatabaseSpec& aSpec, BackgroundFactoryRequestChild* aOpenRequestActor)
1031 : mSpec(MakeUnique<DatabaseSpec>(aSpec)),
1032 mOpenRequestActor(aOpenRequestActor),
1033 mDatabase(nullptr) {
1034 // Can't assert owning thread here because IPDL has not yet set our manager!
1035 MOZ_ASSERT(aOpenRequestActor);
1037 MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseChild);
1040 BackgroundDatabaseChild::~BackgroundDatabaseChild() {
1041 MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseChild);
1044 #ifdef DEBUG
1046 void BackgroundDatabaseChild::AssertIsOnOwningThread() const {
1047 static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread();
1050 #endif // DEBUG
1052 void BackgroundDatabaseChild::SendDeleteMeInternal() {
1053 AssertIsOnOwningThread();
1054 MOZ_ASSERT(!mTemporaryStrongDatabase);
1055 MOZ_ASSERT(!mOpenRequestActor);
1057 if (mDatabase) {
1058 mDatabase->ClearBackgroundActor();
1059 mDatabase = nullptr;
1061 MOZ_ALWAYS_TRUE(PBackgroundIDBDatabaseChild::SendDeleteMe());
1065 bool BackgroundDatabaseChild::EnsureDOMObject() {
1066 AssertIsOnOwningThread();
1067 MOZ_ASSERT(mOpenRequestActor);
1069 if (mTemporaryStrongDatabase) {
1070 MOZ_ASSERT(!mSpec);
1071 MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase);
1072 return true;
1075 MOZ_ASSERT(mSpec);
1077 const auto request = mOpenRequestActor->GetOpenDBRequest();
1079 auto& factory =
1080 static_cast<BackgroundFactoryChild*>(Manager())->GetDOMObject();
1082 if (!factory.GetParentObject()) {
1083 // Already disconnected from global.
1085 // We need to clear mOpenRequestActor here, since that would otherwise be
1086 // done by ReleaseDOMObject, which cannot be called if EnsureDOMObject
1087 // failed.
1088 mOpenRequestActor = nullptr;
1090 return false;
1093 // TODO: This AcquireStrongRefFromRawPtr looks suspicious. This should be
1094 // changed or at least well explained, see also comment on
1095 // BackgroundFactoryChild.
1096 mTemporaryStrongDatabase = IDBDatabase::Create(
1097 request, SafeRefPtr{&factory, AcquireStrongRefFromRawPtr{}}, this,
1098 std::move(mSpec));
1100 MOZ_ASSERT(mTemporaryStrongDatabase);
1101 mTemporaryStrongDatabase->AssertIsOnOwningThread();
1103 mDatabase = mTemporaryStrongDatabase;
1105 mOpenRequestActor->SetDatabaseActor(this);
1107 return true;
1110 void BackgroundDatabaseChild::ReleaseDOMObject() {
1111 AssertIsOnOwningThread();
1112 MOZ_ASSERT(mTemporaryStrongDatabase);
1113 mTemporaryStrongDatabase->AssertIsOnOwningThread();
1114 MOZ_ASSERT(mOpenRequestActor);
1115 MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase);
1117 mOpenRequestActor->SetDatabaseActor(nullptr);
1119 mOpenRequestActor = nullptr;
1121 // This may be the final reference to the IDBDatabase object so we may end up
1122 // calling SendDeleteMeInternal() here. Make sure everything is cleaned up
1123 // properly before proceeding.
1124 mTemporaryStrongDatabase = nullptr;
1126 // XXX Why isn't mDatabase set to nullptr here?
1129 void BackgroundDatabaseChild::ActorDestroy(ActorDestroyReason aWhy) {
1130 AssertIsOnOwningThread();
1132 MaybeCollectGarbageOnIPCMessage();
1134 if (mDatabase) {
1135 mDatabase->ClearBackgroundActor();
1136 #ifdef DEBUG
1137 mDatabase = nullptr;
1138 #endif
1142 PBackgroundIDBDatabaseFileChild*
1143 BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseFileChild(
1144 const IPCBlob& aIPCBlob) {
1145 MOZ_CRASH("PBackgroundIDBFileChild actors should be manually constructed!");
1148 bool BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild(
1149 PBackgroundIDBDatabaseFileChild* aActor) const {
1150 AssertIsOnOwningThread();
1151 MOZ_ASSERT(aActor);
1153 delete aActor;
1154 return true;
1157 already_AddRefed<PBackgroundIDBVersionChangeTransactionChild>
1158 BackgroundDatabaseChild::AllocPBackgroundIDBVersionChangeTransactionChild(
1159 const uint64_t aCurrentVersion, const uint64_t aRequestedVersion,
1160 const int64_t aNextObjectStoreId, const int64_t aNextIndexId) {
1161 AssertIsOnOwningThread();
1163 return RefPtr{new BackgroundVersionChangeTransactionChild(
1164 mOpenRequestActor->GetOpenDBRequest())}
1165 .forget();
1168 mozilla::ipc::IPCResult
1169 BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor(
1170 PBackgroundIDBVersionChangeTransactionChild* aActor,
1171 const uint64_t& aCurrentVersion, const uint64_t& aRequestedVersion,
1172 const int64_t& aNextObjectStoreId, const int64_t& aNextIndexId) {
1173 AssertIsOnOwningThread();
1174 MOZ_ASSERT(aActor);
1175 MOZ_ASSERT(mOpenRequestActor);
1177 MaybeCollectGarbageOnIPCMessage();
1179 auto* const actor =
1180 static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
1182 if (!EnsureDOMObject()) {
1183 NS_WARNING("Factory is already disconnected from global");
1185 actor->SendDeleteMeInternal(true);
1187 // XXX This is a hack to ensure that transaction/request serial numbers stay
1188 // in sync between parent and child. Actually, it might be better to create
1189 // an IDBTransaction in the child and abort that.
1190 Unused
1191 << mozilla::ipc::BackgroundChildImpl::GetThreadLocalForCurrentThread()
1192 ->mIndexedDBThreadLocal->NextTransactionSN(
1193 IDBTransaction::Mode::VersionChange);
1194 Unused << IDBRequest::NextSerialNumber();
1196 // No reason to IPC_FAIL here.
1197 return IPC_OK();
1200 MOZ_ASSERT(!mDatabase->IsInvalidated());
1202 // XXX NotNull might encapsulate this
1203 const auto request =
1204 WrapNotNullUnchecked(RefPtr{mOpenRequestActor->GetOpenDBRequest().get()});
1206 SafeRefPtr<IDBTransaction> transaction = IDBTransaction::CreateVersionChange(
1207 mDatabase, actor, request, aNextObjectStoreId, aNextIndexId);
1208 MOZ_ASSERT(transaction);
1210 transaction->AssertIsOnOwningThread();
1212 actor->SetDOMTransaction(transaction.clonePtr());
1214 const auto database = WrapNotNull(mDatabase);
1216 database->EnterSetVersionTransaction(aRequestedVersion);
1218 request->SetTransaction(transaction.clonePtr());
1220 RefPtr<Event> upgradeNeededEvent = IDBVersionChangeEvent::Create(
1221 request.get(), nsDependentString(kUpgradeNeededEventType),
1222 aCurrentVersion, aRequestedVersion);
1223 MOZ_ASSERT(upgradeNeededEvent);
1225 SetResultAndDispatchSuccessEvent(
1226 WrapNotNullUnchecked<RefPtr<IDBRequest>>(request.get()), transaction,
1227 *database, std::move(upgradeNeededEvent));
1229 return IPC_OK();
1232 mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvVersionChange(
1233 const uint64_t aOldVersion, const Maybe<uint64_t> aNewVersion) {
1234 AssertIsOnOwningThread();
1236 MaybeCollectGarbageOnIPCMessage();
1238 if (!mDatabase || mDatabase->IsClosed()) {
1239 return IPC_OK();
1242 RefPtr<IDBDatabase> kungFuDeathGrip = mDatabase;
1244 // Handle bfcache'd windows.
1245 if (nsPIDOMWindowInner* owner = kungFuDeathGrip->GetOwner()) {
1246 // The database must be closed if the window is already frozen.
1247 bool shouldAbortAndClose = owner->IsFrozen();
1249 // Anything in the bfcache has to be evicted and then we have to close the
1250 // database also.
1251 if (owner->RemoveFromBFCacheSync()) {
1252 shouldAbortAndClose = true;
1255 if (shouldAbortAndClose) {
1256 // Invalidate() doesn't close the database in the parent, so we have
1257 // to call Close() and AbortTransactions() manually.
1258 kungFuDeathGrip->AbortTransactions(/* aShouldWarn */ false);
1259 kungFuDeathGrip->Close();
1260 return IPC_OK();
1264 // Otherwise fire a versionchange event.
1265 const nsDependentString type(kVersionChangeEventType);
1267 RefPtr<Event> versionChangeEvent;
1269 if (aNewVersion.isNothing()) {
1270 versionChangeEvent =
1271 IDBVersionChangeEvent::Create(kungFuDeathGrip, type, aOldVersion);
1272 MOZ_ASSERT(versionChangeEvent);
1273 } else {
1274 versionChangeEvent = IDBVersionChangeEvent::Create(
1275 kungFuDeathGrip, type, aOldVersion, aNewVersion.value());
1276 MOZ_ASSERT(versionChangeEvent);
1279 IDB_LOG_MARK("Child : Firing \"versionchange\" event",
1280 "C: IDBDatabase \"versionchange\" event", IDB_LOG_ID_STRING());
1282 IgnoredErrorResult rv;
1283 kungFuDeathGrip->DispatchEvent(*versionChangeEvent, rv);
1284 if (rv.Failed()) {
1285 NS_WARNING("Failed to dispatch event!");
1288 if (!kungFuDeathGrip->IsClosed()) {
1289 SendBlocked();
1292 return IPC_OK();
1295 mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvInvalidate() {
1296 AssertIsOnOwningThread();
1298 MaybeCollectGarbageOnIPCMessage();
1300 if (mDatabase) {
1301 mDatabase->Invalidate();
1304 return IPC_OK();
1307 mozilla::ipc::IPCResult
1308 BackgroundDatabaseChild::RecvCloseAfterInvalidationComplete() {
1309 AssertIsOnOwningThread();
1311 MaybeCollectGarbageOnIPCMessage();
1313 if (mDatabase) {
1314 mDatabase->DispatchTrustedEvent(nsDependentString(kCloseEventType));
1317 return IPC_OK();
1320 /*******************************************************************************
1321 * BackgroundTransactionBase
1322 ******************************************************************************/
1324 BackgroundTransactionBase::BackgroundTransactionBase(
1325 SafeRefPtr<IDBTransaction> aTransaction)
1326 : mTemporaryStrongTransaction(std::move(aTransaction)),
1327 mTransaction(mTemporaryStrongTransaction.unsafeGetRawPtr()) {
1328 MOZ_ASSERT(mTransaction);
1329 mTransaction->AssertIsOnOwningThread();
1331 MOZ_COUNT_CTOR(BackgroundTransactionBase);
1334 #ifdef DEBUG
1336 void BackgroundTransactionBase::AssertIsOnOwningThread() const {
1337 MOZ_ASSERT(mTransaction);
1338 mTransaction->AssertIsOnOwningThread();
1341 #endif // DEBUG
1343 void BackgroundTransactionBase::NoteActorDestroyed() {
1344 AssertIsOnOwningThread();
1345 MOZ_ASSERT_IF(mTemporaryStrongTransaction, mTransaction);
1347 if (mTransaction) {
1348 mTransaction->ClearBackgroundActor();
1350 // Normally this would be DEBUG-only but NoteActorDestroyed is also called
1351 // from SendDeleteMeInternal. In that case we're going to receive an actual
1352 // ActorDestroy call later and we don't want to touch a dead object.
1353 mTemporaryStrongTransaction = nullptr;
1354 mTransaction = nullptr;
1358 void BackgroundTransactionBase::SetDOMTransaction(
1359 SafeRefPtr<IDBTransaction> aTransaction) {
1360 AssertIsOnOwningThread();
1361 MOZ_ASSERT(aTransaction);
1362 aTransaction->AssertIsOnOwningThread();
1363 MOZ_ASSERT(!mTemporaryStrongTransaction);
1364 MOZ_ASSERT(!mTransaction);
1366 mTemporaryStrongTransaction = std::move(aTransaction);
1367 mTransaction = mTemporaryStrongTransaction.unsafeGetRawPtr();
1370 void BackgroundTransactionBase::NoteComplete() {
1371 AssertIsOnOwningThread();
1372 MOZ_ASSERT_IF(mTransaction, mTemporaryStrongTransaction);
1374 mTemporaryStrongTransaction = nullptr;
1377 /*******************************************************************************
1378 * BackgroundTransactionChild
1379 ******************************************************************************/
1381 BackgroundTransactionChild::BackgroundTransactionChild(
1382 SafeRefPtr<IDBTransaction> aTransaction)
1383 : BackgroundTransactionBase(std::move(aTransaction)) {
1384 MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionChild);
1387 BackgroundTransactionChild::~BackgroundTransactionChild() {
1388 MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionChild);
1391 #ifdef DEBUG
1393 void BackgroundTransactionChild::AssertIsOnOwningThread() const {
1394 static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
1397 #endif // DEBUG
1399 void BackgroundTransactionChild::SendDeleteMeInternal() {
1400 AssertIsOnOwningThread();
1402 if (mTransaction) {
1403 NoteActorDestroyed();
1405 MOZ_ALWAYS_TRUE(PBackgroundIDBTransactionChild::SendDeleteMe());
1409 void BackgroundTransactionChild::ActorDestroy(ActorDestroyReason aWhy) {
1410 AssertIsOnOwningThread();
1412 MaybeCollectGarbageOnIPCMessage();
1414 NoteActorDestroyed();
1417 mozilla::ipc::IPCResult BackgroundTransactionChild::RecvComplete(
1418 const nsresult aResult) {
1419 AssertIsOnOwningThread();
1420 MOZ_ASSERT(mTransaction);
1422 MaybeCollectGarbageOnIPCMessage();
1424 mTransaction->FireCompleteOrAbortEvents(aResult);
1426 NoteComplete();
1427 return IPC_OK();
1430 PBackgroundIDBRequestChild*
1431 BackgroundTransactionChild::AllocPBackgroundIDBRequestChild(
1432 const RequestParams& aParams) {
1433 MOZ_CRASH(
1434 "PBackgroundIDBRequestChild actors should be manually "
1435 "constructed!");
1438 bool BackgroundTransactionChild::DeallocPBackgroundIDBRequestChild(
1439 PBackgroundIDBRequestChild* aActor) {
1440 MOZ_ASSERT(aActor);
1442 delete static_cast<BackgroundRequestChild*>(aActor);
1443 return true;
1446 PBackgroundIDBCursorChild*
1447 BackgroundTransactionChild::AllocPBackgroundIDBCursorChild(
1448 const OpenCursorParams& aParams) {
1449 AssertIsOnOwningThread();
1451 MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
1454 bool BackgroundTransactionChild::DeallocPBackgroundIDBCursorChild(
1455 PBackgroundIDBCursorChild* aActor) {
1456 MOZ_ASSERT(aActor);
1458 delete aActor;
1459 return true;
1462 /*******************************************************************************
1463 * BackgroundVersionChangeTransactionChild
1464 ******************************************************************************/
1466 BackgroundVersionChangeTransactionChild::
1467 BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest)
1468 : mOpenDBRequest(aOpenDBRequest) {
1469 MOZ_ASSERT(aOpenDBRequest);
1470 aOpenDBRequest->AssertIsOnOwningThread();
1472 MOZ_COUNT_CTOR(indexedDB::BackgroundVersionChangeTransactionChild);
1475 BackgroundVersionChangeTransactionChild::
1476 ~BackgroundVersionChangeTransactionChild() {
1477 AssertIsOnOwningThread();
1479 MOZ_COUNT_DTOR(indexedDB::BackgroundVersionChangeTransactionChild);
1482 #ifdef DEBUG
1484 void BackgroundVersionChangeTransactionChild::AssertIsOnOwningThread() const {
1485 static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread();
1488 #endif // DEBUG
1490 void BackgroundVersionChangeTransactionChild::SendDeleteMeInternal(
1491 bool aFailedConstructor) {
1492 AssertIsOnOwningThread();
1494 if (mTransaction || aFailedConstructor) {
1495 NoteActorDestroyed();
1497 MOZ_ALWAYS_TRUE(
1498 PBackgroundIDBVersionChangeTransactionChild::SendDeleteMe());
1502 void BackgroundVersionChangeTransactionChild::ActorDestroy(
1503 ActorDestroyReason aWhy) {
1504 AssertIsOnOwningThread();
1506 MaybeCollectGarbageOnIPCMessage();
1508 mOpenDBRequest = nullptr;
1510 NoteActorDestroyed();
1513 mozilla::ipc::IPCResult BackgroundVersionChangeTransactionChild::RecvComplete(
1514 const nsresult aResult) {
1515 AssertIsOnOwningThread();
1517 MaybeCollectGarbageOnIPCMessage();
1519 if (!mTransaction) {
1520 return IPC_OK();
1523 MOZ_ASSERT(mOpenDBRequest);
1525 IDBDatabase* database = mTransaction->Database();
1526 MOZ_ASSERT(database);
1528 database->ExitSetVersionTransaction();
1530 if (NS_FAILED(aResult)) {
1531 database->Close();
1534 RefPtr<IDBOpenDBRequest> request = mOpenDBRequest;
1535 MOZ_ASSERT(request);
1537 mTransaction->FireCompleteOrAbortEvents(aResult);
1539 request->SetTransaction(nullptr);
1540 request = nullptr;
1542 mOpenDBRequest = nullptr;
1544 NoteComplete();
1545 return IPC_OK();
1548 PBackgroundIDBRequestChild*
1549 BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild(
1550 const RequestParams& aParams) {
1551 MOZ_CRASH(
1552 "PBackgroundIDBRequestChild actors should be manually "
1553 "constructed!");
1556 bool BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBRequestChild(
1557 PBackgroundIDBRequestChild* aActor) {
1558 MOZ_ASSERT(aActor);
1560 delete static_cast<BackgroundRequestChild*>(aActor);
1561 return true;
1564 PBackgroundIDBCursorChild*
1565 BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBCursorChild(
1566 const OpenCursorParams& aParams) {
1567 AssertIsOnOwningThread();
1569 MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
1572 bool BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild(
1573 PBackgroundIDBCursorChild* aActor) {
1574 MOZ_ASSERT(aActor);
1576 delete aActor;
1577 return true;
1580 /*******************************************************************************
1581 * BackgroundRequestChild
1582 ******************************************************************************/
1584 BackgroundRequestChild::BackgroundRequestChild(
1585 MovingNotNull<RefPtr<IDBRequest>> aRequest)
1586 : BackgroundRequestChildBase(std::move(aRequest)),
1587 mTransaction(mRequest->AcquireTransaction()),
1588 mRunningPreprocessHelpers(0),
1589 mCurrentCloneDataIndex(0),
1590 mPreprocessResultCode(NS_OK),
1591 mGetAll(false) {
1592 MOZ_ASSERT(mTransaction);
1593 mTransaction->AssertIsOnOwningThread();
1595 MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild);
1598 BackgroundRequestChild::~BackgroundRequestChild() {
1599 AssertIsOnOwningThread();
1600 MOZ_ASSERT(!mTransaction);
1602 MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild);
1605 void BackgroundRequestChild::MaybeSendContinue() {
1606 AssertIsOnOwningThread();
1607 MOZ_ASSERT(mRunningPreprocessHelpers > 0);
1609 if (--mRunningPreprocessHelpers == 0) {
1610 PreprocessResponse response;
1612 if (NS_SUCCEEDED(mPreprocessResultCode)) {
1613 if (mGetAll) {
1614 response = ObjectStoreGetAllPreprocessResponse();
1615 } else {
1616 response = ObjectStoreGetPreprocessResponse();
1618 } else {
1619 response = mPreprocessResultCode;
1622 MOZ_ALWAYS_TRUE(SendContinue(response));
1626 void BackgroundRequestChild::OnPreprocessFinished(
1627 uint32_t aCloneDataIndex, UniquePtr<JSStructuredCloneData> aCloneData) {
1628 AssertIsOnOwningThread();
1629 MOZ_ASSERT(aCloneDataIndex < mCloneInfos.Length());
1630 MOZ_ASSERT(aCloneData);
1632 auto& cloneInfo = mCloneInfos[aCloneDataIndex];
1633 MOZ_ASSERT(cloneInfo.mPreprocessHelper);
1634 MOZ_ASSERT(!cloneInfo.mCloneData);
1636 cloneInfo.mCloneData = std::move(aCloneData);
1638 MaybeSendContinue();
1640 cloneInfo.mPreprocessHelper = nullptr;
1643 void BackgroundRequestChild::OnPreprocessFailed(uint32_t aCloneDataIndex,
1644 nsresult aErrorCode) {
1645 AssertIsOnOwningThread();
1646 MOZ_ASSERT(aCloneDataIndex < mCloneInfos.Length());
1647 MOZ_ASSERT(NS_FAILED(aErrorCode));
1649 auto& cloneInfo = mCloneInfos[aCloneDataIndex];
1650 MOZ_ASSERT(cloneInfo.mPreprocessHelper);
1651 MOZ_ASSERT(!cloneInfo.mCloneData);
1653 if (NS_SUCCEEDED(mPreprocessResultCode)) {
1654 mPreprocessResultCode = aErrorCode;
1657 MaybeSendContinue();
1659 cloneInfo.mPreprocessHelper = nullptr;
1662 UniquePtr<JSStructuredCloneData> BackgroundRequestChild::GetNextCloneData() {
1663 AssertIsOnOwningThread();
1664 MOZ_ASSERT(mCurrentCloneDataIndex < mCloneInfos.Length());
1665 MOZ_ASSERT(mCloneInfos[mCurrentCloneDataIndex].mCloneData);
1667 return std::move(mCloneInfos[mCurrentCloneDataIndex++].mCloneData);
1670 void BackgroundRequestChild::HandleResponse(nsresult aResponse) {
1671 AssertIsOnOwningThread();
1672 MOZ_ASSERT(NS_FAILED(aResponse));
1673 MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
1674 MOZ_ASSERT(mTransaction);
1676 DispatchErrorEvent(mRequest, aResponse, mTransaction.clonePtr());
1679 void BackgroundRequestChild::HandleResponse(const Key& aResponse) {
1680 AssertIsOnOwningThread();
1682 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse);
1685 void BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse) {
1686 AssertIsOnOwningThread();
1688 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse);
1691 void BackgroundRequestChild::HandleResponse(
1692 SerializedStructuredCloneReadInfo&& aResponse) {
1693 AssertIsOnOwningThread();
1695 if (!mTransaction->Database()->GetOwnerGlobal()) {
1696 // Ignore the response, since we have already been disconnected from the
1697 // global.
1698 return;
1701 auto cloneReadInfo = DeserializeStructuredCloneReadInfo(
1702 std::move(aResponse), mTransaction->Database(),
1703 [this] { return std::move(*GetNextCloneData()); });
1705 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(),
1706 cloneReadInfo);
1709 void BackgroundRequestChild::HandleResponse(
1710 nsTArray<SerializedStructuredCloneReadInfo>&& aResponse) {
1711 AssertIsOnOwningThread();
1713 if (!mTransaction->Database()->GetOwnerGlobal()) {
1714 // Ignore the response, since we have already been disconnected from the
1715 // global.
1716 return;
1719 nsTArray<StructuredCloneReadInfoChild> cloneReadInfos;
1721 QM_TRY(OkIf(cloneReadInfos.SetCapacity(aResponse.Length(), fallible)),
1722 QM_VOID, ([&aResponse, this](const auto) {
1723 // Since we are under memory pressure, release aResponse early.
1724 aResponse.Clear();
1726 DispatchErrorEvent(mRequest, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
1727 AcquireTransaction());
1729 MOZ_ASSERT(mTransaction->IsAborted());
1730 }));
1732 std::transform(std::make_move_iterator(aResponse.begin()),
1733 std::make_move_iterator(aResponse.end()),
1734 MakeBackInserter(cloneReadInfos),
1735 [database = mTransaction->Database(), this](
1736 SerializedStructuredCloneReadInfo&& serializedCloneInfo) {
1737 return DeserializeStructuredCloneReadInfo(
1738 std::move(serializedCloneInfo), database,
1739 [this] { return std::move(*GetNextCloneData()); });
1742 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(),
1743 cloneReadInfos);
1746 void BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse) {
1747 AssertIsOnOwningThread();
1749 SetResultAndDispatchSuccessEvent(
1750 mRequest, AcquireTransaction(),
1751 const_cast<const JS::Handle<JS::Value>&>(aResponse));
1754 void BackgroundRequestChild::HandleResponse(const uint64_t aResponse) {
1755 AssertIsOnOwningThread();
1757 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse);
1760 nsresult BackgroundRequestChild::HandlePreprocess(
1761 const PreprocessInfo& aPreprocessInfo) {
1762 return HandlePreprocessInternal(
1763 AutoTArray<PreprocessInfo, 1>{aPreprocessInfo});
1766 nsresult BackgroundRequestChild::HandlePreprocess(
1767 const nsTArray<PreprocessInfo>& aPreprocessInfos) {
1768 AssertIsOnOwningThread();
1769 mGetAll = true;
1771 return HandlePreprocessInternal(aPreprocessInfos);
1774 nsresult BackgroundRequestChild::HandlePreprocessInternal(
1775 const nsTArray<PreprocessInfo>& aPreprocessInfos) {
1776 AssertIsOnOwningThread();
1778 IDBDatabase* database = mTransaction->Database();
1780 const uint32_t count = aPreprocessInfos.Length();
1782 mCloneInfos.SetLength(count);
1784 // TODO: Since we use the stream transport service, this can spawn 25 threads
1785 // and has the potential to cause some annoying browser hiccups.
1786 // Consider using a single thread or a very small threadpool.
1787 for (uint32_t index = 0; index < count; index++) {
1788 const PreprocessInfo& preprocessInfo = aPreprocessInfos[index];
1790 const auto files =
1791 DeserializeStructuredCloneFiles(database, preprocessInfo.files(),
1792 /* aForPreprocess */ true);
1794 MOZ_ASSERT(files.Length() == 1);
1796 auto& preprocessHelper = mCloneInfos[index].mPreprocessHelper;
1797 preprocessHelper = MakeRefPtr<PreprocessHelper>(index, this);
1799 nsresult rv = preprocessHelper->Init(files[0]);
1800 if (NS_WARN_IF(NS_FAILED(rv))) {
1801 return rv;
1804 rv = preprocessHelper->Dispatch();
1805 if (NS_WARN_IF(NS_FAILED(rv))) {
1806 return rv;
1809 mRunningPreprocessHelpers++;
1812 return NS_OK;
1815 void BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy) {
1816 AssertIsOnOwningThread();
1818 MaybeCollectGarbageOnIPCMessage();
1820 for (auto& cloneInfo : mCloneInfos) {
1821 const auto& preprocessHelper = cloneInfo.mPreprocessHelper;
1823 if (preprocessHelper) {
1824 preprocessHelper->ClearActor();
1827 mCloneInfos.Clear();
1829 if (mTransaction) {
1830 mTransaction->AssertIsOnOwningThread();
1832 mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */
1833 aWhy == Deletion);
1834 #ifdef DEBUG
1835 mTransaction = nullptr;
1836 #endif
1840 mozilla::ipc::IPCResult BackgroundRequestChild::Recv__delete__(
1841 RequestResponse&& aResponse) {
1842 AssertIsOnOwningThread();
1843 MOZ_ASSERT(mTransaction);
1845 MaybeCollectGarbageOnIPCMessage();
1847 if (mTransaction->IsAborted()) {
1848 // Always fire an "error" event with ABORT_ERR if the transaction was
1849 // aborted, even if the request succeeded or failed with another error.
1850 HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
1851 } else {
1852 switch (aResponse.type()) {
1853 case RequestResponse::Tnsresult:
1854 HandleResponse(aResponse.get_nsresult());
1855 break;
1857 case RequestResponse::TObjectStoreAddResponse:
1858 HandleResponse(aResponse.get_ObjectStoreAddResponse().key());
1859 break;
1861 case RequestResponse::TObjectStorePutResponse:
1862 HandleResponse(aResponse.get_ObjectStorePutResponse().key());
1863 break;
1865 case RequestResponse::TObjectStoreGetResponse:
1866 HandleResponse(
1867 std::move(aResponse.get_ObjectStoreGetResponse().cloneInfo()));
1868 break;
1870 case RequestResponse::TObjectStoreGetKeyResponse:
1871 HandleResponse(aResponse.get_ObjectStoreGetKeyResponse().key());
1872 break;
1874 case RequestResponse::TObjectStoreGetAllResponse:
1875 HandleResponse(
1876 std::move(aResponse.get_ObjectStoreGetAllResponse().cloneInfos()));
1877 break;
1879 case RequestResponse::TObjectStoreGetAllKeysResponse:
1880 HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse().keys());
1881 break;
1883 case RequestResponse::TObjectStoreDeleteResponse:
1884 case RequestResponse::TObjectStoreClearResponse:
1885 HandleResponse(JS::UndefinedHandleValue);
1886 break;
1888 case RequestResponse::TObjectStoreCountResponse:
1889 HandleResponse(aResponse.get_ObjectStoreCountResponse().count());
1890 break;
1892 case RequestResponse::TIndexGetResponse:
1893 HandleResponse(std::move(aResponse.get_IndexGetResponse().cloneInfo()));
1894 break;
1896 case RequestResponse::TIndexGetKeyResponse:
1897 HandleResponse(aResponse.get_IndexGetKeyResponse().key());
1898 break;
1900 case RequestResponse::TIndexGetAllResponse:
1901 HandleResponse(
1902 std::move(aResponse.get_IndexGetAllResponse().cloneInfos()));
1903 break;
1905 case RequestResponse::TIndexGetAllKeysResponse:
1906 HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys());
1907 break;
1909 case RequestResponse::TIndexCountResponse:
1910 HandleResponse(aResponse.get_IndexCountResponse().count());
1911 break;
1913 default:
1914 return IPC_FAIL(this, "Unknown response type!");
1918 mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ true);
1920 // Null this out so that we don't try to call OnRequestFinished() again in
1921 // ActorDestroy.
1922 mTransaction = nullptr;
1924 return IPC_OK();
1927 mozilla::ipc::IPCResult BackgroundRequestChild::RecvPreprocess(
1928 const PreprocessParams& aParams) {
1929 AssertIsOnOwningThread();
1930 MOZ_ASSERT(mTransaction);
1932 MaybeCollectGarbageOnIPCMessage();
1934 nsresult rv;
1936 switch (aParams.type()) {
1937 case PreprocessParams::TObjectStoreGetPreprocessParams: {
1938 const auto& params = aParams.get_ObjectStoreGetPreprocessParams();
1940 rv = HandlePreprocess(params.preprocessInfo());
1942 break;
1945 case PreprocessParams::TObjectStoreGetAllPreprocessParams: {
1946 const auto& params = aParams.get_ObjectStoreGetAllPreprocessParams();
1948 rv = HandlePreprocess(params.preprocessInfos());
1950 break;
1953 default:
1954 return IPC_FAIL(this, "Unknown params type!");
1957 if (NS_WARN_IF(NS_FAILED(rv))) {
1958 QM_WARNONLY_TRY(OkIf(SendContinue(rv)));
1961 return IPC_OK();
1964 nsresult BackgroundRequestChild::PreprocessHelper::Init(
1965 const StructuredCloneFileChild& aFile) {
1966 AssertIsOnOwningThread();
1967 MOZ_ASSERT(aFile.HasBlob());
1968 MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eStructuredClone);
1969 MOZ_ASSERT(mState == State::Initial);
1971 // The stream transport service is used for asynchronous processing. It has a
1972 // threadpool with a high cap of 25 threads. Fortunately, the service can be
1973 // used on workers too.
1974 nsCOMPtr<nsIEventTarget> target =
1975 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
1976 MOZ_ASSERT(target);
1978 // We use a TaskQueue here in order to be sure that the events are dispatched
1979 // in the correct order. This is not guaranteed in case we use the I/O thread
1980 // directly.
1981 mTaskQueue = TaskQueue::Create(target.forget(), "BackgroundRequestChild");
1983 ErrorResult errorResult;
1985 nsCOMPtr<nsIInputStream> stream;
1986 // XXX After Bug 1620560, MutableBlob is not needed here anymore.
1987 aFile.MutableBlob().CreateInputStream(getter_AddRefs(stream), errorResult);
1988 if (NS_WARN_IF(errorResult.Failed())) {
1989 return errorResult.StealNSResult();
1992 mStream = std::move(stream);
1994 mCloneData = MakeUnique<JSStructuredCloneData>(
1995 JS::StructuredCloneScope::DifferentProcessForIndexedDB);
1997 return NS_OK;
2000 nsresult BackgroundRequestChild::PreprocessHelper::Dispatch() {
2001 AssertIsOnOwningThread();
2002 MOZ_ASSERT(mState == State::Initial);
2004 nsresult rv = mTaskQueue->Dispatch(this, NS_DISPATCH_NORMAL);
2005 if (NS_WARN_IF(NS_FAILED(rv))) {
2006 return rv;
2009 return NS_OK;
2012 nsresult BackgroundRequestChild::PreprocessHelper::Start() {
2013 MOZ_ASSERT(!IsOnOwningThread());
2014 MOZ_ASSERT(mStream);
2015 MOZ_ASSERT(mState == State::Initial);
2017 nsresult rv;
2019 PRFileDesc* fileDesc = GetFileDescriptorFromStream(mStream);
2020 if (fileDesc) {
2021 rv = ProcessStream();
2022 if (NS_WARN_IF(NS_FAILED(rv))) {
2023 return rv;
2026 return NS_OK;
2029 mState = State::WaitingForStreamReady;
2031 nsCOMPtr<nsIAsyncFileMetadata> asyncFileMetadata = do_QueryInterface(mStream);
2032 if (asyncFileMetadata) {
2033 rv = asyncFileMetadata->AsyncFileMetadataWait(this, mTaskQueue);
2034 if (NS_WARN_IF(NS_FAILED(rv))) {
2035 return rv;
2038 return NS_OK;
2041 nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream);
2042 if (!asyncStream) {
2043 return NS_ERROR_NO_INTERFACE;
2046 rv = asyncStream->AsyncWait(this, 0, 0, mTaskQueue);
2047 if (NS_WARN_IF(NS_FAILED(rv))) {
2048 return rv;
2051 return NS_OK;
2054 nsresult BackgroundRequestChild::PreprocessHelper::ProcessStream() {
2055 MOZ_ASSERT(!IsOnOwningThread());
2056 MOZ_ASSERT(mStream);
2057 MOZ_ASSERT(mState == State::Initial ||
2058 mState == State::WaitingForStreamReady);
2060 // We need to get the internal stream (which is an nsFileInputStream) because
2061 // SnappyUncompressInputStream doesn't support reading from async input
2062 // streams.
2064 nsCOMPtr<mozIRemoteLazyInputStream> blobInputStream =
2065 do_QueryInterface(mStream);
2066 MOZ_ASSERT(blobInputStream);
2068 nsCOMPtr<nsIInputStream> internalInputStream;
2069 MOZ_ALWAYS_SUCCEEDS(
2070 blobInputStream->TakeInternalStream(getter_AddRefs(internalInputStream)));
2071 MOZ_ASSERT(internalInputStream);
2073 QM_TRY(MOZ_TO_RESULT(
2074 SnappyUncompressStructuredCloneData(*internalInputStream, *mCloneData)));
2076 mState = State::Finishing;
2078 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)));
2080 return NS_OK;
2083 void BackgroundRequestChild::PreprocessHelper::Finish() {
2084 AssertIsOnOwningThread();
2086 if (mActor) {
2087 if (NS_SUCCEEDED(mResultCode)) {
2088 mActor->OnPreprocessFinished(mCloneDataIndex, std::move(mCloneData));
2090 MOZ_ASSERT(!mCloneData);
2091 } else {
2092 mActor->OnPreprocessFailed(mCloneDataIndex, mResultCode);
2096 mState = State::Completed;
2099 NS_IMPL_ISUPPORTS_INHERITED(BackgroundRequestChild::PreprocessHelper,
2100 DiscardableRunnable, nsIInputStreamCallback,
2101 nsIFileMetadataCallback)
2103 NS_IMETHODIMP
2104 BackgroundRequestChild::PreprocessHelper::Run() {
2105 nsresult rv;
2107 switch (mState) {
2108 case State::Initial:
2109 rv = Start();
2110 break;
2112 case State::WaitingForStreamReady:
2113 rv = ProcessStream();
2114 break;
2116 case State::Finishing:
2117 Finish();
2118 return NS_OK;
2120 default:
2121 MOZ_CRASH("Bad state!");
2124 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
2125 if (NS_SUCCEEDED(mResultCode)) {
2126 mResultCode = rv;
2129 // Must set mState before dispatching otherwise we will race with the owning
2130 // thread.
2131 mState = State::Finishing;
2133 if (IsOnOwningThread()) {
2134 Finish();
2135 } else {
2136 MOZ_ALWAYS_SUCCEEDS(
2137 mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
2141 return NS_OK;
2144 NS_IMETHODIMP
2145 BackgroundRequestChild::PreprocessHelper::OnInputStreamReady(
2146 nsIAsyncInputStream* aStream) {
2147 MOZ_ASSERT(!IsOnOwningThread());
2148 MOZ_ASSERT(mState == State::WaitingForStreamReady);
2150 MOZ_ALWAYS_SUCCEEDS(this->Run());
2152 return NS_OK;
2155 NS_IMETHODIMP
2156 BackgroundRequestChild::PreprocessHelper::OnFileMetadataReady(
2157 nsIAsyncFileMetadata* aObject) {
2158 MOZ_ASSERT(!IsOnOwningThread());
2159 MOZ_ASSERT(mState == State::WaitingForStreamReady);
2161 MOZ_ALWAYS_SUCCEEDS(this->Run());
2163 return NS_OK;
2166 /*******************************************************************************
2167 * BackgroundCursorChild
2168 ******************************************************************************/
2170 BackgroundCursorChildBase::BackgroundCursorChildBase(
2171 const NotNull<IDBRequest*> aRequest, const Direction aDirection)
2172 : mRequest(aRequest),
2173 mTransaction(aRequest->MaybeTransactionRef()),
2174 mStrongRequest(aRequest),
2175 mDirection(aDirection) {
2176 MOZ_ASSERT(mTransaction);
2179 MovingNotNull<RefPtr<IDBRequest>> BackgroundCursorChildBase::AcquireRequest()
2180 const {
2181 AssertIsOnOwningThread();
2183 // XXX This could be encapsulated by NotNull
2184 return WrapNotNullUnchecked(RefPtr{mRequest->get()});
2187 template <IDBCursorType CursorType>
2188 BackgroundCursorChild<CursorType>::BackgroundCursorChild(
2189 const NotNull<IDBRequest*> aRequest, SourceType* aSource,
2190 Direction aDirection)
2191 : BackgroundCursorChildBase(aRequest, aDirection),
2192 mSource(WrapNotNull(aSource)),
2193 mCursor(nullptr),
2194 mInFlightResponseInvalidationNeeded(false) {
2195 aSource->AssertIsOnOwningThread();
2197 MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild<CursorType>);
2200 template <IDBCursorType CursorType>
2201 BackgroundCursorChild<CursorType>::~BackgroundCursorChild() {
2202 MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild<CursorType>);
2205 template <IDBCursorType CursorType>
2206 SafeRefPtr<BackgroundCursorChild<CursorType>>
2207 BackgroundCursorChild<CursorType>::SafeRefPtrFromThis() {
2208 return BackgroundCursorChildBase::SafeRefPtrFromThis()
2209 .template downcast<BackgroundCursorChild>();
2212 template <IDBCursorType CursorType>
2213 void BackgroundCursorChild<CursorType>::SendContinueInternal(
2214 const CursorRequestParams& aParams,
2215 const CursorData<CursorType>& aCurrentData) {
2216 AssertIsOnOwningThread();
2217 MOZ_ASSERT(mRequest);
2218 MOZ_ASSERT(mTransaction);
2219 MOZ_ASSERT(mCursor);
2220 MOZ_ASSERT(!mStrongRequest);
2221 MOZ_ASSERT(!mStrongCursor);
2223 // Make sure all our DOM objects stay alive.
2224 mStrongCursor = mCursor;
2226 MOZ_ASSERT(GetRequest()->ReadyState() == IDBRequestReadyState::Done);
2227 GetRequest()->Reset();
2229 mTransaction->OnNewRequest();
2231 CursorRequestParams params = aParams;
2232 Key currentKey = aCurrentData.mKey;
2233 Key currentObjectStoreKey;
2234 // TODO: This is still not nice.
2235 if constexpr (!CursorTypeTraits<CursorType>::IsObjectStoreCursor) {
2236 currentObjectStoreKey = aCurrentData.mObjectStoreKey;
2239 switch (params.type()) {
2240 case CursorRequestParams::TContinueParams: {
2241 const auto& key = params.get_ContinueParams().key();
2242 if (key.IsUnset()) {
2243 break;
2246 // Discard cache entries before the target key.
2247 DiscardCachedResponses(
2248 [&key, isLocaleAware = mCursor->IsLocaleAware(),
2249 keyOperator = GetKeyOperator(mDirection),
2250 transactionSerialNumber = mTransaction->LoggingSerialNumber(),
2251 requestSerialNumber = GetRequest()->LoggingSerialNumber()](
2252 const auto& currentCachedResponse) {
2253 // This duplicates the logic from the parent. We could avoid this
2254 // duplication if we invalidated the cached records always for any
2255 // continue-with-key operation, but would lose the benefits of
2256 // preloading then.
2257 const auto& cachedSortKey =
2258 currentCachedResponse.GetSortKey(isLocaleAware);
2259 const bool discard = !(cachedSortKey.*keyOperator)(key);
2260 if (discard) {
2261 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2262 "PRELOAD: Continue to key %s, discarding cached key %s/%s",
2263 "Continue, discarding%.0s%.0s%.0s", transactionSerialNumber,
2264 requestSerialNumber, key.GetBuffer().get(),
2265 cachedSortKey.GetBuffer().get(),
2266 currentCachedResponse.GetObjectStoreKeyForLogging());
2267 } else {
2268 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2269 "PRELOAD: Continue to key %s, keeping cached key %s/%s and "
2270 "further",
2271 "Continue, keeping%.0s%.0s%.0s", transactionSerialNumber,
2272 requestSerialNumber, key.GetBuffer().get(),
2273 cachedSortKey.GetBuffer().get(),
2274 currentCachedResponse.GetObjectStoreKeyForLogging());
2277 return discard;
2280 break;
2283 case CursorRequestParams::TContinuePrimaryKeyParams: {
2284 if constexpr (!CursorTypeTraits<CursorType>::IsObjectStoreCursor) {
2285 const auto& key = params.get_ContinuePrimaryKeyParams().key();
2286 const auto& primaryKey =
2287 params.get_ContinuePrimaryKeyParams().primaryKey();
2288 if (key.IsUnset() || primaryKey.IsUnset()) {
2289 break;
2292 // Discard cache entries before the target key.
2293 DiscardCachedResponses([&key, &primaryKey,
2294 isLocaleAware = mCursor->IsLocaleAware(),
2295 keyCompareOperator = GetKeyOperator(mDirection),
2296 transactionSerialNumber =
2297 mTransaction->LoggingSerialNumber(),
2298 requestSerialNumber =
2299 GetRequest()->LoggingSerialNumber()](
2300 const auto& currentCachedResponse) {
2301 // This duplicates the logic from the parent. We could avoid this
2302 // duplication if we invalidated the cached records always for any
2303 // continue-with-key operation, but would lose the benefits of
2304 // preloading then.
2305 const auto& cachedSortKey =
2306 currentCachedResponse.GetSortKey(isLocaleAware);
2307 const auto& cachedSortPrimaryKey =
2308 currentCachedResponse.mObjectStoreKey;
2310 const bool discard =
2311 (cachedSortKey == key &&
2312 !(cachedSortPrimaryKey.*keyCompareOperator)(primaryKey)) ||
2313 !(cachedSortKey.*keyCompareOperator)(key);
2315 if (discard) {
2316 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2317 "PRELOAD: Continue to key %s with primary key %s, discarding "
2318 "cached key %s with cached primary key %s",
2319 "Continue, discarding%.0s%.0s%.0s%.0s", transactionSerialNumber,
2320 requestSerialNumber, key.GetBuffer().get(),
2321 primaryKey.GetBuffer().get(), cachedSortKey.GetBuffer().get(),
2322 cachedSortPrimaryKey.GetBuffer().get());
2323 } else {
2324 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2325 "PRELOAD: Continue to key %s with primary key %s, keeping "
2326 "cached key %s with cached primary key %s and further",
2327 "Continue, keeping%.0s%.0s%.0s%.0s", transactionSerialNumber,
2328 requestSerialNumber, key.GetBuffer().get(),
2329 primaryKey.GetBuffer().get(), cachedSortKey.GetBuffer().get(),
2330 cachedSortPrimaryKey.GetBuffer().get());
2333 return discard;
2335 } else {
2336 MOZ_CRASH("Shouldn't get here");
2339 break;
2342 case CursorRequestParams::TAdvanceParams: {
2343 uint32_t& advanceCount = params.get_AdvanceParams().count();
2344 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2345 "PRELOAD: Advancing %" PRIu32 " records", "Advancing %" PRIu32,
2346 mTransaction->LoggingSerialNumber(),
2347 GetRequest()->LoggingSerialNumber(), advanceCount);
2349 // Discard cache entries.
2350 DiscardCachedResponses([&advanceCount, &currentKey,
2351 &currentObjectStoreKey](
2352 const auto& currentCachedResponse) {
2353 const bool res = advanceCount > 1;
2354 if (res) {
2355 --advanceCount;
2357 // TODO: We only need to update currentKey on the last entry, the
2358 // others are overwritten in the next iteration anyway.
2359 currentKey = currentCachedResponse.mKey;
2360 if constexpr (!CursorTypeTraits<CursorType>::IsObjectStoreCursor) {
2361 currentObjectStoreKey = currentCachedResponse.mObjectStoreKey;
2362 } else {
2363 Unused << currentObjectStoreKey;
2366 return res;
2368 break;
2371 default:
2372 MOZ_CRASH("Should never get here!");
2375 if (!mCachedResponses.empty()) {
2376 // We need to remove the response here from mCachedResponses, since when
2377 // requests are interleaved, other events may be processed before
2378 // CompleteContinueRequestFromCache, which may modify mCachedResponses.
2379 mDelayedResponses.emplace_back(std::move(mCachedResponses.front()));
2380 mCachedResponses.pop_front();
2382 // We cannot send the response right away, as we must preserve the request
2383 // order. Dispatching a DelayedActionRunnable only partially addresses this.
2384 // This is accompanied by invalidating cached entries at proper locations to
2385 // make it correct. To avoid this, further changes are necessary, see Bug
2386 // 1580499.
2387 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(
2388 MakeAndAddRef<DelayedActionRunnable<BackgroundCursorChild<CursorType>>>(
2389 SafeRefPtrFromThis(),
2390 &BackgroundCursorChild::CompleteContinueRequestFromCache)));
2392 // TODO: Could we preload further entries in the background when the size of
2393 // mCachedResponses falls under some threshold? Or does the response
2394 // handling model disallow this?
2395 } else {
2396 MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(
2397 params, currentKey, currentObjectStoreKey));
2401 template <IDBCursorType CursorType>
2402 void BackgroundCursorChild<CursorType>::CompleteContinueRequestFromCache() {
2403 AssertIsOnOwningThread();
2404 MOZ_ASSERT(mTransaction);
2405 MOZ_ASSERT(mCursor);
2406 MOZ_ASSERT(mStrongCursor);
2407 MOZ_ASSERT(!mDelayedResponses.empty());
2408 MOZ_ASSERT(mCursor->GetType() == CursorType);
2410 const RefPtr<IDBCursor> cursor = std::move(mStrongCursor);
2412 mCursor->Reset(std::move(mDelayedResponses.front()));
2413 mDelayedResponses.pop_front();
2415 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2416 "PRELOAD: Consumed 1 cached response, %zu cached responses remaining",
2417 "Consumed cached response, %zu remaining",
2418 mTransaction->LoggingSerialNumber(), GetRequest()->LoggingSerialNumber(),
2419 mDelayedResponses.size() + mCachedResponses.size());
2421 SetResultAndDispatchSuccessEvent(
2422 GetRequest(),
2423 mTransaction
2424 ? SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}}
2425 : nullptr,
2426 *cursor);
2428 mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ true);
2431 template <IDBCursorType CursorType>
2432 void BackgroundCursorChild<CursorType>::SendDeleteMeInternal() {
2433 AssertIsOnOwningThread();
2434 MOZ_ASSERT(!mStrongRequest);
2435 MOZ_ASSERT(!mStrongCursor);
2437 mRequest.destroy();
2438 mTransaction = Nothing();
2439 // TODO: The things until here could be pulled up to
2440 // BackgroundCursorChildBase.
2442 mSource.destroy();
2444 if (mCursor) {
2445 mCursor->ClearBackgroundActor();
2446 mCursor = nullptr;
2448 MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendDeleteMe());
2452 template <IDBCursorType CursorType>
2453 void BackgroundCursorChild<CursorType>::InvalidateCachedResponses() {
2454 AssertIsOnOwningThread();
2455 MOZ_ASSERT(mTransaction);
2456 MOZ_ASSERT(mRequest);
2458 // TODO: With more information on the reason for the invalidation, we might
2459 // only selectively invalidate cached responses. If the reason is an updated
2460 // value, we do not need to care for key-only cursors. If the key of the
2461 // changed entry is not in the remaining range of the cursor, we also do not
2462 // need to care, etc.
2464 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2465 "PRELOAD: Invalidating all %zu cached responses", "Invalidating %zu",
2466 mTransaction->LoggingSerialNumber(), GetRequest()->LoggingSerialNumber(),
2467 mCachedResponses.size());
2469 mCachedResponses.clear();
2471 // We only hold a strong cursor reference in mStrongCursor when
2472 // continue()/similar has been called. In those cases we expect a response
2473 // that will be received in the future, and it may include prefetched data
2474 // that needs to be discarded.
2475 if (mStrongCursor) {
2476 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2477 "PRELOAD: Setting flag to invalidate in-flight responses",
2478 "Set flag to invalidate in-flight responses",
2479 mTransaction->LoggingSerialNumber(),
2480 GetRequest()->LoggingSerialNumber());
2482 mInFlightResponseInvalidationNeeded = true;
2486 template <IDBCursorType CursorType>
2487 template <typename Condition>
2488 void BackgroundCursorChild<CursorType>::DiscardCachedResponses(
2489 const Condition& aConditionFunc) {
2490 size_t discardedCount = 0;
2491 while (!mCachedResponses.empty() &&
2492 aConditionFunc(mCachedResponses.front())) {
2493 mCachedResponses.pop_front();
2494 ++discardedCount;
2496 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2497 "PRELOAD: Discarded %zu cached responses, %zu remaining",
2498 "Discarded %zu; remaining %zu", mTransaction->LoggingSerialNumber(),
2499 GetRequest()->LoggingSerialNumber(), discardedCount,
2500 mCachedResponses.size());
2503 BackgroundCursorChildBase::~BackgroundCursorChildBase() = default;
2505 void BackgroundCursorChildBase::HandleResponse(nsresult aResponse) {
2506 AssertIsOnOwningThread();
2507 MOZ_ASSERT(NS_FAILED(aResponse));
2508 MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB);
2509 MOZ_ASSERT(mRequest);
2510 MOZ_ASSERT(mTransaction);
2511 MOZ_ASSERT(!mStrongRequest);
2512 MOZ_ASSERT(!mStrongCursor);
2514 DispatchErrorEvent(
2515 GetRequest(), aResponse,
2516 SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}});
2519 template <IDBCursorType CursorType>
2520 void BackgroundCursorChild<CursorType>::HandleResponse(
2521 const void_t& aResponse) {
2522 AssertIsOnOwningThread();
2523 MOZ_ASSERT(mRequest);
2524 MOZ_ASSERT(mTransaction);
2525 MOZ_ASSERT(!mStrongRequest);
2526 MOZ_ASSERT(!mStrongCursor);
2528 if (mCursor) {
2529 mCursor->Reset();
2532 SetResultAndDispatchSuccessEvent(
2533 GetRequest(),
2534 mTransaction
2535 ? SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}}
2536 : nullptr,
2537 JS::NullHandleValue);
2539 if (!mCursor) {
2540 MOZ_ALWAYS_SUCCEEDS(this->GetActorEventTarget()->Dispatch(
2541 MakeAndAddRef<DelayedActionRunnable<BackgroundCursorChild<CursorType>>>(
2542 SafeRefPtrFromThis(), &BackgroundCursorChild::SendDeleteMeInternal),
2543 NS_DISPATCH_NORMAL));
2547 template <IDBCursorType CursorType>
2548 template <typename... Args>
2549 RefPtr<IDBCursor>
2550 BackgroundCursorChild<CursorType>::HandleIndividualCursorResponse(
2551 const bool aUseAsCurrentResult, Args&&... aArgs) {
2552 if (mCursor) {
2553 if (aUseAsCurrentResult) {
2554 mCursor->Reset(CursorData<CursorType>{std::forward<Args>(aArgs)...});
2555 } else {
2556 mCachedResponses.emplace_back(std::forward<Args>(aArgs)...);
2558 return nullptr;
2561 MOZ_ASSERT(aUseAsCurrentResult);
2563 // TODO: This still looks quite dangerous to me. Why is mCursor not a
2564 // RefPtr?
2565 auto newCursor = IDBCursor::Create(this, std::forward<Args>(aArgs)...);
2566 mCursor = newCursor;
2567 return newCursor;
2570 template <IDBCursorType CursorType>
2571 template <typename Func>
2572 void BackgroundCursorChild<CursorType>::HandleMultipleCursorResponses(
2573 nsTArray<ResponseType>&& aResponses, const Func& aHandleRecord) {
2574 AssertIsOnOwningThread();
2575 MOZ_ASSERT(mRequest);
2576 MOZ_ASSERT(mTransaction);
2577 MOZ_ASSERT(!mStrongRequest);
2578 MOZ_ASSERT(!mStrongCursor);
2579 MOZ_ASSERT(aResponses.Length() > 0);
2581 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2582 "PRELOAD: Received %zu cursor responses", "Received %zu",
2583 mTransaction->LoggingSerialNumber(), GetRequest()->LoggingSerialNumber(),
2584 aResponses.Length());
2585 MOZ_ASSERT_IF(aResponses.Length() > 1, mCachedResponses.empty());
2587 // If a new cursor is created, we need to keep a reference to it until the
2588 // SetResultAndDispatchSuccessEvent creates a DOM Binding.
2589 RefPtr<IDBCursor> strongNewCursor;
2591 bool isFirst = true;
2592 for (auto& response : aResponses) {
2593 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2594 "PRELOAD: Processing response for key %s", "Processing%.0s",
2595 mTransaction->LoggingSerialNumber(),
2596 GetRequest()->LoggingSerialNumber(), response.key().GetBuffer().get());
2598 // TODO: At the moment, we only send a cursor request to the parent if
2599 // requested by the user code. Therefore, the first result is always used
2600 // as the current result, and the potential extra results are cached. If
2601 // we extended this towards preloading in the background, all results
2602 // might need to be cached.
2603 auto maybeNewCursor =
2604 aHandleRecord(/* aUseAsCurrentResult */ isFirst, std::move(response));
2605 if (maybeNewCursor) {
2606 MOZ_ASSERT(!strongNewCursor);
2607 strongNewCursor = std::move(maybeNewCursor);
2609 isFirst = false;
2611 if (mInFlightResponseInvalidationNeeded) {
2612 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
2613 "PRELOAD: Discarding remaining responses since "
2614 "mInFlightResponseInvalidationNeeded is set",
2615 "Discarding responses", mTransaction->LoggingSerialNumber(),
2616 GetRequest()->LoggingSerialNumber());
2618 mInFlightResponseInvalidationNeeded = false;
2619 break;
2623 SetResultAndDispatchSuccessEvent(
2624 GetRequest(),
2625 mTransaction
2626 ? SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}}
2627 : nullptr,
2628 *static_cast<IDBCursor*>(mCursor));
2631 template <IDBCursorType CursorType>
2632 void BackgroundCursorChild<CursorType>::HandleResponse(
2633 nsTArray<ResponseType>&& aResponses) {
2634 AssertIsOnOwningThread();
2636 if constexpr (CursorType == IDBCursorType::ObjectStore ||
2637 CursorType == IDBCursorType::Index) {
2638 MOZ_ASSERT(mTransaction);
2640 if (!mTransaction->Database()->GetOwnerGlobal()) {
2641 // Ignore the response, since we have already been disconnected from the
2642 // global.
2643 return;
2647 if constexpr (CursorType == IDBCursorType::ObjectStore) {
2648 HandleMultipleCursorResponses(
2649 std::move(aResponses), [this](const bool useAsCurrentResult,
2650 ObjectStoreCursorResponse&& response) {
2651 // TODO: Maybe move the deserialization of the clone-read-info into
2652 // the cursor, so that it is only done for records actually accessed,
2653 // which might not be the case for all cached records.
2654 return HandleIndividualCursorResponse(
2655 useAsCurrentResult, std::move(response.key()),
2656 DeserializeStructuredCloneReadInfo(
2657 std::move(response.cloneInfo()), mTransaction->Database(),
2658 PreprocessingNotSupported));
2661 if constexpr (CursorType == IDBCursorType::ObjectStoreKey) {
2662 HandleMultipleCursorResponses(
2663 std::move(aResponses), [this](const bool useAsCurrentResult,
2664 ObjectStoreKeyCursorResponse&& response) {
2665 return HandleIndividualCursorResponse(useAsCurrentResult,
2666 std::move(response.key()));
2669 if constexpr (CursorType == IDBCursorType::Index) {
2670 HandleMultipleCursorResponses(
2671 std::move(aResponses),
2672 [this](const bool useAsCurrentResult, IndexCursorResponse&& response) {
2673 return HandleIndividualCursorResponse(
2674 useAsCurrentResult, std::move(response.key()),
2675 std::move(response.sortKey()), std::move(response.objectKey()),
2676 DeserializeStructuredCloneReadInfo(
2677 std::move(response.cloneInfo()), mTransaction->Database(),
2678 PreprocessingNotSupported));
2681 if constexpr (CursorType == IDBCursorType::IndexKey) {
2682 HandleMultipleCursorResponses(
2683 std::move(aResponses), [this](const bool useAsCurrentResult,
2684 IndexKeyCursorResponse&& response) {
2685 return HandleIndividualCursorResponse(
2686 useAsCurrentResult, std::move(response.key()),
2687 std::move(response.sortKey()), std::move(response.objectKey()));
2692 template <IDBCursorType CursorType>
2693 void BackgroundCursorChild<CursorType>::ActorDestroy(ActorDestroyReason aWhy) {
2694 AssertIsOnOwningThread();
2695 MOZ_ASSERT_IF(aWhy == Deletion, !mStrongRequest);
2696 MOZ_ASSERT_IF(aWhy == Deletion, !mStrongCursor);
2698 MaybeCollectGarbageOnIPCMessage();
2700 if (mStrongRequest && !mStrongCursor && mTransaction) {
2701 mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */
2702 aWhy == Deletion);
2705 if (mCursor) {
2706 mCursor->ClearBackgroundActor();
2707 #ifdef DEBUG
2708 mCursor = nullptr;
2709 #endif
2712 #ifdef DEBUG
2713 mRequest.maybeDestroy();
2714 mTransaction = Nothing();
2715 mSource.maybeDestroy();
2716 #endif
2719 template <IDBCursorType CursorType>
2720 mozilla::ipc::IPCResult BackgroundCursorChild<CursorType>::RecvResponse(
2721 CursorResponse&& aResponse) {
2722 AssertIsOnOwningThread();
2723 MOZ_ASSERT(aResponse.type() != CursorResponse::T__None);
2724 MOZ_ASSERT(mRequest);
2725 MOZ_ASSERT(mTransaction);
2726 MOZ_ASSERT_IF(mCursor, mStrongCursor);
2727 MOZ_ASSERT_IF(!mCursor, mStrongRequest);
2729 MaybeCollectGarbageOnIPCMessage();
2731 const RefPtr<IDBRequest> request = std::move(mStrongRequest);
2732 Unused << request; // XXX see Bug 1605075
2733 const RefPtr<IDBCursor> cursor = std::move(mStrongCursor);
2734 Unused << cursor; // XXX see Bug 1605075
2736 const auto transaction =
2737 SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}};
2739 switch (aResponse.type()) {
2740 case CursorResponse::Tnsresult:
2741 HandleResponse(aResponse.get_nsresult());
2742 break;
2744 case CursorResponse::Tvoid_t:
2745 HandleResponse(aResponse.get_void_t());
2746 break;
2748 case CursorResponse::TArrayOfObjectStoreCursorResponse:
2749 if constexpr (CursorType == IDBCursorType::ObjectStore) {
2750 HandleResponse(
2751 std::move(aResponse.get_ArrayOfObjectStoreCursorResponse()));
2752 } else {
2753 MOZ_CRASH("Response type mismatch");
2755 break;
2757 case CursorResponse::TArrayOfObjectStoreKeyCursorResponse:
2758 if constexpr (CursorType == IDBCursorType::ObjectStoreKey) {
2759 HandleResponse(
2760 std::move(aResponse.get_ArrayOfObjectStoreKeyCursorResponse()));
2761 } else {
2762 MOZ_CRASH("Response type mismatch");
2764 break;
2766 case CursorResponse::TArrayOfIndexCursorResponse:
2767 if constexpr (CursorType == IDBCursorType::Index) {
2768 HandleResponse(std::move(aResponse.get_ArrayOfIndexCursorResponse()));
2769 } else {
2770 MOZ_CRASH("Response type mismatch");
2772 break;
2774 case CursorResponse::TArrayOfIndexKeyCursorResponse:
2775 if constexpr (CursorType == IDBCursorType::IndexKey) {
2776 HandleResponse(
2777 std::move(aResponse.get_ArrayOfIndexKeyCursorResponse()));
2778 } else {
2779 MOZ_CRASH("Response type mismatch");
2781 break;
2783 default:
2784 MOZ_CRASH("Should never get here!");
2787 transaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ true);
2789 return IPC_OK();
2792 template class BackgroundCursorChild<IDBCursorType::ObjectStore>;
2793 template class BackgroundCursorChild<IDBCursorType::ObjectStoreKey>;
2794 template class BackgroundCursorChild<IDBCursorType::Index>;
2795 template class BackgroundCursorChild<IDBCursorType::IndexKey>;
2797 template <typename T>
2798 NS_IMETHODIMP DelayedActionRunnable<T>::Run() {
2799 MOZ_ASSERT(mActor);
2800 mActor->AssertIsOnOwningThread();
2801 MOZ_ASSERT(mRequest);
2802 MOZ_ASSERT(mActionFunc);
2804 ((*mActor).*mActionFunc)();
2806 mActor = nullptr;
2807 mRequest = nullptr;
2809 return NS_OK;
2812 template <typename T>
2813 nsresult DelayedActionRunnable<T>::Cancel() {
2814 if (NS_WARN_IF(!mActor)) {
2815 return NS_ERROR_UNEXPECTED;
2818 // This must always run to clean up our state.
2819 Run();
2821 return NS_OK;
2824 /*******************************************************************************
2825 * BackgroundUtilsChild
2826 ******************************************************************************/
2828 BackgroundUtilsChild::BackgroundUtilsChild(IndexedDatabaseManager* aManager)
2829 : mManager(aManager) {
2830 AssertIsOnOwningThread();
2831 MOZ_ASSERT(aManager);
2833 MOZ_COUNT_CTOR(indexedDB::BackgroundUtilsChild);
2836 BackgroundUtilsChild::~BackgroundUtilsChild() {
2837 MOZ_COUNT_DTOR(indexedDB::BackgroundUtilsChild);
2840 void BackgroundUtilsChild::SendDeleteMeInternal() {
2841 AssertIsOnOwningThread();
2843 if (mManager) {
2844 mManager->ClearBackgroundActor();
2845 mManager = nullptr;
2847 MOZ_ALWAYS_TRUE(PBackgroundIndexedDBUtilsChild::SendDeleteMe());
2851 void BackgroundUtilsChild::ActorDestroy(ActorDestroyReason aWhy) {
2852 AssertIsOnOwningThread();
2854 if (mManager) {
2855 mManager->ClearBackgroundActor();
2856 #ifdef DEBUG
2857 mManager = nullptr;
2858 #endif
2862 } // namespace dom::indexedDB
2863 } // namespace mozilla