ActorsChild.cpp (88803B)
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/. */ 6 7 #include "ActorsChild.h" 8 9 #include <mozIRemoteLazyInputStream.h> 10 11 #include <type_traits> 12 13 #include "BackgroundChildImpl.h" 14 #include "IDBDatabase.h" 15 #include "IDBEvents.h" 16 #include "IDBFactory.h" 17 #include "IDBIndex.h" 18 #include "IDBObjectStore.h" 19 #include "IDBRequest.h" 20 #include "IDBTransaction.h" 21 #include "IndexedDBCommon.h" 22 #include "IndexedDatabase.h" 23 #include "IndexedDatabaseInlines.h" 24 #include "ProfilerHelpers.h" 25 #include "ReportInternalError.h" 26 #include "ThreadLocal.h" 27 #include "js/Array.h" // JS::NewArrayObject, JS::SetArrayLength 28 #include "js/Date.h" // JS::NewDateObject, JS::TimeClip 29 #include "js/PropertyAndElement.h" // JS_DefineElement, JS_DefineProperty 30 #include "mozilla/ArrayAlgorithm.h" 31 #include "mozilla/BasicEvents.h" 32 #include "mozilla/CycleCollectedJSRuntime.h" 33 #include "mozilla/Encoding.h" 34 #include "mozilla/Maybe.h" 35 #include "mozilla/ProfilerLabels.h" 36 #include "mozilla/TaskQueue.h" 37 #include "mozilla/dom/BlobImpl.h" 38 #include "mozilla/dom/BrowserChild.h" 39 #include "mozilla/dom/Element.h" 40 #include "mozilla/dom/Event.h" 41 #include "mozilla/dom/IPCBlobUtils.h" 42 #include "mozilla/dom/PermissionMessageUtils.h" 43 #include "mozilla/dom/WorkerPrivate.h" 44 #include "mozilla/dom/WorkerRunnable.h" 45 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h" 46 #include "mozilla/dom/quota/ResultExtensions.h" 47 #include "mozilla/ipc/BackgroundUtils.h" 48 #include "nsCOMPtr.h" 49 #include "nsContentUtils.h" 50 #include "nsIAsyncInputStream.h" 51 #include "nsIEventTarget.h" 52 #include "nsIFileStreams.h" 53 #include "nsNetCID.h" 54 #include "nsPIDOMWindow.h" 55 #include "nsThreadUtils.h" 56 #include "nsTraceRefcnt.h" 57 58 #ifdef DEBUG 59 # include "IndexedDatabaseManager.h" 60 #endif 61 62 #define GC_ON_IPC_MESSAGES 0 63 64 #if defined(DEBUG) || GC_ON_IPC_MESSAGES 65 66 # include "js/GCAPI.h" 67 # include "nsJSEnvironment.h" 68 69 # define BUILD_GC_ON_IPC_MESSAGES 70 71 #endif // DEBUG || GC_ON_IPC_MESSAGES 72 73 namespace mozilla { 74 75 using ipc::PrincipalInfo; 76 77 namespace dom::indexedDB { 78 79 /******************************************************************************* 80 * ThreadLocal 81 ******************************************************************************/ 82 83 ThreadLocal::ThreadLocal(const nsID& aBackgroundChildLoggingId) 84 : mLoggingInfo(aBackgroundChildLoggingId, 1, -1, 1), 85 mLoggingIdString(aBackgroundChildLoggingId) { 86 MOZ_COUNT_CTOR(mozilla::dom::indexedDB::ThreadLocal); 87 } 88 89 ThreadLocal::~ThreadLocal() { 90 MOZ_COUNT_DTOR(mozilla::dom::indexedDB::ThreadLocal); 91 } 92 93 /******************************************************************************* 94 * Helpers 95 ******************************************************************************/ 96 97 namespace { 98 99 void MaybeCollectGarbageOnIPCMessage() { 100 #ifdef BUILD_GC_ON_IPC_MESSAGES 101 static const bool kCollectGarbageOnIPCMessages = 102 # if GC_ON_IPC_MESSAGES 103 true; 104 # else 105 false; 106 # endif // GC_ON_IPC_MESSAGES 107 108 if (!kCollectGarbageOnIPCMessages) { 109 return; 110 } 111 112 static bool haveWarnedAboutGC = false; 113 static bool haveWarnedAboutNonMainThread = false; 114 115 if (!haveWarnedAboutGC) { 116 haveWarnedAboutGC = true; 117 NS_WARNING("IndexedDB child actor GC debugging enabled!"); 118 } 119 120 if (!NS_IsMainThread()) { 121 if (!haveWarnedAboutNonMainThread) { 122 haveWarnedAboutNonMainThread = true; 123 NS_WARNING("Don't know how to GC on a non-main thread yet."); 124 } 125 return; 126 } 127 128 nsJSContext::GarbageCollectNow(JS::GCReason::DOM_IPC); 129 nsJSContext::CycleCollectNow(CCReason::API); 130 #endif // BUILD_GC_ON_IPC_MESSAGES 131 } 132 133 class MOZ_STACK_CLASS AutoSetCurrentTransaction final { 134 using BackgroundChildImpl = mozilla::ipc::BackgroundChildImpl; 135 136 Maybe<IDBTransaction&> const mTransaction; 137 Maybe<IDBTransaction&> mPreviousTransaction; 138 ThreadLocal* mThreadLocal; 139 140 public: 141 AutoSetCurrentTransaction(const AutoSetCurrentTransaction&) = delete; 142 AutoSetCurrentTransaction(AutoSetCurrentTransaction&&) = delete; 143 AutoSetCurrentTransaction& operator=(const AutoSetCurrentTransaction&) = 144 delete; 145 AutoSetCurrentTransaction& operator=(AutoSetCurrentTransaction&&) = delete; 146 147 explicit AutoSetCurrentTransaction(Maybe<IDBTransaction&> aTransaction) 148 : mTransaction(aTransaction), mThreadLocal(nullptr) { 149 if (aTransaction) { 150 BackgroundChildImpl::ThreadLocal* threadLocal = 151 BackgroundChildImpl::GetThreadLocalForCurrentThread(); 152 MOZ_ASSERT(threadLocal); 153 154 // Hang onto this for resetting later. 155 mThreadLocal = threadLocal->mIndexedDBThreadLocal.get(); 156 MOZ_ASSERT(mThreadLocal); 157 158 // Save the current value. 159 mPreviousTransaction = mThreadLocal->MaybeCurrentTransactionRef(); 160 161 // Set the new value. 162 mThreadLocal->SetCurrentTransaction(aTransaction); 163 } 164 } 165 166 ~AutoSetCurrentTransaction() { 167 MOZ_ASSERT_IF(mThreadLocal, mTransaction); 168 MOZ_ASSERT_IF(mThreadLocal, 169 ReferenceEquals(mThreadLocal->MaybeCurrentTransactionRef(), 170 mTransaction)); 171 172 if (mThreadLocal) { 173 // Reset old value. 174 mThreadLocal->SetCurrentTransaction(mPreviousTransaction); 175 } 176 } 177 }; 178 179 template <typename T> 180 void SetResultAndDispatchSuccessEvent( 181 const NotNull<RefPtr<IDBRequest>>& aRequest, 182 const SafeRefPtr<IDBTransaction>& aTransaction, T& aPtr, 183 RefPtr<Event> aEvent = nullptr); 184 185 namespace detail { 186 void DispatchSuccessEvent(const NotNull<RefPtr<IDBRequest>>& aRequest, 187 const SafeRefPtr<IDBTransaction>& aTransaction, 188 const RefPtr<Event>& aEvent); 189 190 template <class T> 191 std::enable_if_t<std::is_same_v<T, IDBDatabase> || std::is_same_v<T, IDBCursor>, 192 nsresult> 193 GetResult(JSContext* aCx, T* aDOMObject, JS::MutableHandle<JS::Value> aResult) { 194 if (!aDOMObject) { 195 aResult.setNull(); 196 return NS_OK; 197 } 198 199 const bool ok = GetOrCreateDOMReflector(aCx, aDOMObject, aResult); 200 if (NS_WARN_IF(!ok)) { 201 IDB_REPORT_INTERNAL_ERR(); 202 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 203 } 204 205 return NS_OK; 206 } 207 208 nsresult GetResult(JSContext* aCx, const JS::Handle<JS::Value>* aValue, 209 JS::MutableHandle<JS::Value> aResult) { 210 aResult.set(*aValue); 211 return NS_OK; 212 } 213 214 nsresult GetResult(JSContext* aCx, const uint64_t* aValue, 215 JS::MutableHandle<JS::Value> aResult) { 216 aResult.set(JS::NumberValue(*aValue)); 217 return NS_OK; 218 } 219 220 nsresult GetResult(JSContext* aCx, StructuredCloneReadInfoChild&& aCloneInfo, 221 JS::MutableHandle<JS::Value> aResult) { 222 const bool ok = 223 IDBObjectStore::DeserializeValue(aCx, std::move(aCloneInfo), aResult); 224 225 if (NS_WARN_IF(!ok)) { 226 return NS_ERROR_DOM_DATA_CLONE_ERR; 227 } 228 229 return NS_OK; 230 } 231 232 nsresult GetResult(JSContext* aCx, StructuredCloneReadInfoChild* aCloneInfo, 233 JS::MutableHandle<JS::Value> aResult) { 234 return GetResult(aCx, std::move(*aCloneInfo), aResult); 235 } 236 237 nsresult GetResult(JSContext* aCx, 238 nsTArray<StructuredCloneReadInfoChild>* aCloneInfos, 239 JS::MutableHandle<JS::Value> aResult) { 240 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0)); 241 if (NS_WARN_IF(!array)) { 242 IDB_REPORT_INTERNAL_ERR(); 243 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 244 } 245 246 if (!aCloneInfos->IsEmpty()) { 247 const uint32_t count = aCloneInfos->Length(); 248 249 if (NS_WARN_IF(!JS::SetArrayLength(aCx, array, count))) { 250 IDB_REPORT_INTERNAL_ERR(); 251 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 252 } 253 254 for (uint32_t index = 0; index < count; index++) { 255 auto& cloneInfo = aCloneInfos->ElementAt(index); 256 257 JS::Rooted<JS::Value> value(aCx); 258 259 const nsresult rv = GetResult(aCx, std::move(cloneInfo), &value); 260 if (NS_WARN_IF(NS_FAILED(rv))) { 261 return rv; 262 } 263 264 if (NS_WARN_IF( 265 !JS_DefineElement(aCx, array, index, value, JSPROP_ENUMERATE))) { 266 IDB_REPORT_INTERNAL_ERR(); 267 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 268 } 269 } 270 } 271 272 aResult.setObject(*array); 273 return NS_OK; 274 } 275 276 nsresult GetResult(JSContext* aCx, const Key* aKey, 277 JS::MutableHandle<JS::Value> aResult) { 278 const nsresult rv = aKey->ToJSVal(aCx, aResult); 279 if (NS_WARN_IF(NS_FAILED(rv))) { 280 return rv; 281 } 282 return NS_OK; 283 } 284 285 nsresult GetResult(JSContext* aCx, const nsTArray<Key>* aKeys, 286 JS::MutableHandle<JS::Value> aResult) { 287 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0)); 288 if (NS_WARN_IF(!array)) { 289 IDB_REPORT_INTERNAL_ERR(); 290 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 291 } 292 293 if (!aKeys->IsEmpty()) { 294 const uint32_t count = aKeys->Length(); 295 296 if (NS_WARN_IF(!JS::SetArrayLength(aCx, array, count))) { 297 IDB_REPORT_INTERNAL_ERR(); 298 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 299 } 300 301 for (uint32_t index = 0; index < count; index++) { 302 const Key& key = aKeys->ElementAt(index); 303 MOZ_ASSERT(!key.IsUnset()); 304 305 JS::Rooted<JS::Value> value(aCx); 306 307 const nsresult rv = GetResult(aCx, &key, &value); 308 if (NS_WARN_IF(NS_FAILED(rv))) { 309 return rv; 310 } 311 312 if (NS_WARN_IF( 313 !JS_DefineElement(aCx, array, index, value, JSPROP_ENUMERATE))) { 314 IDB_REPORT_INTERNAL_ERR(); 315 return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; 316 } 317 } 318 } 319 320 aResult.setObject(*array); 321 return NS_OK; 322 } 323 } // namespace detail 324 325 auto DeserializeStructuredCloneFiles( 326 IDBDatabase* aDatabase, 327 const nsTArray<SerializedStructuredCloneFile>& aSerializedFiles, 328 bool aForPreprocess) { 329 MOZ_ASSERT_IF(aForPreprocess, aSerializedFiles.Length() == 1); 330 331 return TransformIntoNewArray( 332 aSerializedFiles, 333 [aForPreprocess, &database = *aDatabase]( 334 const auto& serializedFile) -> StructuredCloneFileChild { 335 MOZ_ASSERT_IF( 336 aForPreprocess, 337 serializedFile.type() == StructuredCloneFileBase::eStructuredClone); 338 339 const NullableBlob& blob = serializedFile.file(); 340 341 switch (serializedFile.type()) { 342 case StructuredCloneFileBase::eBlob: { 343 MOZ_ASSERT(blob.type() == NullableBlob::TIPCBlob); 344 345 const IPCBlob& ipcBlob = blob.get_IPCBlob(); 346 347 const RefPtr<BlobImpl> blobImpl = 348 IPCBlobUtils::Deserialize(ipcBlob); 349 MOZ_ASSERT(blobImpl); 350 351 RefPtr<Blob> blob = 352 Blob::Create(database.GetOwnerGlobal(), blobImpl); 353 MOZ_ASSERT(blob); 354 355 return {StructuredCloneFileBase::eBlob, std::move(blob)}; 356 } 357 358 case StructuredCloneFileBase::eStructuredClone: { 359 if (aForPreprocess) { 360 MOZ_ASSERT(blob.type() == NullableBlob::TIPCBlob); 361 362 const IPCBlob& ipcBlob = blob.get_IPCBlob(); 363 364 const RefPtr<BlobImpl> blobImpl = 365 IPCBlobUtils::Deserialize(ipcBlob); 366 MOZ_ASSERT(blobImpl); 367 368 RefPtr<Blob> blob = 369 Blob::Create(database.GetOwnerGlobal(), blobImpl); 370 MOZ_ASSERT(blob); 371 372 return {StructuredCloneFileBase::eStructuredClone, 373 std::move(blob)}; 374 } 375 MOZ_ASSERT(blob.type() == NullableBlob::Tnull_t); 376 377 return StructuredCloneFileChild{ 378 StructuredCloneFileBase::eStructuredClone}; 379 } 380 381 case StructuredCloneFileBase::eMutableFile: 382 case StructuredCloneFileBase::eWasmBytecode: 383 case StructuredCloneFileBase::eWasmCompiled: { 384 MOZ_ASSERT(blob.type() == NullableBlob::Tnull_t); 385 386 return StructuredCloneFileChild{serializedFile.type()}; 387 388 // Don't set mBlob, support for storing WebAssembly.Modules has been 389 // removed in bug 1469395. Support for de-serialization of 390 // WebAssembly.Modules has been removed in bug 1561876. Support for 391 // MutableFile has been removed in bug 1500343. Full removal is 392 // tracked in bug 1487479. 393 } 394 395 default: 396 MOZ_CRASH("Should never get here!"); 397 } 398 }); 399 } 400 401 JSStructuredCloneData PreprocessingNotSupported() { 402 MOZ_CRASH("Preprocessing not (yet) supported!"); 403 } 404 405 template <typename PreprocessInfoAccessor> 406 StructuredCloneReadInfoChild DeserializeStructuredCloneReadInfo( 407 SerializedStructuredCloneReadInfo&& aSerialized, 408 IDBDatabase* const aDatabase, 409 PreprocessInfoAccessor preprocessInfoAccessor) { 410 // XXX Make this a class invariant of SerializedStructuredCloneReadInfo. 411 MOZ_ASSERT_IF(aSerialized.hasPreprocessInfo(), 412 0 == aSerialized.data().data.Size()); 413 return {aSerialized.hasPreprocessInfo() ? preprocessInfoAccessor() 414 : std::move(aSerialized.data().data), 415 DeserializeStructuredCloneFiles(aDatabase, aSerialized.files(), 416 /* aForPreprocess */ false), 417 aDatabase}; 418 } 419 420 // TODO: Remove duplication between DispatchErrorEvent and DispatchSucessEvent. 421 422 void DispatchErrorEvent( 423 MovingNotNull<RefPtr<IDBRequest>> aRequest, nsresult aErrorCode, 424 const SafeRefPtr<IDBTransaction>& aTransaction = nullptr, 425 RefPtr<Event> aEvent = nullptr) { 426 const RefPtr<IDBRequest> request = std::move(aRequest); 427 428 request->AssertIsOnOwningThread(); 429 MOZ_ASSERT(NS_FAILED(aErrorCode)); 430 MOZ_ASSERT(NS_ERROR_GET_MODULE(aErrorCode) == NS_ERROR_MODULE_DOM_INDEXEDDB); 431 432 AUTO_PROFILER_LABEL("IndexedDB:DispatchErrorEvent", DOM); 433 434 request->SetError(aErrorCode); 435 436 if (!aEvent) { 437 // Make an error event and fire it at the target. 438 aEvent = CreateGenericEvent(request, nsDependentString(kErrorEventType), 439 eDoesBubble, eCancelable); 440 } 441 MOZ_ASSERT(aEvent); 442 443 // XXX This is redundant if we are called from 444 // DispatchSuccessEvent. 445 Maybe<AutoSetCurrentTransaction> asct; 446 if (aTransaction) { 447 asct.emplace(SomeRef(*aTransaction)); 448 } 449 450 if (aTransaction && aTransaction->IsInactive()) { 451 aTransaction->TransitionToActive(); 452 } 453 454 if (aTransaction) { 455 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 456 "Firing %s event with error 0x%x", "%s (0x%" PRIx32 ")", 457 aTransaction->LoggingSerialNumber(), request->LoggingSerialNumber(), 458 IDB_LOG_STRINGIFY(aEvent, kErrorEventType), 459 static_cast<uint32_t>(aErrorCode)); 460 } else { 461 IDB_LOG_MARK_CHILD_REQUEST("Firing %s event with error 0x%x", 462 "%s (0x%" PRIx32 ")", 463 request->LoggingSerialNumber(), 464 IDB_LOG_STRINGIFY(aEvent, kErrorEventType), 465 static_cast<uint32_t>(aErrorCode)); 466 } 467 468 IgnoredErrorResult rv; 469 const bool doDefault = 470 request->DispatchEvent(*aEvent, CallerType::System, rv); 471 if (NS_WARN_IF(rv.Failed())) { 472 return; 473 } 474 475 MOZ_ASSERT(!aTransaction || aTransaction->IsActive() || 476 aTransaction->IsAborted() || 477 aTransaction->WasExplicitlyCommitted()); 478 479 if (aTransaction && aTransaction->IsActive()) { 480 aTransaction->TransitionToInactive(); 481 482 // Do not abort the transaction here if this request is failed due to the 483 // abortion of its transaction to ensure that the correct error cause of 484 // the abort event be set in IDBTransaction::FireCompleteOrAbortEvents() 485 // later. 486 if (aErrorCode != NS_ERROR_DOM_INDEXEDDB_ABORT_ERR) { 487 WidgetEvent* const internalEvent = aEvent->WidgetEventPtr(); 488 MOZ_ASSERT(internalEvent); 489 490 if (internalEvent->mFlags.mExceptionWasRaised) { 491 aTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); 492 } else if (doDefault) { 493 aTransaction->Abort(request); 494 } 495 } 496 } 497 } 498 499 template <typename T> 500 void SetResultAndDispatchSuccessEvent( 501 const NotNull<RefPtr<IDBRequest>>& aRequest, 502 const SafeRefPtr<IDBTransaction>& aTransaction, T& aPtr, 503 RefPtr<Event> aEvent) { 504 const auto autoTransaction = 505 AutoSetCurrentTransaction{aTransaction.maybeDeref()}; 506 507 AUTO_PROFILER_LABEL("IndexedDB:SetResultAndDispatchSuccessEvent", DOM); 508 509 aRequest->AssertIsOnOwningThread(); 510 511 if (aTransaction && aTransaction->IsAborted()) { 512 DispatchErrorEvent(aRequest, aTransaction->AbortCode(), aTransaction); 513 return; 514 } 515 516 if (!aEvent) { 517 aEvent = 518 CreateGenericEvent(aRequest.get(), nsDependentString(kSuccessEventType), 519 eDoesNotBubble, eNotCancelable); 520 } 521 MOZ_ASSERT(aEvent); 522 523 aRequest->SetResult( 524 [&aPtr](JSContext* aCx, JS::MutableHandle<JS::Value> aResult) { 525 MOZ_ASSERT(aCx); 526 return detail::GetResult(aCx, &aPtr, aResult); 527 }); 528 529 detail::DispatchSuccessEvent(aRequest, aTransaction, aEvent); 530 } 531 532 namespace detail { 533 void DispatchSuccessEvent(const NotNull<RefPtr<IDBRequest>>& aRequest, 534 const SafeRefPtr<IDBTransaction>& aTransaction, 535 const RefPtr<Event>& aEvent) { 536 if (aTransaction && aTransaction->IsInactive()) { 537 aTransaction->TransitionToActive(); 538 } 539 540 if (aTransaction) { 541 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 542 "Firing %s event", "%s", aTransaction->LoggingSerialNumber(), 543 aRequest->LoggingSerialNumber(), 544 IDB_LOG_STRINGIFY(aEvent, kSuccessEventType)); 545 } else { 546 IDB_LOG_MARK_CHILD_REQUEST("Firing %s event", "%s", 547 aRequest->LoggingSerialNumber(), 548 IDB_LOG_STRINGIFY(aEvent, kSuccessEventType)); 549 } 550 551 MOZ_ASSERT_IF(aTransaction && !aTransaction->WasExplicitlyCommitted(), 552 aTransaction->IsActive() && !aTransaction->IsAborted()); 553 554 IgnoredErrorResult rv; 555 aRequest->DispatchEvent(*aEvent, rv); 556 if (NS_WARN_IF(rv.Failed())) { 557 return; 558 } 559 560 WidgetEvent* const internalEvent = aEvent->WidgetEventPtr(); 561 MOZ_ASSERT(internalEvent); 562 563 if (aTransaction && aTransaction->IsActive()) { 564 aTransaction->TransitionToInactive(); 565 566 if (internalEvent->mFlags.mExceptionWasRaised) { 567 aTransaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); 568 } else { 569 // To handle upgrade transaction. 570 aTransaction->CommitIfNotStarted(); 571 } 572 } 573 } 574 } // namespace detail 575 576 PRFileDesc* GetFileDescriptorFromStream(nsIInputStream* aStream) { 577 MOZ_ASSERT(aStream); 578 579 const nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream); 580 if (NS_WARN_IF(!fileMetadata)) { 581 return nullptr; 582 } 583 584 PRFileDesc* fileDesc; 585 const nsresult rv = fileMetadata->GetFileDescriptor(&fileDesc); 586 if (NS_WARN_IF(NS_FAILED(rv))) { 587 return nullptr; 588 } 589 590 MOZ_ASSERT(fileDesc); 591 592 return fileDesc; 593 } 594 595 auto GetKeyOperator(const IDBCursorDirection aDirection) { 596 switch (aDirection) { 597 case IDBCursorDirection::Next: 598 case IDBCursorDirection::Nextunique: 599 return &Key::operator>=; 600 case IDBCursorDirection::Prev: 601 case IDBCursorDirection::Prevunique: 602 return &Key::operator<=; 603 default: 604 MOZ_CRASH("Should never get here."); 605 } 606 } 607 608 // Does not need to be threadsafe since this only runs on one thread, but 609 // inheriting from CancelableRunnable is easy. 610 template <typename T> 611 class DelayedActionRunnable final : public CancelableRunnable { 612 using ActionFunc = void (T::*)(); 613 614 SafeRefPtr<T> mActor; 615 RefPtr<IDBRequest> mRequest; 616 ActionFunc mActionFunc; 617 618 public: 619 explicit DelayedActionRunnable(SafeRefPtr<T> aActor, ActionFunc aActionFunc) 620 : CancelableRunnable("indexedDB::DelayedActionRunnable"), 621 mActor(std::move(aActor)), 622 mRequest(mActor->GetRequest()), 623 mActionFunc(aActionFunc) { 624 MOZ_ASSERT(mActor); 625 mActor->AssertIsOnOwningThread(); 626 MOZ_ASSERT(mRequest); 627 MOZ_ASSERT(mActionFunc); 628 } 629 630 private: 631 ~DelayedActionRunnable() = default; 632 633 NS_DECL_NSIRUNNABLE 634 nsresult Cancel() override; 635 }; 636 637 } // namespace 638 639 /******************************************************************************* 640 * Actor class declarations 641 ******************************************************************************/ 642 643 // DiscardableRunnable is used to make workers happy. 644 class BackgroundRequestChild::PreprocessHelper final 645 : public DiscardableRunnable, 646 public nsIInputStreamCallback, 647 public nsIFileMetadataCallback { 648 enum class State { 649 // Just created on the owning thread, dispatched to the thread pool. Next 650 // step is either Finishing if stream was ready to be read or 651 // WaitingForStreamReady if the stream is not ready. 652 Initial, 653 654 // Waiting for stream to be ready on a thread pool thread. Next state is 655 // Finishing. 656 WaitingForStreamReady, 657 658 // Waiting to finish/finishing on the owning thread. Next step is Completed. 659 Finishing, 660 661 // All done. 662 Completed 663 }; 664 665 const nsCOMPtr<nsIEventTarget> mOwningEventTarget; 666 RefPtr<TaskQueue> mTaskQueue; 667 nsCOMPtr<nsIInputStream> mStream; 668 UniquePtr<JSStructuredCloneData> mCloneData; 669 BackgroundRequestChild* mActor; 670 const uint32_t mCloneDataIndex; 671 nsresult mResultCode; 672 State mState; 673 674 public: 675 PreprocessHelper(uint32_t aCloneDataIndex, BackgroundRequestChild* aActor) 676 : DiscardableRunnable( 677 "indexedDB::BackgroundRequestChild::PreprocessHelper"), 678 mOwningEventTarget(aActor->GetActorEventTarget()), 679 mActor(aActor), 680 mCloneDataIndex(aCloneDataIndex), 681 mResultCode(NS_OK), 682 mState(State::Initial) { 683 AssertIsOnOwningThread(); 684 MOZ_ASSERT(aActor); 685 aActor->AssertIsOnOwningThread(); 686 } 687 688 bool IsOnOwningThread() const { 689 MOZ_ASSERT(mOwningEventTarget); 690 691 bool current; 692 return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && 693 current; 694 } 695 696 void AssertIsOnOwningThread() const { MOZ_ASSERT(IsOnOwningThread()); } 697 698 void ClearActor() { 699 AssertIsOnOwningThread(); 700 701 mActor = nullptr; 702 } 703 704 nsresult Init(const StructuredCloneFileChild& aFile); 705 706 nsresult Dispatch(); 707 708 private: 709 ~PreprocessHelper() { 710 MOZ_ASSERT(mState == State::Initial || mState == State::Completed); 711 712 if (mTaskQueue) { 713 mTaskQueue->BeginShutdown(); 714 } 715 } 716 717 nsresult Start(); 718 719 nsresult ProcessStream(); 720 721 void Finish(); 722 723 NS_DECL_ISUPPORTS_INHERITED 724 NS_DECL_NSIRUNNABLE 725 NS_DECL_NSIINPUTSTREAMCALLBACK 726 NS_DECL_NSIFILEMETADATACALLBACK 727 }; 728 729 /******************************************************************************* 730 * BackgroundRequestChildBase 731 ******************************************************************************/ 732 733 BackgroundRequestChildBase::BackgroundRequestChildBase( 734 MovingNotNull<RefPtr<IDBRequest>> aRequest) 735 : mRequest(std::move(aRequest)) { 736 mRequest->AssertIsOnOwningThread(); 737 738 MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChildBase); 739 } 740 741 BackgroundRequestChildBase::~BackgroundRequestChildBase() { 742 AssertIsOnOwningThread(); 743 744 MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChildBase); 745 } 746 747 #ifdef DEBUG 748 749 void BackgroundRequestChildBase::AssertIsOnOwningThread() const { 750 mRequest->AssertIsOnOwningThread(); 751 } 752 753 #endif // DEBUG 754 755 /******************************************************************************* 756 * BackgroundFactoryChild 757 ******************************************************************************/ 758 759 BackgroundFactoryChild::BackgroundFactoryChild(IDBFactory& aFactory) 760 : mFactory(&aFactory) { 761 AssertIsOnOwningThread(); 762 mFactory->AssertIsOnOwningThread(); 763 764 MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryChild); 765 } 766 767 BackgroundFactoryChild::~BackgroundFactoryChild() { 768 MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryChild); 769 } 770 771 void BackgroundFactoryChild::SendDeleteMeInternal() { 772 AssertIsOnOwningThread(); 773 774 if (mFactory) { 775 mFactory->ClearBackgroundActor(); 776 mFactory = nullptr; 777 778 MOZ_ALWAYS_TRUE(PBackgroundIDBFactoryChild::SendDeleteMe()); 779 } 780 } 781 782 void BackgroundFactoryChild::ActorDestroy(ActorDestroyReason aWhy) { 783 AssertIsOnOwningThread(); 784 785 MaybeCollectGarbageOnIPCMessage(); 786 787 if (mFactory) { 788 mFactory->ClearBackgroundActor(); 789 #ifdef DEBUG 790 mFactory = nullptr; 791 #endif 792 } 793 } 794 795 PBackgroundIDBFactoryRequestChild* 796 BackgroundFactoryChild::AllocPBackgroundIDBFactoryRequestChild( 797 const FactoryRequestParams& aParams) { 798 MOZ_CRASH( 799 "PBackgroundIDBFactoryRequestChild actors should be manually " 800 "constructed!"); 801 } 802 803 bool BackgroundFactoryChild::DeallocPBackgroundIDBFactoryRequestChild( 804 PBackgroundIDBFactoryRequestChild* aActor) { 805 MOZ_ASSERT(aActor); 806 807 delete static_cast<BackgroundFactoryRequestChild*>(aActor); 808 return true; 809 } 810 811 already_AddRefed<PBackgroundIDBDatabaseChild> 812 BackgroundFactoryChild::AllocPBackgroundIDBDatabaseChild( 813 const DatabaseSpec& aSpec, 814 PBackgroundIDBFactoryRequestChild* aRequest) const { 815 AssertIsOnOwningThread(); 816 817 auto* const request = static_cast<BackgroundFactoryRequestChild*>(aRequest); 818 MOZ_ASSERT(request); 819 820 RefPtr<BackgroundDatabaseChild> actor = 821 new BackgroundDatabaseChild(aSpec, request); 822 return actor.forget(); 823 } 824 825 mozilla::ipc::IPCResult 826 BackgroundFactoryChild::RecvPBackgroundIDBDatabaseConstructor( 827 PBackgroundIDBDatabaseChild* aActor, const DatabaseSpec& aSpec, 828 NotNull<PBackgroundIDBFactoryRequestChild*> aRequest) { 829 AssertIsOnOwningThread(); 830 MOZ_ASSERT(aActor); 831 832 return IPC_OK(); 833 } 834 835 /******************************************************************************* 836 * BackgroundFactoryRequestChild 837 ******************************************************************************/ 838 839 BackgroundFactoryRequestChild::BackgroundFactoryRequestChild( 840 SafeRefPtr<IDBFactory> aFactory, 841 MovingNotNull<RefPtr<IDBOpenDBRequest>> aOpenRequest, bool aIsDeleteOp, 842 uint64_t aRequestedVersion) 843 : BackgroundRequestChildBase(std::move(aOpenRequest)), 844 mFactory(std::move(aFactory)), 845 mDatabaseActor(nullptr), 846 mRequestedVersion(aRequestedVersion), 847 mIsDeleteOp(aIsDeleteOp) { 848 // Can't assert owning thread here because IPDL has not yet set our manager! 849 MOZ_ASSERT(mFactory); 850 mFactory->AssertIsOnOwningThread(); 851 852 MOZ_COUNT_CTOR(indexedDB::BackgroundFactoryRequestChild); 853 } 854 855 BackgroundFactoryRequestChild::~BackgroundFactoryRequestChild() { 856 MOZ_COUNT_DTOR(indexedDB::BackgroundFactoryRequestChild); 857 } 858 859 NotNull<IDBOpenDBRequest*> BackgroundFactoryRequestChild::GetOpenDBRequest() 860 const { 861 AssertIsOnOwningThread(); 862 863 // XXX NotNull might provide something to encapsulate this 864 return WrapNotNullUnchecked( 865 static_cast<IDBOpenDBRequest*>(mRequest.get().get())); 866 } 867 868 void BackgroundFactoryRequestChild::SetDatabaseActor( 869 BackgroundDatabaseChild* aActor) { 870 AssertIsOnOwningThread(); 871 MOZ_ASSERT(!aActor || !mDatabaseActor); 872 873 mDatabaseActor = aActor; 874 } 875 876 void BackgroundFactoryRequestChild::HandleResponse(nsresult aResponse) { 877 AssertIsOnOwningThread(); 878 MOZ_ASSERT(NS_FAILED(aResponse)); 879 MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); 880 881 mRequest->Reset(); 882 883 DispatchErrorEvent(mRequest, aResponse); 884 885 if (mDatabaseActor) { 886 mDatabaseActor->ReleaseDOMObject(); 887 MOZ_ASSERT(!mDatabaseActor); 888 } 889 } 890 891 void BackgroundFactoryRequestChild::HandleResponse( 892 const OpenDatabaseRequestResponse& aResponse) { 893 AssertIsOnOwningThread(); 894 895 mRequest->Reset(); 896 897 auto* databaseActor = static_cast<BackgroundDatabaseChild*>( 898 aResponse.database().AsChild().get()); 899 MOZ_ASSERT(databaseActor); 900 901 IDBDatabase* const database = [this, databaseActor]() -> IDBDatabase* { 902 IDBDatabase* database = databaseActor->GetDOMObject(); 903 if (!database) { 904 (void)this; 905 906 if (NS_WARN_IF(!databaseActor->EnsureDOMObject())) { 907 return nullptr; 908 } 909 MOZ_ASSERT(mDatabaseActor); 910 911 database = databaseActor->GetDOMObject(); 912 MOZ_ASSERT(database); 913 } 914 915 return database; 916 }(); 917 918 if (!database || database->IsClosed()) { 919 // If the database was closed already, which is only possible if we fired an 920 // "upgradeneeded" event, then we shouldn't fire a "success" event here. 921 // Instead we fire an error event with AbortErr. 922 DispatchErrorEvent(mRequest, NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); 923 } else { 924 SetResultAndDispatchSuccessEvent(mRequest, nullptr, *database); 925 } 926 927 if (database) { 928 MOZ_ASSERT(mDatabaseActor == databaseActor); 929 930 databaseActor->ReleaseDOMObject(); 931 } else { 932 databaseActor->SendDeleteMeInternal(); 933 } 934 MOZ_ASSERT(!mDatabaseActor); 935 } 936 937 void BackgroundFactoryRequestChild::HandleResponse( 938 const DeleteDatabaseRequestResponse& aResponse) { 939 AssertIsOnOwningThread(); 940 941 RefPtr<Event> successEvent = IDBVersionChangeEvent::Create( 942 mRequest.get(), nsDependentString(kSuccessEventType), 943 aResponse.previousVersion()); 944 MOZ_ASSERT(successEvent); 945 946 SetResultAndDispatchSuccessEvent(mRequest, nullptr, JS::UndefinedHandleValue, 947 std::move(successEvent)); 948 949 MOZ_ASSERT(!mDatabaseActor); 950 } 951 952 void BackgroundFactoryRequestChild::ActorDestroy(ActorDestroyReason aWhy) { 953 AssertIsOnOwningThread(); 954 955 MaybeCollectGarbageOnIPCMessage(); 956 957 if (aWhy != Deletion) { 958 GetOpenDBRequest()->NoteComplete(); 959 } 960 } 961 962 mozilla::ipc::IPCResult BackgroundFactoryRequestChild::Recv__delete__( 963 const FactoryRequestResponse& aResponse) { 964 AssertIsOnOwningThread(); 965 966 MaybeCollectGarbageOnIPCMessage(); 967 968 switch (aResponse.type()) { 969 case FactoryRequestResponse::Tnsresult: 970 HandleResponse(aResponse.get_nsresult()); 971 break; 972 973 case FactoryRequestResponse::TOpenDatabaseRequestResponse: 974 HandleResponse(aResponse.get_OpenDatabaseRequestResponse()); 975 break; 976 977 case FactoryRequestResponse::TDeleteDatabaseRequestResponse: 978 HandleResponse(aResponse.get_DeleteDatabaseRequestResponse()); 979 break; 980 981 default: 982 return IPC_FAIL(this, "Unknown response type!"); 983 } 984 985 auto request = GetOpenDBRequest(); 986 request->NoteComplete(); 987 988 return IPC_OK(); 989 } 990 991 mozilla::ipc::IPCResult BackgroundFactoryRequestChild::RecvBlocked( 992 const uint64_t aCurrentVersion) { 993 AssertIsOnOwningThread(); 994 995 MaybeCollectGarbageOnIPCMessage(); 996 997 const nsDependentString type(kBlockedEventType); 998 999 RefPtr<Event> blockedEvent; 1000 if (mIsDeleteOp) { 1001 blockedEvent = 1002 IDBVersionChangeEvent::Create(mRequest.get(), type, aCurrentVersion); 1003 MOZ_ASSERT(blockedEvent); 1004 } else { 1005 blockedEvent = IDBVersionChangeEvent::Create( 1006 mRequest.get(), type, aCurrentVersion, mRequestedVersion); 1007 MOZ_ASSERT(blockedEvent); 1008 } 1009 1010 RefPtr<IDBRequest> kungFuDeathGrip = mRequest; 1011 1012 IDB_LOG_MARK_CHILD_REQUEST("Firing \"blocked\" event", "\"blocked\"", 1013 kungFuDeathGrip->LoggingSerialNumber()); 1014 1015 IgnoredErrorResult rv; 1016 kungFuDeathGrip->DispatchEvent(*blockedEvent, rv); 1017 if (rv.Failed()) { 1018 NS_WARNING("Failed to dispatch event!"); 1019 } 1020 1021 return IPC_OK(); 1022 } 1023 1024 /******************************************************************************* 1025 * BackgroundDatabaseChild 1026 ******************************************************************************/ 1027 1028 BackgroundDatabaseChild::BackgroundDatabaseChild( 1029 const DatabaseSpec& aSpec, BackgroundFactoryRequestChild* aOpenRequestActor) 1030 : mSpec(MakeUnique<DatabaseSpec>(aSpec)), 1031 mOpenRequestActor(aOpenRequestActor), 1032 mDatabase(nullptr), 1033 mPendingInvalidate(false) { 1034 // Can't assert owning thread here because IPDL has not yet set our manager! 1035 MOZ_ASSERT(aOpenRequestActor); 1036 1037 MOZ_COUNT_CTOR(indexedDB::BackgroundDatabaseChild); 1038 } 1039 1040 BackgroundDatabaseChild::~BackgroundDatabaseChild() { 1041 MOZ_COUNT_DTOR(indexedDB::BackgroundDatabaseChild); 1042 } 1043 1044 #ifdef DEBUG 1045 1046 void BackgroundDatabaseChild::AssertIsOnOwningThread() const { 1047 static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread(); 1048 } 1049 1050 #endif // DEBUG 1051 1052 void BackgroundDatabaseChild::SendDeleteMeInternal() { 1053 AssertIsOnOwningThread(); 1054 MOZ_ASSERT(!mTemporaryStrongDatabase); 1055 MOZ_ASSERT(!mOpenRequestActor); 1056 1057 if (mDatabase) { 1058 mDatabase->ClearBackgroundActor(); 1059 mDatabase = nullptr; 1060 1061 MOZ_ALWAYS_TRUE(PBackgroundIDBDatabaseChild::SendDeleteMe()); 1062 } 1063 } 1064 1065 bool BackgroundDatabaseChild::EnsureDOMObject() { 1066 AssertIsOnOwningThread(); 1067 MOZ_ASSERT(mOpenRequestActor); 1068 1069 if (mTemporaryStrongDatabase) { 1070 MOZ_ASSERT(!mSpec); 1071 MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase); 1072 return true; 1073 } 1074 1075 MOZ_ASSERT(mSpec); 1076 1077 const auto request = mOpenRequestActor->GetOpenDBRequest(); 1078 1079 auto& factory = 1080 static_cast<BackgroundFactoryChild*>(Manager())->GetDOMObject(); 1081 1082 if (!factory.GetOwnerGlobal()) { 1083 // Already disconnected from global. 1084 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; 1089 1090 return false; 1091 } 1092 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)); 1099 1100 MOZ_ASSERT(mTemporaryStrongDatabase); 1101 mTemporaryStrongDatabase->AssertIsOnOwningThread(); 1102 1103 mDatabase = mTemporaryStrongDatabase; 1104 1105 if (mPendingInvalidate) { 1106 mDatabase->Invalidate(); 1107 mPendingInvalidate = false; 1108 } 1109 1110 mOpenRequestActor->SetDatabaseActor(this); 1111 1112 return true; 1113 } 1114 1115 void BackgroundDatabaseChild::ReleaseDOMObject() { 1116 AssertIsOnOwningThread(); 1117 MOZ_ASSERT(mTemporaryStrongDatabase); 1118 mTemporaryStrongDatabase->AssertIsOnOwningThread(); 1119 MOZ_ASSERT(mOpenRequestActor); 1120 MOZ_ASSERT(mDatabase == mTemporaryStrongDatabase); 1121 1122 mOpenRequestActor->SetDatabaseActor(nullptr); 1123 1124 mOpenRequestActor = nullptr; 1125 1126 // This may be the final reference to the IDBDatabase object so we may end up 1127 // calling SendDeleteMeInternal() here. Make sure everything is cleaned up 1128 // properly before proceeding. 1129 mTemporaryStrongDatabase = nullptr; 1130 1131 // XXX Why isn't mDatabase set to nullptr here? 1132 } 1133 1134 void BackgroundDatabaseChild::ActorDestroy(ActorDestroyReason aWhy) { 1135 AssertIsOnOwningThread(); 1136 1137 MaybeCollectGarbageOnIPCMessage(); 1138 1139 if (mDatabase) { 1140 mDatabase->ClearBackgroundActor(); 1141 #ifdef DEBUG 1142 mDatabase = nullptr; 1143 #endif 1144 } 1145 } 1146 1147 PBackgroundIDBDatabaseFileChild* 1148 BackgroundDatabaseChild::AllocPBackgroundIDBDatabaseFileChild( 1149 const IPCBlob& aIPCBlob) { 1150 MOZ_CRASH("PBackgroundIDBFileChild actors should be manually constructed!"); 1151 } 1152 1153 bool BackgroundDatabaseChild::DeallocPBackgroundIDBDatabaseFileChild( 1154 PBackgroundIDBDatabaseFileChild* aActor) const { 1155 AssertIsOnOwningThread(); 1156 MOZ_ASSERT(aActor); 1157 1158 delete aActor; 1159 return true; 1160 } 1161 1162 already_AddRefed<PBackgroundIDBVersionChangeTransactionChild> 1163 BackgroundDatabaseChild::AllocPBackgroundIDBVersionChangeTransactionChild( 1164 const uint64_t aCurrentVersion, const uint64_t aRequestedVersion, 1165 const int64_t aNextObjectStoreId, const int64_t aNextIndexId) { 1166 AssertIsOnOwningThread(); 1167 1168 return RefPtr{new BackgroundVersionChangeTransactionChild( 1169 mOpenRequestActor->GetOpenDBRequest())} 1170 .forget(); 1171 } 1172 1173 mozilla::ipc::IPCResult 1174 BackgroundDatabaseChild::RecvPBackgroundIDBVersionChangeTransactionConstructor( 1175 PBackgroundIDBVersionChangeTransactionChild* aActor, 1176 const uint64_t& aCurrentVersion, const uint64_t& aRequestedVersion, 1177 const int64_t& aNextObjectStoreId, const int64_t& aNextIndexId) { 1178 AssertIsOnOwningThread(); 1179 MOZ_ASSERT(aActor); 1180 MOZ_ASSERT(mOpenRequestActor); 1181 1182 MaybeCollectGarbageOnIPCMessage(); 1183 1184 auto* const actor = 1185 static_cast<BackgroundVersionChangeTransactionChild*>(aActor); 1186 1187 if (!EnsureDOMObject()) { 1188 NS_WARNING("Factory is already disconnected from global"); 1189 1190 actor->SendDeleteMeInternal(true); 1191 1192 // XXX This is a hack to ensure that transaction/request serial numbers stay 1193 // in sync between parent and child. Actually, it might be better to create 1194 // an IDBTransaction in the child and abort that. 1195 (void)mozilla::ipc::BackgroundChildImpl::GetThreadLocalForCurrentThread() 1196 ->mIndexedDBThreadLocal->NextTransactionSN( 1197 IDBTransaction::Mode::VersionChange); 1198 (void)IDBRequest::NextSerialNumber(); 1199 1200 // No reason to IPC_FAIL here. 1201 return IPC_OK(); 1202 } 1203 1204 MOZ_ASSERT(!mDatabase->IsInvalidated()); 1205 1206 // XXX NotNull might encapsulate this 1207 const auto request = 1208 WrapNotNullUnchecked(RefPtr{mOpenRequestActor->GetOpenDBRequest().get()}); 1209 1210 SafeRefPtr<IDBTransaction> transaction = IDBTransaction::CreateVersionChange( 1211 mDatabase, actor, request, aNextObjectStoreId, aNextIndexId); 1212 MOZ_ASSERT(transaction); 1213 1214 transaction->AssertIsOnOwningThread(); 1215 1216 actor->SetDOMTransaction(transaction.clonePtr()); 1217 1218 const auto database = WrapNotNull(mDatabase); 1219 1220 database->EnterSetVersionTransaction(aRequestedVersion); 1221 1222 request->SetTransaction(transaction.clonePtr()); 1223 1224 RefPtr<Event> upgradeNeededEvent = IDBVersionChangeEvent::Create( 1225 request.get(), nsDependentString(kUpgradeNeededEventType), 1226 aCurrentVersion, aRequestedVersion); 1227 MOZ_ASSERT(upgradeNeededEvent); 1228 1229 SetResultAndDispatchSuccessEvent( 1230 WrapNotNullUnchecked<RefPtr<IDBRequest>>(request.get()), transaction, 1231 *database, std::move(upgradeNeededEvent)); 1232 1233 return IPC_OK(); 1234 } 1235 1236 mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvVersionChange( 1237 const uint64_t aOldVersion, const Maybe<uint64_t> aNewVersion) { 1238 AssertIsOnOwningThread(); 1239 1240 MaybeCollectGarbageOnIPCMessage(); 1241 1242 if (!mDatabase || mDatabase->IsClosed()) { 1243 return IPC_OK(); 1244 } 1245 1246 RefPtr<IDBDatabase> kungFuDeathGrip = mDatabase; 1247 1248 // Handle bfcache'd windows. 1249 if (nsGlobalWindowInner* owner = kungFuDeathGrip->GetOwnerWindow()) { 1250 // The database must be closed if the window is already frozen. 1251 bool shouldAbortAndClose = owner->IsFrozen(); 1252 1253 // Anything in the bfcache has to be evicted and then we have to close the 1254 // database also. 1255 if (owner->RemoveFromBFCacheSync()) { 1256 shouldAbortAndClose = true; 1257 } 1258 1259 if (shouldAbortAndClose) { 1260 // Invalidate() doesn't close the database in the parent, so we have 1261 // to call Close() and AbortTransactions() manually. 1262 kungFuDeathGrip->AbortTransactions(/* aShouldWarn */ false); 1263 kungFuDeathGrip->Close(); 1264 return IPC_OK(); 1265 } 1266 } 1267 1268 // Otherwise fire a versionchange event. 1269 const nsDependentString type(kVersionChangeEventType); 1270 1271 RefPtr<Event> versionChangeEvent; 1272 1273 if (aNewVersion.isNothing()) { 1274 versionChangeEvent = 1275 IDBVersionChangeEvent::Create(kungFuDeathGrip, type, aOldVersion); 1276 MOZ_ASSERT(versionChangeEvent); 1277 } else { 1278 versionChangeEvent = IDBVersionChangeEvent::Create( 1279 kungFuDeathGrip, type, aOldVersion, aNewVersion.value()); 1280 MOZ_ASSERT(versionChangeEvent); 1281 } 1282 1283 IDB_LOG_MARK("Child : Firing \"versionchange\" event", 1284 "C: IDBDatabase \"versionchange\" event", IDB_LOG_ID_STRING()); 1285 1286 IgnoredErrorResult rv; 1287 kungFuDeathGrip->DispatchEvent(*versionChangeEvent, rv); 1288 if (rv.Failed()) { 1289 NS_WARNING("Failed to dispatch event!"); 1290 } 1291 1292 if (!kungFuDeathGrip->IsClosed()) { 1293 SendBlocked(); 1294 } 1295 1296 return IPC_OK(); 1297 } 1298 1299 mozilla::ipc::IPCResult BackgroundDatabaseChild::RecvInvalidate() { 1300 AssertIsOnOwningThread(); 1301 1302 MaybeCollectGarbageOnIPCMessage(); 1303 1304 if (mDatabase) { 1305 mDatabase->Invalidate(); 1306 } else { 1307 mPendingInvalidate = true; 1308 } 1309 1310 return IPC_OK(); 1311 } 1312 1313 mozilla::ipc::IPCResult 1314 BackgroundDatabaseChild::RecvCloseAfterInvalidationComplete() { 1315 AssertIsOnOwningThread(); 1316 1317 MaybeCollectGarbageOnIPCMessage(); 1318 1319 if (mDatabase) { 1320 mDatabase->DispatchTrustedEvent(nsDependentString(kCloseEventType)); 1321 } 1322 1323 return IPC_OK(); 1324 } 1325 1326 /******************************************************************************* 1327 * BackgroundTransactionBase 1328 ******************************************************************************/ 1329 1330 BackgroundTransactionBase::BackgroundTransactionBase( 1331 SafeRefPtr<IDBTransaction> aTransaction) 1332 : mTemporaryStrongTransaction(std::move(aTransaction)), 1333 mTransaction(mTemporaryStrongTransaction.unsafeGetRawPtr()) { 1334 MOZ_ASSERT(mTransaction); 1335 mTransaction->AssertIsOnOwningThread(); 1336 1337 MOZ_COUNT_CTOR(BackgroundTransactionBase); 1338 } 1339 1340 #ifdef DEBUG 1341 1342 void BackgroundTransactionBase::AssertIsOnOwningThread() const { 1343 MOZ_ASSERT(mTransaction); 1344 mTransaction->AssertIsOnOwningThread(); 1345 } 1346 1347 #endif // DEBUG 1348 1349 void BackgroundTransactionBase::NoteActorDestroyed() { 1350 AssertIsOnOwningThread(); 1351 MOZ_ASSERT_IF(mTemporaryStrongTransaction, mTransaction); 1352 1353 if (mTransaction) { 1354 mTransaction->ClearBackgroundActor(); 1355 1356 // Normally this would be DEBUG-only but NoteActorDestroyed is also called 1357 // from SendDeleteMeInternal. In that case we're going to receive an actual 1358 // ActorDestroy call later and we don't want to touch a dead object. 1359 mTemporaryStrongTransaction = nullptr; 1360 mTransaction = nullptr; 1361 } 1362 } 1363 1364 void BackgroundTransactionBase::SetDOMTransaction( 1365 SafeRefPtr<IDBTransaction> aTransaction) { 1366 AssertIsOnOwningThread(); 1367 MOZ_ASSERT(aTransaction); 1368 aTransaction->AssertIsOnOwningThread(); 1369 MOZ_ASSERT(!mTemporaryStrongTransaction); 1370 MOZ_ASSERT(!mTransaction); 1371 1372 mTemporaryStrongTransaction = std::move(aTransaction); 1373 mTransaction = mTemporaryStrongTransaction.unsafeGetRawPtr(); 1374 } 1375 1376 void BackgroundTransactionBase::NoteComplete() { 1377 AssertIsOnOwningThread(); 1378 MOZ_ASSERT_IF(mTransaction, mTemporaryStrongTransaction); 1379 1380 mTemporaryStrongTransaction = nullptr; 1381 } 1382 1383 /******************************************************************************* 1384 * BackgroundTransactionChild 1385 ******************************************************************************/ 1386 1387 BackgroundTransactionChild::BackgroundTransactionChild( 1388 SafeRefPtr<IDBTransaction> aTransaction) 1389 : BackgroundTransactionBase(std::move(aTransaction)) { 1390 MOZ_COUNT_CTOR(indexedDB::BackgroundTransactionChild); 1391 } 1392 1393 BackgroundTransactionChild::~BackgroundTransactionChild() { 1394 MOZ_COUNT_DTOR(indexedDB::BackgroundTransactionChild); 1395 } 1396 1397 #ifdef DEBUG 1398 1399 void BackgroundTransactionChild::AssertIsOnOwningThread() const { 1400 static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread(); 1401 } 1402 1403 #endif // DEBUG 1404 1405 void BackgroundTransactionChild::SendDeleteMeInternal() { 1406 AssertIsOnOwningThread(); 1407 1408 if (mTransaction) { 1409 NoteActorDestroyed(); 1410 1411 MOZ_ALWAYS_TRUE(PBackgroundIDBTransactionChild::SendDeleteMe()); 1412 } 1413 } 1414 1415 void BackgroundTransactionChild::ActorDestroy(ActorDestroyReason aWhy) { 1416 AssertIsOnOwningThread(); 1417 1418 MaybeCollectGarbageOnIPCMessage(); 1419 1420 NoteActorDestroyed(); 1421 } 1422 1423 mozilla::ipc::IPCResult BackgroundTransactionChild::RecvComplete( 1424 const nsresult aResult) { 1425 AssertIsOnOwningThread(); 1426 MOZ_ASSERT(mTransaction); 1427 1428 MaybeCollectGarbageOnIPCMessage(); 1429 1430 mTransaction->FireCompleteOrAbortEvents(aResult); 1431 1432 NoteComplete(); 1433 return IPC_OK(); 1434 } 1435 1436 PBackgroundIDBRequestChild* 1437 BackgroundTransactionChild::AllocPBackgroundIDBRequestChild( 1438 const int64_t& aRequestId, const RequestParams& aParams) { 1439 MOZ_CRASH( 1440 "PBackgroundIDBRequestChild actors should be manually " 1441 "constructed!"); 1442 } 1443 1444 bool BackgroundTransactionChild::DeallocPBackgroundIDBRequestChild( 1445 PBackgroundIDBRequestChild* aActor) { 1446 MOZ_ASSERT(aActor); 1447 1448 delete static_cast<BackgroundRequestChild*>(aActor); 1449 return true; 1450 } 1451 1452 PBackgroundIDBCursorChild* 1453 BackgroundTransactionChild::AllocPBackgroundIDBCursorChild( 1454 const int64_t& aRequestId, const OpenCursorParams& aParams) { 1455 AssertIsOnOwningThread(); 1456 1457 MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!"); 1458 } 1459 1460 bool BackgroundTransactionChild::DeallocPBackgroundIDBCursorChild( 1461 PBackgroundIDBCursorChild* aActor) { 1462 MOZ_ASSERT(aActor); 1463 1464 delete aActor; 1465 return true; 1466 } 1467 1468 /******************************************************************************* 1469 * BackgroundVersionChangeTransactionChild 1470 ******************************************************************************/ 1471 1472 BackgroundVersionChangeTransactionChild:: 1473 BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest) 1474 : mOpenDBRequest(aOpenDBRequest) { 1475 MOZ_ASSERT(aOpenDBRequest); 1476 aOpenDBRequest->AssertIsOnOwningThread(); 1477 1478 MOZ_COUNT_CTOR(indexedDB::BackgroundVersionChangeTransactionChild); 1479 } 1480 1481 BackgroundVersionChangeTransactionChild:: 1482 ~BackgroundVersionChangeTransactionChild() { 1483 AssertIsOnOwningThread(); 1484 1485 MOZ_COUNT_DTOR(indexedDB::BackgroundVersionChangeTransactionChild); 1486 } 1487 1488 #ifdef DEBUG 1489 1490 void BackgroundVersionChangeTransactionChild::AssertIsOnOwningThread() const { 1491 static_cast<BackgroundDatabaseChild*>(Manager())->AssertIsOnOwningThread(); 1492 } 1493 1494 #endif // DEBUG 1495 1496 void BackgroundVersionChangeTransactionChild::SendDeleteMeInternal( 1497 bool aFailedConstructor) { 1498 AssertIsOnOwningThread(); 1499 1500 if (mTransaction || aFailedConstructor) { 1501 NoteActorDestroyed(); 1502 1503 MOZ_ALWAYS_TRUE( 1504 PBackgroundIDBVersionChangeTransactionChild::SendDeleteMe()); 1505 } 1506 } 1507 1508 void BackgroundVersionChangeTransactionChild::ActorDestroy( 1509 ActorDestroyReason aWhy) { 1510 AssertIsOnOwningThread(); 1511 1512 MaybeCollectGarbageOnIPCMessage(); 1513 1514 mOpenDBRequest = nullptr; 1515 1516 NoteActorDestroyed(); 1517 } 1518 1519 mozilla::ipc::IPCResult BackgroundVersionChangeTransactionChild::RecvComplete( 1520 const nsresult aResult) { 1521 AssertIsOnOwningThread(); 1522 1523 MaybeCollectGarbageOnIPCMessage(); 1524 1525 if (!mTransaction) { 1526 return IPC_OK(); 1527 } 1528 1529 MOZ_ASSERT(mOpenDBRequest); 1530 1531 IDBDatabase* database = mTransaction->Database(); 1532 MOZ_ASSERT(database); 1533 1534 database->ExitSetVersionTransaction(); 1535 1536 if (NS_FAILED(aResult)) { 1537 database->Close(); 1538 } 1539 1540 RefPtr<IDBOpenDBRequest> request = mOpenDBRequest; 1541 MOZ_ASSERT(request); 1542 1543 mTransaction->FireCompleteOrAbortEvents(aResult); 1544 1545 request->SetTransaction(nullptr); 1546 request = nullptr; 1547 1548 mOpenDBRequest = nullptr; 1549 1550 NoteComplete(); 1551 return IPC_OK(); 1552 } 1553 1554 PBackgroundIDBRequestChild* 1555 BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild( 1556 const int64_t& aRequestId, const RequestParams& aParams) { 1557 MOZ_CRASH( 1558 "PBackgroundIDBRequestChild actors should be manually " 1559 "constructed!"); 1560 } 1561 1562 bool BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBRequestChild( 1563 PBackgroundIDBRequestChild* aActor) { 1564 MOZ_ASSERT(aActor); 1565 1566 delete static_cast<BackgroundRequestChild*>(aActor); 1567 return true; 1568 } 1569 1570 PBackgroundIDBCursorChild* 1571 BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBCursorChild( 1572 const int64_t& aRequestId, const OpenCursorParams& aParams) { 1573 AssertIsOnOwningThread(); 1574 1575 MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!"); 1576 } 1577 1578 bool BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBCursorChild( 1579 PBackgroundIDBCursorChild* aActor) { 1580 MOZ_ASSERT(aActor); 1581 1582 delete aActor; 1583 return true; 1584 } 1585 1586 /******************************************************************************* 1587 * BackgroundRequestChild 1588 ******************************************************************************/ 1589 1590 BackgroundRequestChild::BackgroundRequestChild( 1591 MovingNotNull<RefPtr<IDBRequest>> aRequest) 1592 : BackgroundRequestChildBase(std::move(aRequest)), 1593 mTransaction(mRequest->AcquireTransaction()), 1594 mRunningPreprocessHelpers(0), 1595 mCurrentCloneDataIndex(0), 1596 mPreprocessResultCode(NS_OK), 1597 mGetAll(false) { 1598 MOZ_ASSERT(mTransaction); 1599 mTransaction->AssertIsOnOwningThread(); 1600 1601 MOZ_COUNT_CTOR(indexedDB::BackgroundRequestChild); 1602 } 1603 1604 BackgroundRequestChild::~BackgroundRequestChild() { 1605 AssertIsOnOwningThread(); 1606 MOZ_ASSERT(!mTransaction); 1607 1608 MOZ_COUNT_DTOR(indexedDB::BackgroundRequestChild); 1609 } 1610 1611 void BackgroundRequestChild::MaybeSendContinue() { 1612 AssertIsOnOwningThread(); 1613 MOZ_ASSERT(mRunningPreprocessHelpers > 0); 1614 1615 if (--mRunningPreprocessHelpers == 0) { 1616 PreprocessResponse response; 1617 1618 if (NS_SUCCEEDED(mPreprocessResultCode)) { 1619 if (mGetAll) { 1620 response = ObjectStoreGetAllPreprocessResponse(); 1621 } else { 1622 response = ObjectStoreGetPreprocessResponse(); 1623 } 1624 } else { 1625 response = mPreprocessResultCode; 1626 } 1627 1628 MOZ_ALWAYS_TRUE(SendContinue(response)); 1629 } 1630 } 1631 1632 void BackgroundRequestChild::OnPreprocessFinished( 1633 uint32_t aCloneDataIndex, UniquePtr<JSStructuredCloneData> aCloneData) { 1634 AssertIsOnOwningThread(); 1635 MOZ_ASSERT(aCloneDataIndex < mCloneInfos.Length()); 1636 MOZ_ASSERT(aCloneData); 1637 1638 auto& cloneInfo = mCloneInfos[aCloneDataIndex]; 1639 MOZ_ASSERT(cloneInfo.mPreprocessHelper); 1640 MOZ_ASSERT(!cloneInfo.mCloneData); 1641 1642 cloneInfo.mCloneData = std::move(aCloneData); 1643 1644 MaybeSendContinue(); 1645 1646 cloneInfo.mPreprocessHelper = nullptr; 1647 } 1648 1649 void BackgroundRequestChild::OnPreprocessFailed(uint32_t aCloneDataIndex, 1650 nsresult aErrorCode) { 1651 AssertIsOnOwningThread(); 1652 MOZ_ASSERT(aCloneDataIndex < mCloneInfos.Length()); 1653 MOZ_ASSERT(NS_FAILED(aErrorCode)); 1654 1655 auto& cloneInfo = mCloneInfos[aCloneDataIndex]; 1656 MOZ_ASSERT(cloneInfo.mPreprocessHelper); 1657 MOZ_ASSERT(!cloneInfo.mCloneData); 1658 1659 if (NS_SUCCEEDED(mPreprocessResultCode)) { 1660 mPreprocessResultCode = aErrorCode; 1661 } 1662 1663 MaybeSendContinue(); 1664 1665 cloneInfo.mPreprocessHelper = nullptr; 1666 } 1667 1668 UniquePtr<JSStructuredCloneData> BackgroundRequestChild::GetNextCloneData() { 1669 AssertIsOnOwningThread(); 1670 MOZ_ASSERT(mCurrentCloneDataIndex < mCloneInfos.Length()); 1671 MOZ_ASSERT(mCloneInfos[mCurrentCloneDataIndex].mCloneData); 1672 1673 return std::move(mCloneInfos[mCurrentCloneDataIndex++].mCloneData); 1674 } 1675 1676 void BackgroundRequestChild::HandleResponse(nsresult aResponse) { 1677 AssertIsOnOwningThread(); 1678 MOZ_ASSERT(NS_FAILED(aResponse)); 1679 MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); 1680 MOZ_ASSERT(mTransaction); 1681 1682 DispatchErrorEvent(mRequest, aResponse, mTransaction.clonePtr()); 1683 } 1684 1685 void BackgroundRequestChild::HandleResponse(const Key& aResponse) { 1686 AssertIsOnOwningThread(); 1687 1688 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse); 1689 } 1690 1691 void BackgroundRequestChild::HandleResponse(const nsTArray<Key>& aResponse) { 1692 AssertIsOnOwningThread(); 1693 1694 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse); 1695 } 1696 1697 void BackgroundRequestChild::HandleResponse( 1698 SerializedStructuredCloneReadInfo&& aResponse) { 1699 AssertIsOnOwningThread(); 1700 1701 if (!mTransaction->Database()->GetOwnerGlobal()) { 1702 // Ignore the response, since we have already been disconnected from the 1703 // global. 1704 return; 1705 } 1706 1707 auto cloneReadInfo = DeserializeStructuredCloneReadInfo( 1708 std::move(aResponse), mTransaction->Database(), 1709 [this] { return std::move(*GetNextCloneData()); }); 1710 1711 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), 1712 cloneReadInfo); 1713 } 1714 1715 void BackgroundRequestChild::HandleResponse( 1716 nsTArray<SerializedStructuredCloneReadInfo>&& aResponse) { 1717 AssertIsOnOwningThread(); 1718 1719 if (!mTransaction->Database()->GetOwnerGlobal()) { 1720 // Ignore the response, since we have already been disconnected from the 1721 // global. 1722 return; 1723 } 1724 1725 nsTArray<StructuredCloneReadInfoChild> cloneReadInfos; 1726 1727 QM_TRY(OkIf(cloneReadInfos.SetCapacity(aResponse.Length(), fallible)), 1728 QM_VOID, ([&aResponse, this](const auto) { 1729 // Since we are under memory pressure, release aResponse early. 1730 aResponse.Clear(); 1731 1732 DispatchErrorEvent(mRequest, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, 1733 AcquireTransaction()); 1734 1735 MOZ_ASSERT(mTransaction->IsAborted()); 1736 })); 1737 1738 std::transform(std::make_move_iterator(aResponse.begin()), 1739 std::make_move_iterator(aResponse.end()), 1740 MakeBackInserter(cloneReadInfos), 1741 [database = mTransaction->Database(), this]( 1742 SerializedStructuredCloneReadInfo&& serializedCloneInfo) { 1743 return DeserializeStructuredCloneReadInfo( 1744 std::move(serializedCloneInfo), database, 1745 [this] { return std::move(*GetNextCloneData()); }); 1746 }); 1747 1748 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), 1749 cloneReadInfos); 1750 } 1751 1752 void BackgroundRequestChild::HandleResponse(JS::Handle<JS::Value> aResponse) { 1753 AssertIsOnOwningThread(); 1754 1755 SetResultAndDispatchSuccessEvent( 1756 mRequest, AcquireTransaction(), 1757 const_cast<const JS::Handle<JS::Value>&>(aResponse)); 1758 } 1759 1760 void BackgroundRequestChild::HandleResponse(const uint64_t aResponse) { 1761 AssertIsOnOwningThread(); 1762 1763 SetResultAndDispatchSuccessEvent(mRequest, AcquireTransaction(), aResponse); 1764 } 1765 1766 nsresult BackgroundRequestChild::HandlePreprocess( 1767 const PreprocessInfo& aPreprocessInfo) { 1768 return HandlePreprocessInternal( 1769 AutoTArray<PreprocessInfo, 1>{aPreprocessInfo}); 1770 } 1771 1772 nsresult BackgroundRequestChild::HandlePreprocess( 1773 const nsTArray<PreprocessInfo>& aPreprocessInfos) { 1774 AssertIsOnOwningThread(); 1775 mGetAll = true; 1776 1777 return HandlePreprocessInternal(aPreprocessInfos); 1778 } 1779 1780 nsresult BackgroundRequestChild::HandlePreprocessInternal( 1781 const nsTArray<PreprocessInfo>& aPreprocessInfos) { 1782 AssertIsOnOwningThread(); 1783 1784 IDBDatabase* database = mTransaction->Database(); 1785 1786 const uint32_t count = aPreprocessInfos.Length(); 1787 1788 mCloneInfos.SetLength(count); 1789 1790 // TODO: Since we use the stream transport service, this can spawn 25 threads 1791 // and has the potential to cause some annoying browser hiccups. 1792 // Consider using a single thread or a very small threadpool. 1793 for (uint32_t index = 0; index < count; index++) { 1794 const PreprocessInfo& preprocessInfo = aPreprocessInfos[index]; 1795 1796 const auto files = 1797 DeserializeStructuredCloneFiles(database, preprocessInfo.files(), 1798 /* aForPreprocess */ true); 1799 1800 MOZ_ASSERT(files.Length() == 1); 1801 1802 auto& preprocessHelper = mCloneInfos[index].mPreprocessHelper; 1803 preprocessHelper = MakeRefPtr<PreprocessHelper>(index, this); 1804 1805 nsresult rv = preprocessHelper->Init(files[0]); 1806 if (NS_WARN_IF(NS_FAILED(rv))) { 1807 return rv; 1808 } 1809 1810 rv = preprocessHelper->Dispatch(); 1811 if (NS_WARN_IF(NS_FAILED(rv))) { 1812 return rv; 1813 } 1814 1815 mRunningPreprocessHelpers++; 1816 } 1817 1818 return NS_OK; 1819 } 1820 1821 void BackgroundRequestChild::ActorDestroy(ActorDestroyReason aWhy) { 1822 AssertIsOnOwningThread(); 1823 1824 MaybeCollectGarbageOnIPCMessage(); 1825 1826 for (auto& cloneInfo : mCloneInfos) { 1827 const auto& preprocessHelper = cloneInfo.mPreprocessHelper; 1828 1829 if (preprocessHelper) { 1830 preprocessHelper->ClearActor(); 1831 } 1832 } 1833 mCloneInfos.Clear(); 1834 1835 if (mTransaction) { 1836 mTransaction->AssertIsOnOwningThread(); 1837 1838 mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ 1839 aWhy == Deletion); 1840 #ifdef DEBUG 1841 mTransaction = nullptr; 1842 #endif 1843 } 1844 } 1845 1846 mozilla::ipc::IPCResult BackgroundRequestChild::Recv__delete__( 1847 RequestResponse&& aResponse) { 1848 AssertIsOnOwningThread(); 1849 MOZ_ASSERT(mTransaction); 1850 1851 MaybeCollectGarbageOnIPCMessage(); 1852 1853 if (mTransaction->IsAborted()) { 1854 // Always fire an "error" event with ABORT_ERR if the transaction was 1855 // aborted, even if the request succeeded or failed with another error. 1856 HandleResponse(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR); 1857 } else { 1858 switch (aResponse.type()) { 1859 case RequestResponse::Tnsresult: 1860 HandleResponse(aResponse.get_nsresult()); 1861 break; 1862 1863 case RequestResponse::TObjectStoreAddResponse: 1864 HandleResponse(aResponse.get_ObjectStoreAddResponse().key()); 1865 break; 1866 1867 case RequestResponse::TObjectStorePutResponse: 1868 HandleResponse(aResponse.get_ObjectStorePutResponse().key()); 1869 break; 1870 1871 case RequestResponse::TObjectStoreGetResponse: 1872 HandleResponse( 1873 std::move(aResponse.get_ObjectStoreGetResponse().cloneInfo())); 1874 break; 1875 1876 case RequestResponse::TObjectStoreGetKeyResponse: 1877 HandleResponse(aResponse.get_ObjectStoreGetKeyResponse().key()); 1878 break; 1879 1880 case RequestResponse::TObjectStoreGetAllResponse: 1881 HandleResponse( 1882 std::move(aResponse.get_ObjectStoreGetAllResponse().cloneInfos())); 1883 break; 1884 1885 case RequestResponse::TObjectStoreGetAllKeysResponse: 1886 HandleResponse(aResponse.get_ObjectStoreGetAllKeysResponse().keys()); 1887 break; 1888 1889 case RequestResponse::TObjectStoreDeleteResponse: 1890 case RequestResponse::TObjectStoreClearResponse: 1891 HandleResponse(JS::UndefinedHandleValue); 1892 break; 1893 1894 case RequestResponse::TObjectStoreCountResponse: 1895 HandleResponse(aResponse.get_ObjectStoreCountResponse().count()); 1896 break; 1897 1898 case RequestResponse::TIndexGetResponse: 1899 HandleResponse(std::move(aResponse.get_IndexGetResponse().cloneInfo())); 1900 break; 1901 1902 case RequestResponse::TIndexGetKeyResponse: 1903 HandleResponse(aResponse.get_IndexGetKeyResponse().key()); 1904 break; 1905 1906 case RequestResponse::TIndexGetAllResponse: 1907 HandleResponse( 1908 std::move(aResponse.get_IndexGetAllResponse().cloneInfos())); 1909 break; 1910 1911 case RequestResponse::TIndexGetAllKeysResponse: 1912 HandleResponse(aResponse.get_IndexGetAllKeysResponse().keys()); 1913 break; 1914 1915 case RequestResponse::TIndexCountResponse: 1916 HandleResponse(aResponse.get_IndexCountResponse().count()); 1917 break; 1918 1919 default: 1920 return IPC_FAIL(this, "Unknown response type!"); 1921 } 1922 } 1923 1924 mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ true); 1925 1926 // Null this out so that we don't try to call OnRequestFinished() again in 1927 // ActorDestroy. 1928 mTransaction = nullptr; 1929 1930 return IPC_OK(); 1931 } 1932 1933 mozilla::ipc::IPCResult BackgroundRequestChild::RecvPreprocess( 1934 const PreprocessParams& aParams) { 1935 AssertIsOnOwningThread(); 1936 MOZ_ASSERT(mTransaction); 1937 1938 MaybeCollectGarbageOnIPCMessage(); 1939 1940 nsresult rv; 1941 1942 switch (aParams.type()) { 1943 case PreprocessParams::TObjectStoreGetPreprocessParams: { 1944 const auto& params = aParams.get_ObjectStoreGetPreprocessParams(); 1945 1946 rv = HandlePreprocess(params.preprocessInfo()); 1947 1948 break; 1949 } 1950 1951 case PreprocessParams::TObjectStoreGetAllPreprocessParams: { 1952 const auto& params = aParams.get_ObjectStoreGetAllPreprocessParams(); 1953 1954 rv = HandlePreprocess(params.preprocessInfos()); 1955 1956 break; 1957 } 1958 1959 default: 1960 return IPC_FAIL(this, "Unknown params type!"); 1961 } 1962 1963 if (NS_WARN_IF(NS_FAILED(rv))) { 1964 QM_WARNONLY_TRY(OkIf(SendContinue(rv))); 1965 } 1966 1967 return IPC_OK(); 1968 } 1969 1970 nsresult BackgroundRequestChild::PreprocessHelper::Init( 1971 const StructuredCloneFileChild& aFile) { 1972 AssertIsOnOwningThread(); 1973 MOZ_ASSERT(aFile.HasBlob()); 1974 MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eStructuredClone); 1975 MOZ_ASSERT(mState == State::Initial); 1976 1977 // The stream transport service is used for asynchronous processing. It has a 1978 // threadpool with a high cap of 25 threads. Fortunately, the service can be 1979 // used on workers too. 1980 nsCOMPtr<nsIEventTarget> target = 1981 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); 1982 MOZ_ASSERT(target); 1983 1984 // We use a TaskQueue here in order to be sure that the events are dispatched 1985 // in the correct order. This is not guaranteed in case we use the I/O thread 1986 // directly. 1987 mTaskQueue = TaskQueue::Create(target.forget(), "BackgroundRequestChild"); 1988 1989 ErrorResult errorResult; 1990 1991 nsCOMPtr<nsIInputStream> stream; 1992 // XXX After Bug 1620560, MutableBlob is not needed here anymore. 1993 aFile.MutableBlob().CreateInputStream(getter_AddRefs(stream), errorResult); 1994 if (NS_WARN_IF(errorResult.Failed())) { 1995 return errorResult.StealNSResult(); 1996 } 1997 1998 mStream = std::move(stream); 1999 2000 mCloneData = MakeUnique<JSStructuredCloneData>( 2001 JS::StructuredCloneScope::DifferentProcessForIndexedDB); 2002 2003 return NS_OK; 2004 } 2005 2006 nsresult BackgroundRequestChild::PreprocessHelper::Dispatch() { 2007 AssertIsOnOwningThread(); 2008 MOZ_ASSERT(mState == State::Initial); 2009 2010 nsresult rv = mTaskQueue->Dispatch(this, NS_DISPATCH_NORMAL); 2011 if (NS_WARN_IF(NS_FAILED(rv))) { 2012 return rv; 2013 } 2014 2015 return NS_OK; 2016 } 2017 2018 nsresult BackgroundRequestChild::PreprocessHelper::Start() { 2019 MOZ_ASSERT(!IsOnOwningThread()); 2020 MOZ_ASSERT(mStream); 2021 MOZ_ASSERT(mState == State::Initial); 2022 2023 nsresult rv; 2024 2025 PRFileDesc* fileDesc = GetFileDescriptorFromStream(mStream); 2026 if (fileDesc) { 2027 rv = ProcessStream(); 2028 if (NS_WARN_IF(NS_FAILED(rv))) { 2029 return rv; 2030 } 2031 2032 return NS_OK; 2033 } 2034 2035 mState = State::WaitingForStreamReady; 2036 2037 nsCOMPtr<nsIAsyncFileMetadata> asyncFileMetadata = do_QueryInterface(mStream); 2038 if (asyncFileMetadata) { 2039 rv = asyncFileMetadata->AsyncFileMetadataWait(this, mTaskQueue); 2040 if (NS_WARN_IF(NS_FAILED(rv))) { 2041 return rv; 2042 } 2043 2044 return NS_OK; 2045 } 2046 2047 nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mStream); 2048 if (!asyncStream) { 2049 return NS_ERROR_NO_INTERFACE; 2050 } 2051 2052 rv = asyncStream->AsyncWait(this, 0, 0, mTaskQueue); 2053 if (NS_WARN_IF(NS_FAILED(rv))) { 2054 return rv; 2055 } 2056 2057 return NS_OK; 2058 } 2059 2060 nsresult BackgroundRequestChild::PreprocessHelper::ProcessStream() { 2061 MOZ_ASSERT(!IsOnOwningThread()); 2062 MOZ_ASSERT(mStream); 2063 MOZ_ASSERT(mState == State::Initial || 2064 mState == State::WaitingForStreamReady); 2065 2066 // We need to get the internal stream (which is an nsFileInputStream) because 2067 // SnappyUncompressInputStream doesn't support reading from async input 2068 // streams. 2069 2070 nsCOMPtr<mozIRemoteLazyInputStream> blobInputStream = 2071 do_QueryInterface(mStream); 2072 MOZ_ASSERT(blobInputStream); 2073 2074 nsCOMPtr<nsIInputStream> internalInputStream; 2075 MOZ_ALWAYS_SUCCEEDS( 2076 blobInputStream->TakeInternalStream(getter_AddRefs(internalInputStream))); 2077 MOZ_ASSERT(internalInputStream); 2078 2079 QM_TRY(MOZ_TO_RESULT( 2080 SnappyUncompressStructuredCloneData(*internalInputStream, *mCloneData))); 2081 2082 mState = State::Finishing; 2083 2084 QM_TRY(MOZ_TO_RESULT(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL))); 2085 2086 return NS_OK; 2087 } 2088 2089 void BackgroundRequestChild::PreprocessHelper::Finish() { 2090 AssertIsOnOwningThread(); 2091 2092 if (mActor) { 2093 if (NS_SUCCEEDED(mResultCode)) { 2094 mActor->OnPreprocessFinished(mCloneDataIndex, std::move(mCloneData)); 2095 2096 MOZ_ASSERT(!mCloneData); 2097 } else { 2098 mActor->OnPreprocessFailed(mCloneDataIndex, mResultCode); 2099 } 2100 } 2101 2102 mState = State::Completed; 2103 } 2104 2105 NS_IMPL_ISUPPORTS_INHERITED(BackgroundRequestChild::PreprocessHelper, 2106 DiscardableRunnable, nsIInputStreamCallback, 2107 nsIFileMetadataCallback) 2108 2109 NS_IMETHODIMP 2110 BackgroundRequestChild::PreprocessHelper::Run() { 2111 nsresult rv; 2112 2113 switch (mState) { 2114 case State::Initial: 2115 rv = Start(); 2116 break; 2117 2118 case State::WaitingForStreamReady: 2119 rv = ProcessStream(); 2120 break; 2121 2122 case State::Finishing: 2123 Finish(); 2124 return NS_OK; 2125 2126 default: 2127 MOZ_CRASH("Bad state!"); 2128 } 2129 2130 if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) { 2131 if (NS_SUCCEEDED(mResultCode)) { 2132 mResultCode = rv; 2133 } 2134 2135 // Must set mState before dispatching otherwise we will race with the owning 2136 // thread. 2137 mState = State::Finishing; 2138 2139 if (IsOnOwningThread()) { 2140 Finish(); 2141 } else { 2142 MOZ_ALWAYS_SUCCEEDS( 2143 mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)); 2144 } 2145 } 2146 2147 return NS_OK; 2148 } 2149 2150 NS_IMETHODIMP 2151 BackgroundRequestChild::PreprocessHelper::OnInputStreamReady( 2152 nsIAsyncInputStream* aStream) { 2153 MOZ_ASSERT(!IsOnOwningThread()); 2154 MOZ_ASSERT(mState == State::WaitingForStreamReady); 2155 2156 MOZ_ALWAYS_SUCCEEDS(this->Run()); 2157 2158 return NS_OK; 2159 } 2160 2161 NS_IMETHODIMP 2162 BackgroundRequestChild::PreprocessHelper::OnFileMetadataReady( 2163 nsIAsyncFileMetadata* aObject) { 2164 MOZ_ASSERT(!IsOnOwningThread()); 2165 MOZ_ASSERT(mState == State::WaitingForStreamReady); 2166 2167 MOZ_ALWAYS_SUCCEEDS(this->Run()); 2168 2169 return NS_OK; 2170 } 2171 2172 /******************************************************************************* 2173 * BackgroundCursorChild 2174 ******************************************************************************/ 2175 2176 BackgroundCursorChildBase::BackgroundCursorChildBase( 2177 const NotNull<IDBRequest*> aRequest, const Direction aDirection) 2178 : mRequest(aRequest), 2179 mTransaction(aRequest->MaybeTransactionRef()), 2180 mStrongRequest(aRequest), 2181 mDirection(aDirection) { 2182 MOZ_ASSERT(mTransaction); 2183 } 2184 2185 MovingNotNull<RefPtr<IDBRequest>> BackgroundCursorChildBase::AcquireRequest() 2186 const { 2187 AssertIsOnOwningThread(); 2188 2189 // XXX This could be encapsulated by NotNull 2190 return WrapNotNullUnchecked(RefPtr{mRequest->get()}); 2191 } 2192 2193 template <IDBCursorType CursorType> 2194 BackgroundCursorChild<CursorType>::BackgroundCursorChild( 2195 const NotNull<IDBRequest*> aRequest, SourceType* aSource, 2196 Direction aDirection) 2197 : BackgroundCursorChildBase(aRequest, aDirection), 2198 mSource(WrapNotNull(aSource)), 2199 mCursor(nullptr), 2200 mInFlightResponseInvalidationNeeded(false) { 2201 aSource->AssertIsOnOwningThread(); 2202 2203 MOZ_COUNT_CTOR(indexedDB::BackgroundCursorChild<CursorType>); 2204 } 2205 2206 template <IDBCursorType CursorType> 2207 BackgroundCursorChild<CursorType>::~BackgroundCursorChild() { 2208 MOZ_COUNT_DTOR(indexedDB::BackgroundCursorChild<CursorType>); 2209 } 2210 2211 template <IDBCursorType CursorType> 2212 SafeRefPtr<BackgroundCursorChild<CursorType>> 2213 BackgroundCursorChild<CursorType>::SafeRefPtrFromThis() { 2214 return BackgroundCursorChildBase::SafeRefPtrFromThis() 2215 .template downcast<BackgroundCursorChild>(); 2216 } 2217 2218 template <IDBCursorType CursorType> 2219 void BackgroundCursorChild<CursorType>::SendContinueInternal( 2220 const int64_t aRequestId, const CursorRequestParams& aParams, 2221 const CursorData<CursorType>& aCurrentData) { 2222 AssertIsOnOwningThread(); 2223 MOZ_ASSERT(mRequest); 2224 MOZ_ASSERT(mTransaction); 2225 MOZ_ASSERT(mCursor); 2226 MOZ_ASSERT(!mStrongRequest); 2227 MOZ_ASSERT(!mStrongCursor); 2228 2229 // Make sure all our DOM objects stay alive. 2230 mStrongCursor = mCursor; 2231 2232 MOZ_ASSERT(GetRequest()->ReadyState() == IDBRequestReadyState::Done); 2233 GetRequest()->Reset(); 2234 2235 mTransaction->OnNewRequest(); 2236 2237 CursorRequestParams params = aParams; 2238 Key currentKey = aCurrentData.mKey; 2239 Key currentObjectStoreKey; 2240 // TODO: This is still not nice. 2241 if constexpr (!CursorTypeTraits<CursorType>::IsObjectStoreCursor) { 2242 currentObjectStoreKey = aCurrentData.mObjectStoreKey; 2243 } 2244 2245 switch (params.type()) { 2246 case CursorRequestParams::TContinueParams: { 2247 const auto& key = params.get_ContinueParams().key(); 2248 if (key.IsUnset()) { 2249 break; 2250 } 2251 2252 // Discard cache entries before the target key. 2253 DiscardCachedResponses( 2254 [&key, isLocaleAware = mCursor->IsLocaleAware(), 2255 keyOperator = GetKeyOperator(mDirection), 2256 transactionSerialNumber = mTransaction->LoggingSerialNumber(), 2257 requestSerialNumber = GetRequest()->LoggingSerialNumber()]( 2258 const auto& currentCachedResponse) { 2259 // This duplicates the logic from the parent. We could avoid this 2260 // duplication if we invalidated the cached records always for any 2261 // continue-with-key operation, but would lose the benefits of 2262 // preloading then. 2263 const auto& cachedSortKey = 2264 currentCachedResponse.GetSortKey(isLocaleAware); 2265 const bool discard = !(cachedSortKey.*keyOperator)(key); 2266 if (discard) { 2267 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2268 "PRELOAD: Continue to key %s, discarding cached key %s/%s", 2269 "Continue, discarding%.0s%.0s%.0s", transactionSerialNumber, 2270 requestSerialNumber, key.GetBuffer().get(), 2271 cachedSortKey.GetBuffer().get(), 2272 currentCachedResponse.GetObjectStoreKeyForLogging()); 2273 } else { 2274 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2275 "PRELOAD: Continue to key %s, keeping cached key %s/%s and " 2276 "further", 2277 "Continue, keeping%.0s%.0s%.0s", transactionSerialNumber, 2278 requestSerialNumber, key.GetBuffer().get(), 2279 cachedSortKey.GetBuffer().get(), 2280 currentCachedResponse.GetObjectStoreKeyForLogging()); 2281 } 2282 2283 return discard; 2284 }); 2285 2286 break; 2287 } 2288 2289 case CursorRequestParams::TContinuePrimaryKeyParams: { 2290 if constexpr (!CursorTypeTraits<CursorType>::IsObjectStoreCursor) { 2291 const auto& key = params.get_ContinuePrimaryKeyParams().key(); 2292 const auto& primaryKey = 2293 params.get_ContinuePrimaryKeyParams().primaryKey(); 2294 if (key.IsUnset() || primaryKey.IsUnset()) { 2295 break; 2296 } 2297 2298 // Discard cache entries before the target key. 2299 DiscardCachedResponses([&key, &primaryKey, 2300 isLocaleAware = mCursor->IsLocaleAware(), 2301 keyCompareOperator = GetKeyOperator(mDirection), 2302 transactionSerialNumber = 2303 mTransaction->LoggingSerialNumber(), 2304 requestSerialNumber = 2305 GetRequest()->LoggingSerialNumber()]( 2306 const auto& currentCachedResponse) { 2307 // This duplicates the logic from the parent. We could avoid this 2308 // duplication if we invalidated the cached records always for any 2309 // continue-with-key operation, but would lose the benefits of 2310 // preloading then. 2311 const auto& cachedSortKey = 2312 currentCachedResponse.GetSortKey(isLocaleAware); 2313 const auto& cachedSortPrimaryKey = 2314 currentCachedResponse.mObjectStoreKey; 2315 2316 const bool discard = 2317 (cachedSortKey == key && 2318 !(cachedSortPrimaryKey.*keyCompareOperator)(primaryKey)) || 2319 !(cachedSortKey.*keyCompareOperator)(key); 2320 2321 if (discard) { 2322 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2323 "PRELOAD: Continue to key %s with primary key %s, discarding " 2324 "cached key %s with cached primary key %s", 2325 "Continue, discarding%.0s%.0s%.0s%.0s", transactionSerialNumber, 2326 requestSerialNumber, key.GetBuffer().get(), 2327 primaryKey.GetBuffer().get(), cachedSortKey.GetBuffer().get(), 2328 cachedSortPrimaryKey.GetBuffer().get()); 2329 } else { 2330 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2331 "PRELOAD: Continue to key %s with primary key %s, keeping " 2332 "cached key %s with cached primary key %s and further", 2333 "Continue, keeping%.0s%.0s%.0s%.0s", transactionSerialNumber, 2334 requestSerialNumber, key.GetBuffer().get(), 2335 primaryKey.GetBuffer().get(), cachedSortKey.GetBuffer().get(), 2336 cachedSortPrimaryKey.GetBuffer().get()); 2337 } 2338 2339 return discard; 2340 }); 2341 } else { 2342 MOZ_CRASH("Shouldn't get here"); 2343 } 2344 2345 break; 2346 } 2347 2348 case CursorRequestParams::TAdvanceParams: { 2349 uint32_t& advanceCount = params.get_AdvanceParams().count(); 2350 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2351 "PRELOAD: Advancing %" PRIu32 " records", "Advancing %" PRIu32, 2352 mTransaction->LoggingSerialNumber(), 2353 GetRequest()->LoggingSerialNumber(), advanceCount); 2354 2355 // Discard cache entries. 2356 DiscardCachedResponses([&advanceCount, ¤tKey, 2357 ¤tObjectStoreKey]( 2358 const auto& currentCachedResponse) { 2359 const bool res = advanceCount > 1; 2360 if (res) { 2361 --advanceCount; 2362 2363 // TODO: We only need to update currentKey on the last entry, the 2364 // others are overwritten in the next iteration anyway. 2365 currentKey = currentCachedResponse.mKey; 2366 if constexpr (!CursorTypeTraits<CursorType>::IsObjectStoreCursor) { 2367 currentObjectStoreKey = currentCachedResponse.mObjectStoreKey; 2368 } else { 2369 (void)currentObjectStoreKey; 2370 } 2371 } 2372 return res; 2373 }); 2374 break; 2375 } 2376 2377 default: 2378 MOZ_CRASH("Should never get here!"); 2379 } 2380 2381 if (!mCachedResponses.empty()) { 2382 // We need to remove the response here from mCachedResponses, since when 2383 // requests are interleaved, other events may be processed before 2384 // CompleteContinueRequestFromCache, which may modify mCachedResponses. 2385 mDelayedResponses.emplace_back(std::move(mCachedResponses.front())); 2386 mCachedResponses.pop_front(); 2387 2388 // We cannot send the response right away, as we must preserve the request 2389 // order. Dispatching a DelayedActionRunnable only partially addresses this. 2390 // This is accompanied by invalidating cached entries at proper locations to 2391 // make it correct. To avoid this, further changes are necessary, see Bug 2392 // 1580499. 2393 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread( 2394 MakeAndAddRef<DelayedActionRunnable<BackgroundCursorChild<CursorType>>>( 2395 SafeRefPtrFromThis(), 2396 &BackgroundCursorChild::CompleteContinueRequestFromCache))); 2397 2398 // TODO: Could we preload further entries in the background when the size of 2399 // mCachedResponses falls under some threshold? Or does the response 2400 // handling model disallow this? 2401 } else { 2402 MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue( 2403 aRequestId, params, currentKey, currentObjectStoreKey)); 2404 } 2405 } 2406 2407 template <IDBCursorType CursorType> 2408 void BackgroundCursorChild<CursorType>::CompleteContinueRequestFromCache() { 2409 AssertIsOnOwningThread(); 2410 MOZ_ASSERT(mTransaction); 2411 MOZ_ASSERT(mCursor); 2412 MOZ_ASSERT(mStrongCursor); 2413 MOZ_ASSERT(!mDelayedResponses.empty()); 2414 MOZ_ASSERT(mCursor->GetType() == CursorType); 2415 2416 const RefPtr<IDBCursor> cursor = std::move(mStrongCursor); 2417 2418 mCursor->Reset(std::move(mDelayedResponses.front())); 2419 mDelayedResponses.pop_front(); 2420 2421 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2422 "PRELOAD: Consumed 1 cached response, %zu cached responses remaining", 2423 "Consumed cached response, %zu remaining", 2424 mTransaction->LoggingSerialNumber(), GetRequest()->LoggingSerialNumber(), 2425 mDelayedResponses.size() + mCachedResponses.size()); 2426 2427 SetResultAndDispatchSuccessEvent( 2428 GetRequest(), 2429 mTransaction 2430 ? SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}} 2431 : nullptr, 2432 *cursor); 2433 2434 mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ true); 2435 } 2436 2437 template <IDBCursorType CursorType> 2438 void BackgroundCursorChild<CursorType>::SendDeleteMeInternal() { 2439 AssertIsOnOwningThread(); 2440 MOZ_ASSERT(!mStrongRequest); 2441 MOZ_ASSERT(!mStrongCursor); 2442 2443 mRequest.destroy(); 2444 mTransaction = Nothing(); 2445 // TODO: The things until here could be pulled up to 2446 // BackgroundCursorChildBase. 2447 2448 mSource.destroy(); 2449 2450 if (mCursor) { 2451 mCursor->ClearBackgroundActor(); 2452 mCursor = nullptr; 2453 2454 MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendDeleteMe()); 2455 } 2456 } 2457 2458 template <IDBCursorType CursorType> 2459 void BackgroundCursorChild<CursorType>::InvalidateCachedResponses() { 2460 AssertIsOnOwningThread(); 2461 MOZ_ASSERT(mTransaction); 2462 MOZ_ASSERT(mRequest); 2463 2464 // TODO: With more information on the reason for the invalidation, we might 2465 // only selectively invalidate cached responses. If the reason is an updated 2466 // value, we do not need to care for key-only cursors. If the key of the 2467 // changed entry is not in the remaining range of the cursor, we also do not 2468 // need to care, etc. 2469 2470 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2471 "PRELOAD: Invalidating all %zu cached responses", "Invalidating %zu", 2472 mTransaction->LoggingSerialNumber(), GetRequest()->LoggingSerialNumber(), 2473 mCachedResponses.size()); 2474 2475 mCachedResponses.clear(); 2476 2477 // We only hold a strong cursor reference in mStrongCursor when 2478 // continue()/similar has been called. In those cases we expect a response 2479 // that will be received in the future, and it may include prefetched data 2480 // that needs to be discarded. 2481 if (mStrongCursor) { 2482 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2483 "PRELOAD: Setting flag to invalidate in-flight responses", 2484 "Set flag to invalidate in-flight responses", 2485 mTransaction->LoggingSerialNumber(), 2486 GetRequest()->LoggingSerialNumber()); 2487 2488 mInFlightResponseInvalidationNeeded = true; 2489 } 2490 } 2491 2492 template <IDBCursorType CursorType> 2493 template <typename Condition> 2494 void BackgroundCursorChild<CursorType>::DiscardCachedResponses( 2495 const Condition& aConditionFunc) { 2496 size_t discardedCount = 0; 2497 while (!mCachedResponses.empty() && 2498 aConditionFunc(mCachedResponses.front())) { 2499 mCachedResponses.pop_front(); 2500 ++discardedCount; 2501 } 2502 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2503 "PRELOAD: Discarded %zu cached responses, %zu remaining", 2504 "Discarded %zu; remaining %zu", mTransaction->LoggingSerialNumber(), 2505 GetRequest()->LoggingSerialNumber(), discardedCount, 2506 mCachedResponses.size()); 2507 } 2508 2509 BackgroundCursorChildBase::~BackgroundCursorChildBase() = default; 2510 2511 void BackgroundCursorChildBase::HandleResponse(nsresult aResponse) { 2512 AssertIsOnOwningThread(); 2513 MOZ_ASSERT(NS_FAILED(aResponse)); 2514 MOZ_ASSERT(NS_ERROR_GET_MODULE(aResponse) == NS_ERROR_MODULE_DOM_INDEXEDDB); 2515 MOZ_ASSERT(mRequest); 2516 MOZ_ASSERT(mTransaction); 2517 MOZ_ASSERT(!mStrongRequest); 2518 MOZ_ASSERT(!mStrongCursor); 2519 2520 DispatchErrorEvent( 2521 GetRequest(), aResponse, 2522 SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}}); 2523 } 2524 2525 template <IDBCursorType CursorType> 2526 void BackgroundCursorChild<CursorType>::HandleResponse( 2527 const void_t& aResponse) { 2528 AssertIsOnOwningThread(); 2529 MOZ_ASSERT(mRequest); 2530 MOZ_ASSERT(mTransaction); 2531 MOZ_ASSERT(!mStrongRequest); 2532 MOZ_ASSERT(!mStrongCursor); 2533 2534 if (mCursor) { 2535 mCursor->Reset(); 2536 } 2537 2538 SetResultAndDispatchSuccessEvent( 2539 GetRequest(), 2540 mTransaction 2541 ? SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}} 2542 : nullptr, 2543 JS::NullHandleValue); 2544 2545 if (!mCursor) { 2546 MOZ_ALWAYS_SUCCEEDS(this->GetActorEventTarget()->Dispatch( 2547 MakeAndAddRef<DelayedActionRunnable<BackgroundCursorChild<CursorType>>>( 2548 SafeRefPtrFromThis(), &BackgroundCursorChild::SendDeleteMeInternal), 2549 NS_DISPATCH_NORMAL)); 2550 } 2551 } 2552 2553 template <IDBCursorType CursorType> 2554 template <typename... Args> 2555 RefPtr<IDBCursor> 2556 BackgroundCursorChild<CursorType>::HandleIndividualCursorResponse( 2557 const bool aUseAsCurrentResult, Args&&... aArgs) { 2558 if (mCursor) { 2559 if (aUseAsCurrentResult) { 2560 mCursor->Reset(CursorData<CursorType>{std::forward<Args>(aArgs)...}); 2561 } else { 2562 mCachedResponses.emplace_back(std::forward<Args>(aArgs)...); 2563 } 2564 return nullptr; 2565 } 2566 2567 MOZ_ASSERT(aUseAsCurrentResult); 2568 2569 // TODO: This still looks quite dangerous to me. Why is mCursor not a 2570 // RefPtr? 2571 auto newCursor = IDBCursor::Create(this, std::forward<Args>(aArgs)...); 2572 mCursor = newCursor; 2573 return newCursor; 2574 } 2575 2576 template <IDBCursorType CursorType> 2577 template <typename Func> 2578 void BackgroundCursorChild<CursorType>::HandleMultipleCursorResponses( 2579 nsTArray<ResponseType>&& aResponses, const Func& aHandleRecord) { 2580 AssertIsOnOwningThread(); 2581 MOZ_ASSERT(mRequest); 2582 MOZ_ASSERT(mTransaction); 2583 MOZ_ASSERT(!mStrongRequest); 2584 MOZ_ASSERT(!mStrongCursor); 2585 MOZ_ASSERT(aResponses.Length() > 0); 2586 2587 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2588 "PRELOAD: Received %zu cursor responses", "Received %zu", 2589 mTransaction->LoggingSerialNumber(), GetRequest()->LoggingSerialNumber(), 2590 aResponses.Length()); 2591 MOZ_ASSERT_IF(aResponses.Length() > 1, mCachedResponses.empty()); 2592 2593 // If a new cursor is created, we need to keep a reference to it until the 2594 // SetResultAndDispatchSuccessEvent creates a DOM Binding. 2595 RefPtr<IDBCursor> strongNewCursor; 2596 2597 bool isFirst = true; 2598 for (auto& response : aResponses) { 2599 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2600 "PRELOAD: Processing response for key %s", "Processing%.0s", 2601 mTransaction->LoggingSerialNumber(), 2602 GetRequest()->LoggingSerialNumber(), response.key().GetBuffer().get()); 2603 2604 // TODO: At the moment, we only send a cursor request to the parent if 2605 // requested by the user code. Therefore, the first result is always used 2606 // as the current result, and the potential extra results are cached. If 2607 // we extended this towards preloading in the background, all results 2608 // might need to be cached. 2609 auto maybeNewCursor = 2610 aHandleRecord(/* aUseAsCurrentResult */ isFirst, std::move(response)); 2611 if (maybeNewCursor) { 2612 MOZ_ASSERT(!strongNewCursor); 2613 strongNewCursor = std::move(maybeNewCursor); 2614 } 2615 isFirst = false; 2616 2617 if (mInFlightResponseInvalidationNeeded) { 2618 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST( 2619 "PRELOAD: Discarding remaining responses since " 2620 "mInFlightResponseInvalidationNeeded is set", 2621 "Discarding responses", mTransaction->LoggingSerialNumber(), 2622 GetRequest()->LoggingSerialNumber()); 2623 2624 mInFlightResponseInvalidationNeeded = false; 2625 break; 2626 } 2627 } 2628 2629 SetResultAndDispatchSuccessEvent( 2630 GetRequest(), 2631 mTransaction 2632 ? SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}} 2633 : nullptr, 2634 *static_cast<IDBCursor*>(mCursor)); 2635 } 2636 2637 template <IDBCursorType CursorType> 2638 void BackgroundCursorChild<CursorType>::HandleResponse( 2639 nsTArray<ResponseType>&& aResponses) { 2640 AssertIsOnOwningThread(); 2641 2642 if constexpr (CursorType == IDBCursorType::ObjectStore || 2643 CursorType == IDBCursorType::Index) { 2644 MOZ_ASSERT(mTransaction); 2645 2646 if (!mTransaction->Database()->GetOwnerGlobal()) { 2647 // Ignore the response, since we have already been disconnected from the 2648 // global. 2649 return; 2650 } 2651 } 2652 2653 if constexpr (CursorType == IDBCursorType::ObjectStore) { 2654 HandleMultipleCursorResponses( 2655 std::move(aResponses), [this](const bool useAsCurrentResult, 2656 ObjectStoreCursorResponse&& response) { 2657 // TODO: Maybe move the deserialization of the clone-read-info into 2658 // the cursor, so that it is only done for records actually accessed, 2659 // which might not be the case for all cached records. 2660 return HandleIndividualCursorResponse( 2661 useAsCurrentResult, std::move(response.key()), 2662 DeserializeStructuredCloneReadInfo( 2663 std::move(response.cloneInfo()), mTransaction->Database(), 2664 PreprocessingNotSupported)); 2665 }); 2666 } 2667 if constexpr (CursorType == IDBCursorType::ObjectStoreKey) { 2668 HandleMultipleCursorResponses( 2669 std::move(aResponses), [this](const bool useAsCurrentResult, 2670 ObjectStoreKeyCursorResponse&& response) { 2671 return HandleIndividualCursorResponse(useAsCurrentResult, 2672 std::move(response.key())); 2673 }); 2674 } 2675 if constexpr (CursorType == IDBCursorType::Index) { 2676 HandleMultipleCursorResponses( 2677 std::move(aResponses), 2678 [this](const bool useAsCurrentResult, IndexCursorResponse&& response) { 2679 return HandleIndividualCursorResponse( 2680 useAsCurrentResult, std::move(response.key()), 2681 std::move(response.sortKey()), std::move(response.objectKey()), 2682 DeserializeStructuredCloneReadInfo( 2683 std::move(response.cloneInfo()), mTransaction->Database(), 2684 PreprocessingNotSupported)); 2685 }); 2686 } 2687 if constexpr (CursorType == IDBCursorType::IndexKey) { 2688 HandleMultipleCursorResponses( 2689 std::move(aResponses), [this](const bool useAsCurrentResult, 2690 IndexKeyCursorResponse&& response) { 2691 return HandleIndividualCursorResponse( 2692 useAsCurrentResult, std::move(response.key()), 2693 std::move(response.sortKey()), std::move(response.objectKey())); 2694 }); 2695 } 2696 } 2697 2698 template <IDBCursorType CursorType> 2699 void BackgroundCursorChild<CursorType>::ActorDestroy(ActorDestroyReason aWhy) { 2700 AssertIsOnOwningThread(); 2701 MOZ_ASSERT_IF(aWhy == Deletion, !mStrongRequest); 2702 MOZ_ASSERT_IF(aWhy == Deletion, !mStrongCursor); 2703 2704 MaybeCollectGarbageOnIPCMessage(); 2705 2706 if (mStrongRequest && !mStrongCursor && mTransaction) { 2707 mTransaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ 2708 aWhy == Deletion); 2709 } 2710 2711 if (mCursor) { 2712 mCursor->ClearBackgroundActor(); 2713 #ifdef DEBUG 2714 mCursor = nullptr; 2715 #endif 2716 } 2717 2718 #ifdef DEBUG 2719 mRequest.maybeDestroy(); 2720 mTransaction = Nothing(); 2721 mSource.maybeDestroy(); 2722 #endif 2723 } 2724 2725 template <IDBCursorType CursorType> 2726 mozilla::ipc::IPCResult BackgroundCursorChild<CursorType>::RecvResponse( 2727 CursorResponse&& aResponse) { 2728 AssertIsOnOwningThread(); 2729 MOZ_ASSERT(aResponse.type() != CursorResponse::T__None); 2730 MOZ_ASSERT(mRequest); 2731 MOZ_ASSERT(mTransaction); 2732 MOZ_ASSERT_IF(mCursor, mStrongCursor); 2733 MOZ_ASSERT_IF(!mCursor, mStrongRequest); 2734 2735 MaybeCollectGarbageOnIPCMessage(); 2736 2737 const RefPtr<IDBRequest> request = std::move(mStrongRequest); 2738 (void)request; // XXX see Bug 1605075 2739 const RefPtr<IDBCursor> cursor = std::move(mStrongCursor); 2740 (void)cursor; // XXX see Bug 1605075 2741 2742 const auto transaction = 2743 SafeRefPtr{&mTransaction.ref(), AcquireStrongRefFromRawPtr{}}; 2744 2745 switch (aResponse.type()) { 2746 case CursorResponse::Tnsresult: 2747 HandleResponse(aResponse.get_nsresult()); 2748 break; 2749 2750 case CursorResponse::Tvoid_t: 2751 HandleResponse(aResponse.get_void_t()); 2752 break; 2753 2754 case CursorResponse::TArrayOfObjectStoreCursorResponse: 2755 if constexpr (CursorType == IDBCursorType::ObjectStore) { 2756 HandleResponse( 2757 std::move(aResponse.get_ArrayOfObjectStoreCursorResponse())); 2758 } else { 2759 MOZ_CRASH("Response type mismatch"); 2760 } 2761 break; 2762 2763 case CursorResponse::TArrayOfObjectStoreKeyCursorResponse: 2764 if constexpr (CursorType == IDBCursorType::ObjectStoreKey) { 2765 HandleResponse( 2766 std::move(aResponse.get_ArrayOfObjectStoreKeyCursorResponse())); 2767 } else { 2768 MOZ_CRASH("Response type mismatch"); 2769 } 2770 break; 2771 2772 case CursorResponse::TArrayOfIndexCursorResponse: 2773 if constexpr (CursorType == IDBCursorType::Index) { 2774 HandleResponse(std::move(aResponse.get_ArrayOfIndexCursorResponse())); 2775 } else { 2776 MOZ_CRASH("Response type mismatch"); 2777 } 2778 break; 2779 2780 case CursorResponse::TArrayOfIndexKeyCursorResponse: 2781 if constexpr (CursorType == IDBCursorType::IndexKey) { 2782 HandleResponse( 2783 std::move(aResponse.get_ArrayOfIndexKeyCursorResponse())); 2784 } else { 2785 MOZ_CRASH("Response type mismatch"); 2786 } 2787 break; 2788 2789 default: 2790 MOZ_CRASH("Should never get here!"); 2791 } 2792 2793 transaction->OnRequestFinished(/* aRequestCompletedSuccessfully */ true); 2794 2795 return IPC_OK(); 2796 } 2797 2798 template class BackgroundCursorChild<IDBCursorType::ObjectStore>; 2799 template class BackgroundCursorChild<IDBCursorType::ObjectStoreKey>; 2800 template class BackgroundCursorChild<IDBCursorType::Index>; 2801 template class BackgroundCursorChild<IDBCursorType::IndexKey>; 2802 2803 template <typename T> 2804 NS_IMETHODIMP DelayedActionRunnable<T>::Run() { 2805 MOZ_ASSERT(mActor); 2806 mActor->AssertIsOnOwningThread(); 2807 MOZ_ASSERT(mRequest); 2808 MOZ_ASSERT(mActionFunc); 2809 2810 ((*mActor).*mActionFunc)(); 2811 2812 mActor = nullptr; 2813 mRequest = nullptr; 2814 2815 return NS_OK; 2816 } 2817 2818 template <typename T> 2819 nsresult DelayedActionRunnable<T>::Cancel() { 2820 if (NS_WARN_IF(!mActor)) { 2821 return NS_ERROR_UNEXPECTED; 2822 } 2823 2824 // This must always run to clean up our state. 2825 Run(); 2826 2827 return NS_OK; 2828 } 2829 2830 /******************************************************************************* 2831 * BackgroundUtilsChild 2832 ******************************************************************************/ 2833 2834 BackgroundUtilsChild::BackgroundUtilsChild(IndexedDatabaseManager* aManager) 2835 : mManager(aManager) { 2836 AssertIsOnOwningThread(); 2837 MOZ_ASSERT(aManager); 2838 2839 MOZ_COUNT_CTOR(indexedDB::BackgroundUtilsChild); 2840 } 2841 2842 BackgroundUtilsChild::~BackgroundUtilsChild() { 2843 MOZ_COUNT_DTOR(indexedDB::BackgroundUtilsChild); 2844 } 2845 2846 void BackgroundUtilsChild::SendDeleteMeInternal() { 2847 AssertIsOnOwningThread(); 2848 2849 if (mManager) { 2850 mManager->ClearBackgroundActor(); 2851 mManager = nullptr; 2852 2853 MOZ_ALWAYS_TRUE(PBackgroundIndexedDBUtilsChild::SendDeleteMe()); 2854 } 2855 } 2856 2857 void BackgroundUtilsChild::ActorDestroy(ActorDestroyReason aWhy) { 2858 AssertIsOnOwningThread(); 2859 2860 if (mManager) { 2861 mManager->ClearBackgroundActor(); 2862 #ifdef DEBUG 2863 mManager = nullptr; 2864 #endif 2865 } 2866 } 2867 2868 } // namespace dom::indexedDB 2869 } // namespace mozilla