Bug 1755316 - Add audio tests with simultaneous processes r=alwu
[gecko.git] / dom / indexedDB / IDBFileHandle.cpp
blobf3286fe77413fa3a81343e47a96a239907773f53
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "IDBFileHandle.h"
9 #include "ActorsChild.h"
10 #include "BackgroundChildImpl.h"
11 #include "IDBEvents.h"
12 #include "IDBMutableFile.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/dom/File.h"
15 #include "mozilla/dom/IDBFileHandleBinding.h"
16 #include "mozilla/dom/IPCBlobUtils.h"
17 #include "mozilla/dom/PBackgroundFileHandle.h"
18 #include "mozilla/EventDispatcher.h"
19 #include "mozilla/ipc/BackgroundChild.h"
20 #include "nsContentUtils.h"
21 #include "nsQueryObject.h"
22 #include "nsServiceManagerUtils.h"
23 #include "nsWidgetsCID.h"
25 namespace mozilla::dom {
27 using namespace mozilla::dom::indexedDB;
28 using namespace mozilla::ipc;
30 namespace {
32 RefPtr<IDBFileRequest> GenerateFileRequest(IDBFileHandle* aFileHandle) {
33 MOZ_ASSERT(aFileHandle);
34 aFileHandle->AssertIsOnOwningThread();
36 return IDBFileRequest::Create(aFileHandle, /* aWrapAsDOMRequest */ false);
39 } // namespace
41 IDBFileHandle::IDBFileHandle(IDBMutableFile* aMutableFile, FileMode aMode)
42 : DOMEventTargetHelper(aMutableFile),
43 mMutableFile(aMutableFile),
44 mBackgroundActor(nullptr),
45 mLocation(0),
46 mPendingRequestCount(0),
47 mReadyState(INITIAL),
48 mMode(aMode),
49 mAborted(false),
50 mCreating(false)
51 #ifdef DEBUG
53 mSentFinishOrAbort(false),
54 mFiredCompleteOrAbort(false)
55 #endif
57 MOZ_ASSERT(aMutableFile);
58 aMutableFile->AssertIsOnOwningThread();
61 IDBFileHandle::~IDBFileHandle() {
62 AssertIsOnOwningThread();
63 MOZ_ASSERT(!mPendingRequestCount);
64 MOZ_ASSERT(!mCreating);
65 MOZ_ASSERT(mSentFinishOrAbort);
66 MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort);
68 mMutableFile->UnregisterFileHandle(this);
70 if (mBackgroundActor) {
71 mBackgroundActor->SendDeleteMeInternal();
73 MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
77 // static
78 RefPtr<IDBFileHandle> IDBFileHandle::Create(IDBMutableFile* aMutableFile,
79 FileMode aMode) {
80 MOZ_ASSERT(aMutableFile);
81 aMutableFile->AssertIsOnOwningThread();
82 MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
84 RefPtr<IDBFileHandle> fileHandle = new IDBFileHandle(aMutableFile, aMode);
86 // XXX Fix!
87 MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
89 nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
90 nsContentUtils::AddPendingIDBTransaction(runnable.forget());
92 fileHandle->mCreating = true;
94 aMutableFile->RegisterFileHandle(fileHandle);
96 return fileHandle;
99 // static
100 IDBFileHandle* IDBFileHandle::GetCurrent() {
101 MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
103 BackgroundChildImpl::ThreadLocal* threadLocal =
104 BackgroundChildImpl::GetThreadLocalForCurrentThread();
105 MOZ_ASSERT(threadLocal);
107 return threadLocal->mCurrentFileHandle;
110 #ifdef DEBUG
112 void IDBFileHandle::AssertIsOnOwningThread() const {
113 MOZ_ASSERT(mMutableFile);
114 mMutableFile->AssertIsOnOwningThread();
117 #endif // DEBUG
119 void IDBFileHandle::SetBackgroundActor(BackgroundFileHandleChild* aActor) {
120 AssertIsOnOwningThread();
121 MOZ_ASSERT(aActor);
122 MOZ_ASSERT(!mBackgroundActor);
124 mBackgroundActor = aActor;
127 void IDBFileHandle::StartRequest(IDBFileRequest* aFileRequest,
128 const FileRequestParams& aParams) {
129 AssertIsOnOwningThread();
130 MOZ_ASSERT(aFileRequest);
131 MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
133 BackgroundFileRequestChild* actor =
134 new BackgroundFileRequestChild(aFileRequest);
136 mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams);
138 // Balanced in BackgroundFileRequestChild::Recv__delete__().
139 OnNewRequest();
142 void IDBFileHandle::OnNewRequest() {
143 AssertIsOnOwningThread();
145 if (!mPendingRequestCount) {
146 MOZ_ASSERT(mReadyState == INITIAL);
147 mReadyState = LOADING;
150 ++mPendingRequestCount;
153 void IDBFileHandle::OnRequestFinished(bool aActorDestroyedNormally) {
154 AssertIsOnOwningThread();
155 MOZ_ASSERT(mPendingRequestCount);
157 --mPendingRequestCount;
159 if (!mPendingRequestCount && !mMutableFile->IsInvalidated()) {
160 mReadyState = FINISHING;
162 if (aActorDestroyedNormally) {
163 if (!mAborted) {
164 SendFinish();
165 } else {
166 SendAbort();
168 } else {
169 // Don't try to send any more messages to the parent if the request actor
170 // was killed.
171 #ifdef DEBUG
172 MOZ_ASSERT(!mSentFinishOrAbort);
173 mSentFinishOrAbort = true;
174 #endif
179 void IDBFileHandle::FireCompleteOrAbortEvents(bool aAborted) {
180 AssertIsOnOwningThread();
181 MOZ_ASSERT(!mFiredCompleteOrAbort);
183 mReadyState = DONE;
185 #ifdef DEBUG
186 mFiredCompleteOrAbort = true;
187 #endif
189 // TODO: Why is it necessary to create the Event on the heap at all?
190 const auto event = CreateGenericEvent(
191 this,
192 aAborted ? nsDependentString(kAbortEventType)
193 : nsDependentString(kCompleteEventType),
194 aAborted ? eDoesBubble : eDoesNotBubble, eNotCancelable);
195 MOZ_ASSERT(event);
197 IgnoredErrorResult rv;
198 DispatchEvent(*event, rv);
199 if (rv.Failed()) {
200 NS_WARNING("DispatchEvent failed!");
204 bool IDBFileHandle::IsOpen() const {
205 AssertIsOnOwningThread();
207 // If we haven't started anything then we're open.
208 if (mReadyState == INITIAL) {
209 return true;
212 // If we've already started then we need to check to see if we still have the
213 // mCreating flag set. If we do (i.e. we haven't returned to the event loop
214 // from the time we were created) then we are open. Otherwise check the
215 // currently running file handles to see if it's the same. We only allow other
216 // requests to be made if this file handle is currently running.
217 if (mReadyState == LOADING && (mCreating || GetCurrent() == this)) {
218 return true;
221 return false;
224 void IDBFileHandle::Abort() {
225 AssertIsOnOwningThread();
227 if (IsFinishingOrDone()) {
228 // Already started (and maybe finished) the finish or abort so there is
229 // nothing to do here.
230 return;
233 const bool isInvalidated = mMutableFile->IsInvalidated();
234 bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
236 #ifdef DEBUG
237 if (isInvalidated) {
238 mSentFinishOrAbort = true;
240 #endif
242 mAborted = true;
243 mReadyState = DONE;
245 // Fire the abort event if there are no outstanding requests. Otherwise the
246 // abort event will be fired when all outstanding requests finish.
247 if (needToSendAbort) {
248 SendAbort();
252 RefPtr<IDBFileRequest> IDBFileHandle::GetMetadata(
253 const IDBFileMetadataParameters& aParameters, ErrorResult& aRv) {
254 AssertIsOnOwningThread();
256 // Common state checking
257 if (!CheckState(aRv)) {
258 return nullptr;
261 // Argument checking for get metadata.
262 if (!aParameters.mSize && !aParameters.mLastModified) {
263 aRv.ThrowTypeError("Either size or lastModified should be true.");
264 return nullptr;
267 // Do nothing if the window is closed
268 if (!CheckWindow()) {
269 return nullptr;
272 FileRequestGetMetadataParams params;
273 params.size() = aParameters.mSize;
274 params.lastModified() = aParameters.mLastModified;
276 auto fileRequest = GenerateFileRequest(this);
278 StartRequest(fileRequest, params);
280 return fileRequest;
283 RefPtr<IDBFileRequest> IDBFileHandle::Truncate(const Optional<uint64_t>& aSize,
284 ErrorResult& aRv) {
285 AssertIsOnOwningThread();
287 // State checking for write
288 if (!CheckStateForWrite(aRv)) {
289 return nullptr;
292 // Getting location and additional state checking for truncate
293 uint64_t location;
294 if (aSize.WasPassed()) {
295 // Cannot use UINT64_MAX as the truncation size, as this is used as a
296 // special value for the location to mark append mode. This is not really of
297 // practical relevance, as a file cannot actually have a size that large.
299 // XXX: Remove this check when removing the use of UINT64_MAX as a special
300 // value for the location to mark append mode?
301 if (aSize.Value() == UINT64_MAX) {
302 aRv.ThrowTypeError("UINT64_MAX is not a valid size");
303 return nullptr;
305 location = aSize.Value();
306 } else {
307 // Fail if we are in append mode.
309 // XXX: Is it really ok that truncate with a size parameter works when in
310 // append mode, but one without a size parameter does not?
311 if (mLocation == UINT64_MAX) {
312 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
313 return nullptr;
315 location = mLocation;
318 // Do nothing if the window is closed
319 if (!CheckWindow()) {
320 return nullptr;
323 FileRequestTruncateParams params;
324 params.offset() = location;
326 auto fileRequest = GenerateFileRequest(this);
328 StartRequest(fileRequest, params);
330 if (aSize.WasPassed()) {
331 mLocation = aSize.Value();
334 return fileRequest;
337 RefPtr<IDBFileRequest> IDBFileHandle::Flush(ErrorResult& aRv) {
338 AssertIsOnOwningThread();
340 // State checking for write
341 if (!CheckStateForWrite(aRv)) {
342 return nullptr;
345 // Do nothing if the window is closed
346 if (!CheckWindow()) {
347 return nullptr;
350 FileRequestFlushParams params;
352 auto fileRequest = GenerateFileRequest(this);
354 StartRequest(fileRequest, params);
356 return fileRequest;
359 void IDBFileHandle::Abort(ErrorResult& aRv) {
360 AssertIsOnOwningThread();
362 // This method is special enough for not using generic state checking methods.
364 if (IsFinishingOrDone()) {
365 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
366 return;
369 Abort();
372 bool IDBFileHandle::CheckState(ErrorResult& aRv) {
373 if (!IsOpen()) {
374 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
375 return false;
378 return true;
381 bool IDBFileHandle::CheckStateAndArgumentsForRead(uint64_t aSize,
382 ErrorResult& aRv) {
383 // Common state checking
384 if (!CheckState(aRv)) {
385 return false;
388 // Additional state checking for read
389 if (mLocation == UINT64_MAX) {
390 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
391 return false;
394 // Argument checking for read
395 if (!aSize) {
396 aRv.ThrowTypeError("0 (Zero) is not a valid read size.");
397 return false;
400 if (aSize > UINT32_MAX) {
401 aRv.ThrowTypeError("Data size for read is too large.");
402 return false;
405 return true;
408 bool IDBFileHandle::CheckStateForWrite(ErrorResult& aRv) {
409 // Common state checking
410 if (!CheckState(aRv)) {
411 return false;
414 // Additional state checking for write
415 if (mMode != FileMode::Readwrite) {
416 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
417 return false;
420 return true;
423 bool IDBFileHandle::CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv) {
424 // State checking for write
425 if (!CheckStateForWrite(aRv)) {
426 return false;
429 // Additional state checking for write
430 if (!aAppend && mLocation == UINT64_MAX) {
431 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
432 return false;
435 return true;
438 bool IDBFileHandle::CheckWindow() {
439 AssertIsOnOwningThread();
441 return GetOwner();
444 RefPtr<IDBFileRequest> IDBFileHandle::Read(uint64_t aSize, bool aHasEncoding,
445 const nsAString& aEncoding,
446 ErrorResult& aRv) {
447 AssertIsOnOwningThread();
449 // State and argument checking for read
450 if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
451 return nullptr;
454 // Do nothing if the window is closed
455 if (!CheckWindow()) {
456 return nullptr;
459 FileRequestReadParams params;
460 params.offset() = mLocation;
461 params.size() = aSize;
463 auto fileRequest = GenerateFileRequest(this);
464 if (aHasEncoding) {
465 fileRequest->SetEncoding(aEncoding);
468 StartRequest(fileRequest, params);
470 mLocation += aSize;
472 return fileRequest;
475 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(
476 const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue, bool aAppend,
477 ErrorResult& aRv) {
478 AssertIsOnOwningThread();
480 if (aValue.IsString()) {
481 return WriteOrAppend(aValue.GetAsString(), aAppend, aRv);
484 if (aValue.IsArrayBuffer()) {
485 return WriteOrAppend(aValue.GetAsArrayBuffer(), aAppend, aRv);
488 if (aValue.IsArrayBufferView()) {
489 return WriteOrAppend(aValue.GetAsArrayBufferView(), aAppend, aRv);
492 MOZ_ASSERT(aValue.IsBlob());
493 return WriteOrAppend(aValue.GetAsBlob(), aAppend, aRv);
496 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(const nsAString& aValue,
497 bool aAppend,
498 ErrorResult& aRv) {
499 AssertIsOnOwningThread();
501 // State checking for write or append
502 if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
503 return nullptr;
506 NS_ConvertUTF16toUTF8 cstr(aValue);
508 uint64_t dataLength = cstr.Length();
510 if (!dataLength) {
511 return nullptr;
514 FileRequestStringData stringData(cstr);
516 // Do nothing if the window is closed
517 if (!CheckWindow()) {
518 return nullptr;
521 return WriteInternal(stringData, dataLength, aAppend, aRv);
524 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(const ArrayBuffer& aValue,
525 bool aAppend,
526 ErrorResult& aRv) {
527 AssertIsOnOwningThread();
529 // State checking for write or append
530 if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
531 return nullptr;
534 aValue.ComputeState();
536 uint64_t dataLength = aValue.Length();
538 if (!dataLength) {
539 return nullptr;
542 const char* data = reinterpret_cast<const char*>(aValue.Data());
544 FileRequestStringData stringData;
545 if (NS_WARN_IF(
546 !stringData.string().Assign(data, aValue.Length(), fallible_t()))) {
547 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
548 return nullptr;
551 // Do nothing if the window is closed
552 if (!CheckWindow()) {
553 return nullptr;
556 return WriteInternal(stringData, dataLength, aAppend, aRv);
559 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(
560 const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv) {
561 AssertIsOnOwningThread();
563 // State checking for write or append
564 if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
565 return nullptr;
568 aValue.ComputeState();
570 uint64_t dataLength = aValue.Length();
572 if (!dataLength) {
573 return nullptr;
576 const char* data = reinterpret_cast<const char*>(aValue.Data());
578 FileRequestStringData stringData;
579 if (NS_WARN_IF(
580 !stringData.string().Assign(data, aValue.Length(), fallible_t()))) {
581 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
582 return nullptr;
585 // Do nothing if the window is closed
586 if (!CheckWindow()) {
587 return nullptr;
590 return WriteInternal(stringData, dataLength, aAppend, aRv);
593 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(Blob& aValue, bool aAppend,
594 ErrorResult& aRv) {
595 AssertIsOnOwningThread();
597 // State checking for write or append
598 if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
599 return nullptr;
602 ErrorResult error;
603 uint64_t dataLength = aValue.GetSize(error);
604 if (NS_WARN_IF(error.Failed())) {
605 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
606 return nullptr;
609 if (!dataLength) {
610 return nullptr;
613 PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread();
614 MOZ_ASSERT(backgroundActor);
616 IPCBlob ipcBlob;
617 nsresult rv =
618 IPCBlobUtils::Serialize(aValue.Impl(), backgroundActor, ipcBlob);
619 if (NS_WARN_IF(NS_FAILED(rv))) {
620 aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
621 return nullptr;
624 FileRequestBlobData blobData;
625 blobData.blob() = ipcBlob;
627 // Do nothing if the window is closed
628 if (!CheckWindow()) {
629 return nullptr;
632 return WriteInternal(blobData, dataLength, aAppend, aRv);
635 RefPtr<IDBFileRequest> IDBFileHandle::WriteInternal(
636 const FileRequestData& aData, uint64_t aDataLength, bool aAppend,
637 ErrorResult& aRv) {
638 AssertIsOnOwningThread();
640 DebugOnly<ErrorResult> error;
641 MOZ_ASSERT(CheckStateForWrite(error));
642 MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
643 MOZ_ASSERT(aDataLength);
644 MOZ_ASSERT(CheckWindow());
646 FileRequestWriteParams params;
647 params.offset() = aAppend ? UINT64_MAX : mLocation;
648 params.data() = aData;
649 params.dataLength() = aDataLength;
651 auto fileRequest = GenerateFileRequest(this);
652 MOZ_ASSERT(fileRequest);
654 StartRequest(fileRequest, params);
656 if (aAppend) {
657 mLocation = UINT64_MAX;
658 } else {
659 mLocation += aDataLength;
662 return fileRequest;
665 void IDBFileHandle::SendFinish() {
666 AssertIsOnOwningThread();
667 MOZ_ASSERT(!mAborted);
668 MOZ_ASSERT(IsFinishingOrDone());
669 MOZ_ASSERT(!mSentFinishOrAbort);
670 MOZ_ASSERT(!mPendingRequestCount);
672 MOZ_ASSERT(mBackgroundActor);
673 mBackgroundActor->SendFinish();
675 #ifdef DEBUG
676 mSentFinishOrAbort = true;
677 #endif
680 void IDBFileHandle::SendAbort() {
681 AssertIsOnOwningThread();
682 MOZ_ASSERT(mAborted);
683 MOZ_ASSERT(IsFinishingOrDone());
684 MOZ_ASSERT(!mSentFinishOrAbort);
686 MOZ_ASSERT(mBackgroundActor);
687 mBackgroundActor->SendAbort();
689 #ifdef DEBUG
690 mSentFinishOrAbort = true;
691 #endif
694 NS_IMPL_ADDREF_INHERITED(IDBFileHandle, DOMEventTargetHelper)
695 NS_IMPL_RELEASE_INHERITED(IDBFileHandle, DOMEventTargetHelper)
697 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFileHandle)
698 NS_INTERFACE_MAP_ENTRY(nsIRunnable)
699 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
700 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
702 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFileHandle)
704 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBFileHandle,
705 DOMEventTargetHelper)
706 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMutableFile)
707 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
709 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBFileHandle,
710 DOMEventTargetHelper)
711 // Don't unlink mMutableFile!
712 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
713 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
715 NS_IMETHODIMP
716 IDBFileHandle::Run() {
717 AssertIsOnOwningThread();
719 // We're back at the event loop, no longer newborn.
720 mCreating = false;
722 // Maybe finish if there were no requests generated.
723 if (mReadyState == INITIAL) {
724 mReadyState = DONE;
726 SendFinish();
729 return NS_OK;
732 void IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
733 AssertIsOnOwningThread();
735 aVisitor.mCanHandle = true;
736 aVisitor.SetParentTarget(mMutableFile, false);
739 // virtual
740 JSObject* IDBFileHandle::WrapObject(JSContext* aCx,
741 JS::Handle<JSObject*> aGivenProto) {
742 AssertIsOnOwningThread();
744 return IDBFileHandle_Binding::Wrap(aCx, this, aGivenProto);
747 } // namespace mozilla::dom