FileSystemSyncAccessHandle.cpp (22752B)
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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "FileSystemSyncAccessHandle.h" 8 9 #include "fs/FileSystemAsyncCopy.h" 10 #include "fs/FileSystemRequestHandler.h" 11 #include "mozilla/CheckedInt.h" 12 #include "mozilla/ErrorResult.h" 13 #include "mozilla/FixedBufferOutputStream.h" 14 #include "mozilla/MozPromise.h" 15 #include "mozilla/TaskQueue.h" 16 #include "mozilla/dom/BindingDeclarations.h" 17 #include "mozilla/dom/BufferSourceBinding.h" 18 #include "mozilla/dom/FileSystemAccessHandleChild.h" 19 #include "mozilla/dom/FileSystemAccessHandleControlChild.h" 20 #include "mozilla/dom/FileSystemHandleBinding.h" 21 #include "mozilla/dom/FileSystemLog.h" 22 #include "mozilla/dom/FileSystemManager.h" 23 #include "mozilla/dom/FileSystemManagerChild.h" 24 #include "mozilla/dom/FileSystemSyncAccessHandleBinding.h" 25 #include "mozilla/dom/Promise.h" 26 #include "mozilla/dom/UnionTypes.h" 27 #include "mozilla/dom/WorkerCommon.h" 28 #include "mozilla/dom/WorkerPrivate.h" 29 #include "mozilla/dom/WorkerRef.h" 30 #include "mozilla/dom/fs/IPCRejectReporter.h" 31 #include "mozilla/dom/quota/QuotaCommon.h" 32 #include "mozilla/dom/quota/ResultExtensions.h" 33 #include "mozilla/dom/quota/TargetPtrHolder.h" 34 #include "mozilla/ipc/RandomAccessStreamUtils.h" 35 #include "nsNetCID.h" 36 #include "nsStringStream.h" 37 38 namespace mozilla::dom { 39 40 namespace { 41 42 using SizePromise = Int64Promise; 43 const auto CreateAndRejectSizePromise = CreateAndRejectInt64Promise; 44 45 } // namespace 46 47 FileSystemSyncAccessHandle::FileSystemSyncAccessHandle( 48 nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager, 49 mozilla::ipc::RandomAccessStreamParams&& aStreamParams, 50 RefPtr<FileSystemAccessHandleChild> aActor, 51 RefPtr<FileSystemAccessHandleControlChild> aControlActor, 52 RefPtr<TaskQueue> aIOTaskQueue, 53 const fs::FileSystemEntryMetadata& aMetadata) 54 : mGlobal(aGlobal), 55 mManager(aManager), 56 mActor(std::move(aActor)), 57 mControlActor(std::move(aControlActor)), 58 mIOTaskQueue(std::move(aIOTaskQueue)), 59 mStreamParams(std::move(aStreamParams)), 60 mMetadata(aMetadata), 61 mState(State::Initial) { 62 LOG(("Created SyncAccessHandle %p", this)); 63 64 // Connect with the actor directly in the constructor. This way the actor 65 // can call `FileSystemSyncAccessHandle::ClearActor` when we call 66 // `PFileSystemAccessHandleChild::Send__delete__` even when 67 // FileSystemSyncAccessHandle::Create fails, in which case the not yet 68 // fully constructed FileSystemSyncAccessHandle is being destroyed. 69 mActor->SetAccessHandle(this); 70 71 mControlActor->SetAccessHandle(this); 72 } 73 74 FileSystemSyncAccessHandle::~FileSystemSyncAccessHandle() { 75 MOZ_ASSERT(!mActor); 76 MOZ_ASSERT(IsClosed()); 77 } 78 79 // static 80 Result<RefPtr<FileSystemSyncAccessHandle>, nsresult> 81 FileSystemSyncAccessHandle::Create( 82 nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager, 83 mozilla::ipc::RandomAccessStreamParams&& aStreamParams, 84 mozilla::ipc::ManagedEndpoint<PFileSystemAccessHandleChild>&& 85 aAccessHandleChildEndpoint, 86 mozilla::ipc::Endpoint<PFileSystemAccessHandleControlChild>&& 87 aAccessHandleControlChildEndpoint, 88 const fs::FileSystemEntryMetadata& aMetadata) { 89 WorkerPrivate* const workerPrivate = GetCurrentThreadWorkerPrivate(); 90 MOZ_ASSERT(workerPrivate); 91 92 auto accessHandleChild = MakeRefPtr<FileSystemAccessHandleChild>(); 93 94 QM_TRY(MOZ_TO_RESULT( 95 aManager->ActorStrongRef()->BindPFileSystemAccessHandleEndpoint( 96 std::move(aAccessHandleChildEndpoint), accessHandleChild))); 97 98 auto accessHandleControlChild = 99 MakeRefPtr<FileSystemAccessHandleControlChild>(); 100 101 aAccessHandleControlChildEndpoint.Bind(accessHandleControlChild, 102 workerPrivate->ControlEventTarget()); 103 104 QM_TRY_UNWRAP(auto streamTransportService, 105 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIEventTarget>, 106 MOZ_SELECT_OVERLOAD(do_GetService), 107 NS_STREAMTRANSPORTSERVICE_CONTRACTID)); 108 109 RefPtr<TaskQueue> ioTaskQueue = TaskQueue::Create( 110 streamTransportService.forget(), "FileSystemSyncAccessHandle"); 111 QM_TRY(MOZ_TO_RESULT(ioTaskQueue)); 112 113 RefPtr<FileSystemSyncAccessHandle> result = new FileSystemSyncAccessHandle( 114 aGlobal, aManager, std::move(aStreamParams), std::move(accessHandleChild), 115 std::move(accessHandleControlChild), std::move(ioTaskQueue), aMetadata); 116 117 auto autoClose = MakeScopeExit([result] { 118 MOZ_ASSERT(result->mState == State::Initial); 119 result->mState = State::Closed; 120 result->mActor->SendClose(); 121 }); 122 123 workerPrivate->AssertIsOnWorkerThread(); 124 125 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( 126 workerPrivate, "FileSystemSyncAccessHandle", [result]() { 127 if (result->IsOpen()) { 128 // We don't need to use the result, we just need to begin the closing 129 // process. 130 (void)result->BeginClose(); 131 } 132 }); 133 QM_TRY(MOZ_TO_RESULT(workerRef)); 134 135 autoClose.release(); 136 137 result->mWorkerRef = std::move(workerRef); 138 result->mState = State::Open; 139 140 return result; 141 } 142 143 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemSyncAccessHandle) 144 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 145 NS_INTERFACE_MAP_ENTRY(nsISupports) 146 NS_INTERFACE_MAP_END 147 148 NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemSyncAccessHandle) 149 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FileSystemSyncAccessHandle, 150 LastRelease()) 151 152 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FileSystemSyncAccessHandle) 153 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FileSystemSyncAccessHandle) 154 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) 155 // Don't unlink mManager! 156 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 157 if (tmp->IsOpen()) { 158 // We don't need to use the result, we just need to begin the closing 159 // process. 160 (void)tmp->BeginClose(); 161 } 162 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 163 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FileSystemSyncAccessHandle) 164 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) 165 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mManager) 166 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 167 168 void FileSystemSyncAccessHandle::LastRelease() { 169 // We can't call `FileSystemSyncAccessHandle::Close` here because it may need 170 // to keep FileSystemSyncAccessHandle object alive which isn't possible when 171 // the object is about to be deleted. There are other mechanisms which ensure 172 // that the object is correctly closed before destruction. For example the 173 // object unlinking and the worker shutdown (we get notified about it via the 174 // callback passed to `StrongWorkerRef`) are used to close the object if it 175 // hasn't been closed yet. 176 177 if (mActor) { 178 PFileSystemAccessHandleChild::Send__delete__(mActor); 179 180 // `PFileSystemAccessHandleChild::Send__delete__` is supposed to call 181 // `FileSystemAccessHandleChild::ActorDestroy` which in turn calls 182 // `FileSystemSyncAccessHandle::ClearActor`, so `mActor` should be be null 183 // at this point. 184 MOZ_ASSERT(!mActor); 185 } 186 187 if (mControlActor) { 188 mControlActor->Close(); 189 190 // `FileSystemAccessHandleControlChild::Close` is supposed to call 191 // `FileSystemAccessHandleControlChild::ActorDestroy` which in turn calls 192 // `FileSystemSyncAccessHandle::ClearControlActor`, so `mControlActor` 193 // should be be null at this point. 194 MOZ_ASSERT(!mControlActor); 195 } 196 } 197 198 void FileSystemSyncAccessHandle::ClearActor() { 199 MOZ_ASSERT(mActor); 200 201 mActor = nullptr; 202 } 203 204 void FileSystemSyncAccessHandle::ClearControlActor() { 205 // `mControlActor` is initialized in the constructor and this method is 206 // supposed to be called only once. 207 MOZ_ASSERT(mControlActor); 208 209 mControlActor = nullptr; 210 } 211 212 bool FileSystemSyncAccessHandle::IsOpen() const { 213 MOZ_ASSERT(mState != State::Initial); 214 215 return mState == State::Open; 216 } 217 218 bool FileSystemSyncAccessHandle::IsClosing() const { 219 MOZ_ASSERT(mState != State::Initial); 220 221 return mState == State::Closing; 222 } 223 224 bool FileSystemSyncAccessHandle::IsClosed() const { 225 MOZ_ASSERT(mState != State::Initial); 226 227 return mState == State::Closed; 228 } 229 230 RefPtr<BoolPromise> FileSystemSyncAccessHandle::BeginClose() { 231 MOZ_ASSERT(IsOpen()); 232 233 mState = State::Closing; 234 235 InvokeAsync(mIOTaskQueue, __func__, 236 [selfHolder = quota::TargetPtrHolder(this)]() { 237 if (selfHolder->mStream) { 238 LOG(("%p: Closing", selfHolder->mStream.get())); 239 240 selfHolder->mStream->OutputStream()->Close(); 241 selfHolder->mStream = nullptr; 242 } else { 243 LOG(("Closing (no stream)")); 244 245 // If the stream was not deserialized, `mStreamParams` still 246 // contains a pre-opened file descriptor which needs to be 247 // closed here by moving `mStreamParams` to a local variable 248 // (the file descriptor will be closed for real when 249 // `streamParams` goes out of scope). 250 251 mozilla::ipc::RandomAccessStreamParams streamParams( 252 std::move(selfHolder->mStreamParams)); 253 } 254 255 return BoolPromise::CreateAndResolve(true, __func__); 256 }) 257 ->Then(mWorkerRef->Private()->ControlEventTarget(), __func__, 258 [self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue&) { 259 return self->mIOTaskQueue->BeginShutdown(); 260 }) 261 ->Then( 262 mWorkerRef->Private()->ControlEventTarget(), __func__, 263 [self = RefPtr(this)](const ShutdownPromise::ResolveOrRejectValue&) { 264 if (self->mControlActor) { 265 RefPtr<BoolPromise::Private> promise = 266 new BoolPromise::Private(__func__); 267 268 self->mControlActor->SendClose( 269 [promise](void_t&&) { promise->Resolve(true, __func__); }, 270 [promise](const mozilla::ipc::ResponseRejectReason& aReason) { 271 fs::IPCRejectReporter(aReason); 272 273 promise->Reject(NS_ERROR_FAILURE, __func__); 274 }); 275 276 return RefPtr<BoolPromise>(promise); 277 } 278 279 return BoolPromise::CreateAndResolve(true, __func__); 280 }) 281 ->Then(mWorkerRef->Private()->ControlEventTarget(), __func__, 282 [self = RefPtr(this)](const BoolPromise::ResolveOrRejectValue&) { 283 self->mWorkerRef = nullptr; 284 285 self->mState = State::Closed; 286 287 self->mClosePromiseHolder.ResolveIfExists(true, __func__); 288 }); 289 290 return OnClose(); 291 } 292 293 RefPtr<BoolPromise> FileSystemSyncAccessHandle::OnClose() { 294 MOZ_ASSERT(mState == State::Closing); 295 296 return mClosePromiseHolder.Ensure(__func__); 297 } 298 299 // WebIDL Boilerplate 300 301 nsIGlobalObject* FileSystemSyncAccessHandle::GetParentObject() const { 302 return mGlobal; 303 } 304 305 JSObject* FileSystemSyncAccessHandle::WrapObject( 306 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 307 return FileSystemSyncAccessHandle_Binding::Wrap(aCx, this, aGivenProto); 308 } 309 310 // WebIDL Interface 311 312 uint64_t FileSystemSyncAccessHandle::Read( 313 const AllowSharedBufferSource& aBuffer, 314 const FileSystemReadWriteOptions& aOptions, ErrorResult& aRv) { 315 return ReadOrWrite(aBuffer, aOptions, /* aRead */ true, aRv); 316 } 317 318 uint64_t FileSystemSyncAccessHandle::Write( 319 const AllowSharedBufferSource& aBuffer, 320 const FileSystemReadWriteOptions& aOptions, ErrorResult& aRv) { 321 return ReadOrWrite(aBuffer, aOptions, /* aRead */ false, aRv); 322 } 323 324 void FileSystemSyncAccessHandle::Truncate(uint64_t aSize, ErrorResult& aError) { 325 if (!IsOpen()) { 326 aError.ThrowInvalidStateError("SyncAccessHandle is closed"); 327 return; 328 } 329 330 MOZ_ASSERT(mWorkerRef); 331 332 AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling); 333 334 nsCOMPtr<nsISerialEventTarget> syncLoopTarget = 335 syncLoop.GetSerialEventTarget(); 336 QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) { 337 aError.ThrowInvalidStateError("Worker is shutting down"); 338 }); 339 340 InvokeAsync( 341 mIOTaskQueue, __func__, 342 [selfHolder = quota::TargetPtrHolder(this), aSize]() { 343 QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()), 344 CreateAndRejectBoolPromise); 345 346 LOG(("%p: Truncate to %" PRIu64, selfHolder->mStream.get(), aSize)); 347 int64_t offset = 0; 348 QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Tell(&offset)), 349 CreateAndRejectBoolPromise); 350 QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek( 351 nsISeekableStream::NS_SEEK_SET, aSize)), 352 CreateAndRejectBoolPromise); 353 354 QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->SetEOF()), 355 CreateAndRejectBoolPromise); 356 // restore cursor position (clamp to end of file) 357 QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek( 358 nsISeekableStream::NS_SEEK_SET, 359 std::min((uint64_t)offset, aSize))), 360 CreateAndRejectBoolPromise); 361 362 return BoolPromise::CreateAndResolve(true, __func__); 363 }) 364 ->Then(syncLoopTarget, __func__, 365 [this, &syncLoopTarget]( 366 const BoolPromise::ResolveOrRejectValue& aValue) { 367 MOZ_ASSERT(mWorkerRef); 368 369 mWorkerRef->Private()->AssertIsOnWorkerThread(); 370 371 mWorkerRef->Private()->StopSyncLoop( 372 syncLoopTarget, 373 aValue.IsResolve() ? NS_OK : aValue.RejectValue()); 374 }); 375 376 QM_TRY(MOZ_TO_RESULT(syncLoop.Run()), 377 [&aError](const nsresult rv) { aError.Throw(rv); }); 378 } 379 380 uint64_t FileSystemSyncAccessHandle::GetSize(ErrorResult& aError) { 381 if (!IsOpen()) { 382 aError.ThrowInvalidStateError("SyncAccessHandle is closed"); 383 return 0; 384 } 385 386 MOZ_ASSERT(mWorkerRef); 387 388 AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling); 389 390 nsCOMPtr<nsISerialEventTarget> syncLoopTarget = 391 syncLoop.GetSerialEventTarget(); 392 QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) { 393 aError.ThrowInvalidStateError("Worker is shutting down"); 394 return 0; 395 }); 396 397 // XXX Could we somehow pass the size to `StopSyncLoop` and then get it via 398 // `QM_TRY_INSPECT(const auto& size, syncLoop.Run)` ? 399 // Could we use Result<UniquePtr<...>, nsresult> for that ? 400 int64_t size; 401 402 InvokeAsync(mIOTaskQueue, __func__, 403 [selfHolder = quota::TargetPtrHolder(this)]() { 404 QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()), 405 CreateAndRejectSizePromise); 406 407 nsCOMPtr<nsIFileMetadata> fileMetadata = 408 do_QueryInterface(selfHolder->mStream); 409 MOZ_ASSERT(fileMetadata); 410 411 QM_TRY_INSPECT( 412 const auto& size, 413 MOZ_TO_RESULT_INVOKE_MEMBER(fileMetadata, GetSize), 414 CreateAndRejectSizePromise); 415 416 LOG(("%p: GetSize %" PRIu64, selfHolder->mStream.get(), size)); 417 418 return SizePromise::CreateAndResolve(size, __func__); 419 }) 420 ->Then(syncLoopTarget, __func__, 421 [this, &syncLoopTarget, 422 &size](const Int64Promise::ResolveOrRejectValue& aValue) { 423 MOZ_ASSERT(mWorkerRef); 424 425 mWorkerRef->Private()->AssertIsOnWorkerThread(); 426 427 if (aValue.IsResolve()) { 428 size = aValue.ResolveValue(); 429 430 mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK); 431 } else { 432 mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, 433 aValue.RejectValue()); 434 } 435 }); 436 437 QM_TRY(MOZ_TO_RESULT(syncLoop.Run()), [&aError](const nsresult rv) { 438 aError.Throw(rv); 439 return 0; 440 }); 441 442 return size; 443 } 444 445 void FileSystemSyncAccessHandle::Flush(ErrorResult& aError) { 446 if (!IsOpen()) { 447 aError.ThrowInvalidStateError("SyncAccessHandle is closed"); 448 return; 449 } 450 451 MOZ_ASSERT(mWorkerRef); 452 453 AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling); 454 455 nsCOMPtr<nsISerialEventTarget> syncLoopTarget = 456 syncLoop.GetSerialEventTarget(); 457 QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aError](nsresult) { 458 aError.ThrowInvalidStateError("Worker is shutting down"); 459 }); 460 461 InvokeAsync(mIOTaskQueue, __func__, 462 [selfHolder = quota::TargetPtrHolder(this)]() { 463 QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()), 464 CreateAndRejectBoolPromise); 465 466 LOG(("%p: Flush", selfHolder->mStream.get())); 467 468 QM_TRY( 469 MOZ_TO_RESULT(selfHolder->mStream->OutputStream()->Flush()), 470 CreateAndRejectBoolPromise); 471 472 return BoolPromise::CreateAndResolve(true, __func__); 473 }) 474 ->Then(syncLoopTarget, __func__, 475 [this, &syncLoopTarget]( 476 const BoolPromise::ResolveOrRejectValue& aValue) { 477 MOZ_ASSERT(mWorkerRef); 478 479 mWorkerRef->Private()->AssertIsOnWorkerThread(); 480 481 mWorkerRef->Private()->StopSyncLoop( 482 syncLoopTarget, 483 aValue.IsResolve() ? NS_OK : aValue.RejectValue()); 484 }); 485 486 QM_TRY(MOZ_TO_RESULT(syncLoop.Run()), 487 [&aError](const nsresult rv) { aError.Throw(rv); }); 488 } 489 490 void FileSystemSyncAccessHandle::Close() { 491 if (!(IsOpen() || IsClosing())) { 492 return; 493 } 494 495 MOZ_ASSERT(mWorkerRef); 496 497 // Normally mWorkerRef can be used directly for stopping the sync loop, but 498 // the async close is special because mWorkerRef is cleared as part of the 499 // operation. That's why we need to use this extra strong ref to the 500 // `StrongWorkerRef`. 501 RefPtr<StrongWorkerRef> workerRef = mWorkerRef; 502 503 AutoSyncLoopHolder syncLoop(workerRef->Private(), Killing); 504 505 nsCOMPtr<nsISerialEventTarget> syncLoopTarget = 506 syncLoop.GetSerialEventTarget(); 507 MOZ_ASSERT(syncLoopTarget); 508 509 InvokeAsync(syncLoopTarget, __func__, [self = RefPtr(this)]() { 510 if (self->IsOpen()) { 511 return self->BeginClose(); 512 } 513 return self->OnClose(); 514 })->Then(syncLoopTarget, __func__, [&workerRef, &syncLoopTarget]() { 515 MOZ_ASSERT(workerRef); 516 517 workerRef->Private()->AssertIsOnWorkerThread(); 518 519 workerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK); 520 }); 521 522 MOZ_ALWAYS_SUCCEEDS(syncLoop.Run()); 523 } 524 525 uint64_t FileSystemSyncAccessHandle::ReadOrWrite( 526 const AllowSharedBufferSource& aBuffer, 527 const FileSystemReadWriteOptions& aOptions, const bool aRead, 528 ErrorResult& aRv) { 529 if (!IsOpen()) { 530 aRv.ThrowInvalidStateError("SyncAccessHandle is closed"); 531 return 0; 532 } 533 534 MOZ_ASSERT(mWorkerRef); 535 536 auto throwAndReturn = [&aRv](const nsresult rv) { 537 aRv.Throw(rv); 538 return 0; 539 }; 540 541 // Handle seek before read ('at') 542 const auto at = [&aOptions]() -> uint64_t { 543 if (aOptions.mAt.WasPassed()) { 544 return aOptions.mAt.Value(); 545 } 546 // Spec says default for at is 0 (2.6) 547 return 0; 548 }(); 549 550 const auto offset = CheckedInt<int64_t>(at); 551 QM_TRY(MOZ_TO_RESULT(offset.isValid()), throwAndReturn); 552 553 AutoSyncLoopHolder syncLoop(mWorkerRef->Private(), Canceling); 554 555 nsCOMPtr<nsISerialEventTarget> syncLoopTarget = 556 syncLoop.GetSerialEventTarget(); 557 QM_TRY(MOZ_TO_RESULT(syncLoopTarget), [&aRv](nsresult) { 558 aRv.ThrowInvalidStateError("Worker is shutting down"); 559 return 0; 560 }); 561 562 uint64_t totalCount = 0; 563 564 ProcessTypedArraysFixed(aBuffer, [&](const Span<uint8_t> aData) { 565 InvokeAsync( 566 mIOTaskQueue, __func__, 567 [selfHolder = quota::TargetPtrHolder(this), aData, 568 use_offset = aOptions.mAt.WasPassed(), offset, aRead, syncLoopTarget, 569 &totalCount]() { 570 QM_TRY(MOZ_TO_RESULT(selfHolder->EnsureStream()), 571 CreateAndRejectBoolPromise); 572 if (use_offset) { 573 LOG_VERBOSE(("%p: Seeking to %" PRIu64, selfHolder->mStream.get(), 574 offset.value())); 575 576 QM_TRY(MOZ_TO_RESULT(selfHolder->mStream->Seek( 577 nsISeekableStream::NS_SEEK_SET, offset.value())), 578 CreateAndRejectBoolPromise); 579 } 580 581 nsCOMPtr<nsIInputStream> inputStream; 582 nsCOMPtr<nsIOutputStream> outputStream; 583 584 if (aRead) { 585 LOG_VERBOSE(("%p: Reading %zu bytes", selfHolder->mStream.get(), 586 aData.Length())); 587 588 inputStream = selfHolder->mStream->InputStream(); 589 outputStream = 590 FixedBufferOutputStream::Create(AsWritableChars(aData)); 591 } else { 592 LOG_VERBOSE(("%p: Writing %zu bytes", selfHolder->mStream.get(), 593 aData.Length())); 594 595 QM_TRY(MOZ_TO_RESULT(NS_NewByteInputStream( 596 getter_AddRefs(inputStream), AsChars(aData), 597 NS_ASSIGNMENT_DEPEND)), 598 CreateAndRejectBoolPromise); 599 600 outputStream = selfHolder->mStream->OutputStream(); 601 } 602 603 auto promiseHolder = MakeUnique<MozPromiseHolder<BoolPromise>>(); 604 RefPtr<BoolPromise> promise = promiseHolder->Ensure(__func__); 605 606 QM_TRY(MOZ_TO_RESULT(fs::AsyncCopy( 607 inputStream, outputStream, GetCurrentSerialEventTarget(), 608 aRead ? NS_ASYNCCOPY_VIA_WRITESEGMENTS 609 : NS_ASYNCCOPY_VIA_READSEGMENTS, 610 /* aCloseSource */ !aRead, /* aCloseSink */ aRead, 611 [&totalCount](uint32_t count) { totalCount += count; }, 612 [promiseHolder = std::move(promiseHolder)](nsresult rv) { 613 promiseHolder->ResolveIfExists(true, __func__); 614 })), 615 CreateAndRejectBoolPromise); 616 617 return promise; 618 }) 619 ->Then(syncLoopTarget, __func__, 620 [this, &syncLoopTarget]( 621 const BoolPromise::ResolveOrRejectValue& aValue) { 622 MOZ_ASSERT(mWorkerRef); 623 624 mWorkerRef->Private()->AssertIsOnWorkerThread(); 625 626 mWorkerRef->Private()->StopSyncLoop(syncLoopTarget, NS_OK); 627 }); 628 629 MOZ_ALWAYS_SUCCEEDS(syncLoop.Run()); 630 }); 631 632 return totalCount; 633 } 634 635 nsresult FileSystemSyncAccessHandle::EnsureStream() { 636 if (!mStream) { 637 QM_TRY_UNWRAP(mStream, DeserializeRandomAccessStream(mStreamParams), 638 NS_ERROR_FAILURE); 639 640 mozilla::ipc::RandomAccessStreamParams streamParams( 641 std::move(mStreamParams)); 642 } 643 644 return NS_OK; 645 } 646 647 } // namespace mozilla::dom