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 "IDBFileHandle.h"
8 
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"
24 
25 namespace mozilla::dom {
26 
27 using namespace mozilla::dom::indexedDB;
28 using namespace mozilla::ipc;
29 
30 namespace {
31 
GenerateFileRequest(IDBFileHandle * aFileHandle)32 RefPtr<IDBFileRequest> GenerateFileRequest(IDBFileHandle* aFileHandle) {
33   MOZ_ASSERT(aFileHandle);
34   aFileHandle->AssertIsOnOwningThread();
35 
36   return IDBFileRequest::Create(aFileHandle, /* aWrapAsDOMRequest */ false);
37 }
38 
39 }  // namespace
40 
IDBFileHandle(IDBMutableFile * aMutableFile,FileMode aMode)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
52       ,
53       mSentFinishOrAbort(false),
54       mFiredCompleteOrAbort(false)
55 #endif
56 {
57   MOZ_ASSERT(aMutableFile);
58   aMutableFile->AssertIsOnOwningThread();
59 }
60 
~IDBFileHandle()61 IDBFileHandle::~IDBFileHandle() {
62   AssertIsOnOwningThread();
63   MOZ_ASSERT(!mPendingRequestCount);
64   MOZ_ASSERT(!mCreating);
65   MOZ_ASSERT(mSentFinishOrAbort);
66   MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort);
67 
68   mMutableFile->UnregisterFileHandle(this);
69 
70   if (mBackgroundActor) {
71     mBackgroundActor->SendDeleteMeInternal();
72 
73     MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
74   }
75 }
76 
77 // static
Create(IDBMutableFile * aMutableFile,FileMode aMode)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);
83 
84   RefPtr<IDBFileHandle> fileHandle = new IDBFileHandle(aMutableFile, aMode);
85 
86   // XXX Fix!
87   MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
88 
89   nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
90   nsContentUtils::AddPendingIDBTransaction(runnable.forget());
91 
92   fileHandle->mCreating = true;
93 
94   aMutableFile->RegisterFileHandle(fileHandle);
95 
96   return fileHandle;
97 }
98 
99 // static
GetCurrent()100 IDBFileHandle* IDBFileHandle::GetCurrent() {
101   MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
102 
103   BackgroundChildImpl::ThreadLocal* threadLocal =
104       BackgroundChildImpl::GetThreadLocalForCurrentThread();
105   MOZ_ASSERT(threadLocal);
106 
107   return threadLocal->mCurrentFileHandle;
108 }
109 
110 #ifdef DEBUG
111 
AssertIsOnOwningThread() const112 void IDBFileHandle::AssertIsOnOwningThread() const {
113   MOZ_ASSERT(mMutableFile);
114   mMutableFile->AssertIsOnOwningThread();
115 }
116 
117 #endif  // DEBUG
118 
SetBackgroundActor(BackgroundFileHandleChild * aActor)119 void IDBFileHandle::SetBackgroundActor(BackgroundFileHandleChild* aActor) {
120   AssertIsOnOwningThread();
121   MOZ_ASSERT(aActor);
122   MOZ_ASSERT(!mBackgroundActor);
123 
124   mBackgroundActor = aActor;
125 }
126 
StartRequest(IDBFileRequest * aFileRequest,const FileRequestParams & aParams)127 void IDBFileHandle::StartRequest(IDBFileRequest* aFileRequest,
128                                  const FileRequestParams& aParams) {
129   AssertIsOnOwningThread();
130   MOZ_ASSERT(aFileRequest);
131   MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
132 
133   BackgroundFileRequestChild* actor =
134       new BackgroundFileRequestChild(aFileRequest);
135 
136   mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams);
137 
138   // Balanced in BackgroundFileRequestChild::Recv__delete__().
139   OnNewRequest();
140 }
141 
OnNewRequest()142 void IDBFileHandle::OnNewRequest() {
143   AssertIsOnOwningThread();
144 
145   if (!mPendingRequestCount) {
146     MOZ_ASSERT(mReadyState == INITIAL);
147     mReadyState = LOADING;
148   }
149 
150   ++mPendingRequestCount;
151 }
152 
OnRequestFinished(bool aActorDestroyedNormally)153 void IDBFileHandle::OnRequestFinished(bool aActorDestroyedNormally) {
154   AssertIsOnOwningThread();
155   MOZ_ASSERT(mPendingRequestCount);
156 
157   --mPendingRequestCount;
158 
159   if (!mPendingRequestCount && !mMutableFile->IsInvalidated()) {
160     mReadyState = FINISHING;
161 
162     if (aActorDestroyedNormally) {
163       if (!mAborted) {
164         SendFinish();
165       } else {
166         SendAbort();
167       }
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
175     }
176   }
177 }
178 
FireCompleteOrAbortEvents(bool aAborted)179 void IDBFileHandle::FireCompleteOrAbortEvents(bool aAborted) {
180   AssertIsOnOwningThread();
181   MOZ_ASSERT(!mFiredCompleteOrAbort);
182 
183   mReadyState = DONE;
184 
185 #ifdef DEBUG
186   mFiredCompleteOrAbort = true;
187 #endif
188 
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);
196 
197   IgnoredErrorResult rv;
198   DispatchEvent(*event, rv);
199   if (rv.Failed()) {
200     NS_WARNING("DispatchEvent failed!");
201   }
202 }
203 
IsOpen() const204 bool IDBFileHandle::IsOpen() const {
205   AssertIsOnOwningThread();
206 
207   // If we haven't started anything then we're open.
208   if (mReadyState == INITIAL) {
209     return true;
210   }
211 
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;
219   }
220 
221   return false;
222 }
223 
Abort()224 void IDBFileHandle::Abort() {
225   AssertIsOnOwningThread();
226 
227   if (IsFinishingOrDone()) {
228     // Already started (and maybe finished) the finish or abort so there is
229     // nothing to do here.
230     return;
231   }
232 
233   const bool isInvalidated = mMutableFile->IsInvalidated();
234   bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
235 
236 #ifdef DEBUG
237   if (isInvalidated) {
238     mSentFinishOrAbort = true;
239   }
240 #endif
241 
242   mAborted = true;
243   mReadyState = DONE;
244 
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();
249   }
250 }
251 
GetMetadata(const IDBFileMetadataParameters & aParameters,ErrorResult & aRv)252 RefPtr<IDBFileRequest> IDBFileHandle::GetMetadata(
253     const IDBFileMetadataParameters& aParameters, ErrorResult& aRv) {
254   AssertIsOnOwningThread();
255 
256   // Common state checking
257   if (!CheckState(aRv)) {
258     return nullptr;
259   }
260 
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;
265   }
266 
267   // Do nothing if the window is closed
268   if (!CheckWindow()) {
269     return nullptr;
270   }
271 
272   FileRequestGetMetadataParams params;
273   params.size() = aParameters.mSize;
274   params.lastModified() = aParameters.mLastModified;
275 
276   auto fileRequest = GenerateFileRequest(this);
277 
278   StartRequest(fileRequest, params);
279 
280   return fileRequest;
281 }
282 
Truncate(const Optional<uint64_t> & aSize,ErrorResult & aRv)283 RefPtr<IDBFileRequest> IDBFileHandle::Truncate(const Optional<uint64_t>& aSize,
284                                                ErrorResult& aRv) {
285   AssertIsOnOwningThread();
286 
287   // State checking for write
288   if (!CheckStateForWrite(aRv)) {
289     return nullptr;
290   }
291 
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.
298 
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;
304     }
305     location = aSize.Value();
306   } else {
307     // Fail if we are in append mode.
308 
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;
314     }
315     location = mLocation;
316   }
317 
318   // Do nothing if the window is closed
319   if (!CheckWindow()) {
320     return nullptr;
321   }
322 
323   FileRequestTruncateParams params;
324   params.offset() = location;
325 
326   auto fileRequest = GenerateFileRequest(this);
327 
328   StartRequest(fileRequest, params);
329 
330   if (aSize.WasPassed()) {
331     mLocation = aSize.Value();
332   }
333 
334   return fileRequest;
335 }
336 
Flush(ErrorResult & aRv)337 RefPtr<IDBFileRequest> IDBFileHandle::Flush(ErrorResult& aRv) {
338   AssertIsOnOwningThread();
339 
340   // State checking for write
341   if (!CheckStateForWrite(aRv)) {
342     return nullptr;
343   }
344 
345   // Do nothing if the window is closed
346   if (!CheckWindow()) {
347     return nullptr;
348   }
349 
350   FileRequestFlushParams params;
351 
352   auto fileRequest = GenerateFileRequest(this);
353 
354   StartRequest(fileRequest, params);
355 
356   return fileRequest;
357 }
358 
Abort(ErrorResult & aRv)359 void IDBFileHandle::Abort(ErrorResult& aRv) {
360   AssertIsOnOwningThread();
361 
362   // This method is special enough for not using generic state checking methods.
363 
364   if (IsFinishingOrDone()) {
365     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
366     return;
367   }
368 
369   Abort();
370 }
371 
CheckState(ErrorResult & aRv)372 bool IDBFileHandle::CheckState(ErrorResult& aRv) {
373   if (!IsOpen()) {
374     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
375     return false;
376   }
377 
378   return true;
379 }
380 
CheckStateAndArgumentsForRead(uint64_t aSize,ErrorResult & aRv)381 bool IDBFileHandle::CheckStateAndArgumentsForRead(uint64_t aSize,
382                                                   ErrorResult& aRv) {
383   // Common state checking
384   if (!CheckState(aRv)) {
385     return false;
386   }
387 
388   // Additional state checking for read
389   if (mLocation == UINT64_MAX) {
390     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
391     return false;
392   }
393 
394   // Argument checking for read
395   if (!aSize) {
396     aRv.ThrowTypeError("0 (Zero) is not a valid read size.");
397     return false;
398   }
399 
400   if (aSize > UINT32_MAX) {
401     aRv.ThrowTypeError("Data size for read is too large.");
402     return false;
403   }
404 
405   return true;
406 }
407 
CheckStateForWrite(ErrorResult & aRv)408 bool IDBFileHandle::CheckStateForWrite(ErrorResult& aRv) {
409   // Common state checking
410   if (!CheckState(aRv)) {
411     return false;
412   }
413 
414   // Additional state checking for write
415   if (mMode != FileMode::Readwrite) {
416     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
417     return false;
418   }
419 
420   return true;
421 }
422 
CheckStateForWriteOrAppend(bool aAppend,ErrorResult & aRv)423 bool IDBFileHandle::CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv) {
424   // State checking for write
425   if (!CheckStateForWrite(aRv)) {
426     return false;
427   }
428 
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;
433   }
434 
435   return true;
436 }
437 
CheckWindow()438 bool IDBFileHandle::CheckWindow() {
439   AssertIsOnOwningThread();
440 
441   return GetOwner();
442 }
443 
Read(uint64_t aSize,bool aHasEncoding,const nsAString & aEncoding,ErrorResult & aRv)444 RefPtr<IDBFileRequest> IDBFileHandle::Read(uint64_t aSize, bool aHasEncoding,
445                                            const nsAString& aEncoding,
446                                            ErrorResult& aRv) {
447   AssertIsOnOwningThread();
448 
449   // State and argument checking for read
450   if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
451     return nullptr;
452   }
453 
454   // Do nothing if the window is closed
455   if (!CheckWindow()) {
456     return nullptr;
457   }
458 
459   FileRequestReadParams params;
460   params.offset() = mLocation;
461   params.size() = aSize;
462 
463   auto fileRequest = GenerateFileRequest(this);
464   if (aHasEncoding) {
465     fileRequest->SetEncoding(aEncoding);
466   }
467 
468   StartRequest(fileRequest, params);
469 
470   mLocation += aSize;
471 
472   return fileRequest;
473 }
474 
WriteOrAppend(const StringOrArrayBufferOrArrayBufferViewOrBlob & aValue,bool aAppend,ErrorResult & aRv)475 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(
476     const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue, bool aAppend,
477     ErrorResult& aRv) {
478   AssertIsOnOwningThread();
479 
480   if (aValue.IsString()) {
481     return WriteOrAppend(aValue.GetAsString(), aAppend, aRv);
482   }
483 
484   if (aValue.IsArrayBuffer()) {
485     return WriteOrAppend(aValue.GetAsArrayBuffer(), aAppend, aRv);
486   }
487 
488   if (aValue.IsArrayBufferView()) {
489     return WriteOrAppend(aValue.GetAsArrayBufferView(), aAppend, aRv);
490   }
491 
492   MOZ_ASSERT(aValue.IsBlob());
493   return WriteOrAppend(aValue.GetAsBlob(), aAppend, aRv);
494 }
495 
WriteOrAppend(const nsAString & aValue,bool aAppend,ErrorResult & aRv)496 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(const nsAString& aValue,
497                                                     bool aAppend,
498                                                     ErrorResult& aRv) {
499   AssertIsOnOwningThread();
500 
501   // State checking for write or append
502   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
503     return nullptr;
504   }
505 
506   NS_ConvertUTF16toUTF8 cstr(aValue);
507 
508   uint64_t dataLength = cstr.Length();
509   ;
510   if (!dataLength) {
511     return nullptr;
512   }
513 
514   FileRequestStringData stringData(cstr);
515 
516   // Do nothing if the window is closed
517   if (!CheckWindow()) {
518     return nullptr;
519   }
520 
521   return WriteInternal(stringData, dataLength, aAppend, aRv);
522 }
523 
WriteOrAppend(const ArrayBuffer & aValue,bool aAppend,ErrorResult & aRv)524 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(const ArrayBuffer& aValue,
525                                                     bool aAppend,
526                                                     ErrorResult& aRv) {
527   AssertIsOnOwningThread();
528 
529   // State checking for write or append
530   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
531     return nullptr;
532   }
533 
534   aValue.ComputeState();
535 
536   uint64_t dataLength = aValue.Length();
537   ;
538   if (!dataLength) {
539     return nullptr;
540   }
541 
542   const char* data = reinterpret_cast<const char*>(aValue.Data());
543 
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;
549   }
550 
551   // Do nothing if the window is closed
552   if (!CheckWindow()) {
553     return nullptr;
554   }
555 
556   return WriteInternal(stringData, dataLength, aAppend, aRv);
557 }
558 
WriteOrAppend(const ArrayBufferView & aValue,bool aAppend,ErrorResult & aRv)559 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(
560     const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv) {
561   AssertIsOnOwningThread();
562 
563   // State checking for write or append
564   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
565     return nullptr;
566   }
567 
568   aValue.ComputeState();
569 
570   uint64_t dataLength = aValue.Length();
571   ;
572   if (!dataLength) {
573     return nullptr;
574   }
575 
576   const char* data = reinterpret_cast<const char*>(aValue.Data());
577 
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;
583   }
584 
585   // Do nothing if the window is closed
586   if (!CheckWindow()) {
587     return nullptr;
588   }
589 
590   return WriteInternal(stringData, dataLength, aAppend, aRv);
591 }
592 
WriteOrAppend(Blob & aValue,bool aAppend,ErrorResult & aRv)593 RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(Blob& aValue, bool aAppend,
594                                                     ErrorResult& aRv) {
595   AssertIsOnOwningThread();
596 
597   // State checking for write or append
598   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
599     return nullptr;
600   }
601 
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;
607   }
608 
609   if (!dataLength) {
610     return nullptr;
611   }
612 
613   PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread();
614   MOZ_ASSERT(backgroundActor);
615 
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;
622   }
623 
624   FileRequestBlobData blobData;
625   blobData.blob() = ipcBlob;
626 
627   // Do nothing if the window is closed
628   if (!CheckWindow()) {
629     return nullptr;
630   }
631 
632   return WriteInternal(blobData, dataLength, aAppend, aRv);
633 }
634 
WriteInternal(const FileRequestData & aData,uint64_t aDataLength,bool aAppend,ErrorResult & aRv)635 RefPtr<IDBFileRequest> IDBFileHandle::WriteInternal(
636     const FileRequestData& aData, uint64_t aDataLength, bool aAppend,
637     ErrorResult& aRv) {
638   AssertIsOnOwningThread();
639 
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());
645 
646   FileRequestWriteParams params;
647   params.offset() = aAppend ? UINT64_MAX : mLocation;
648   params.data() = aData;
649   params.dataLength() = aDataLength;
650 
651   auto fileRequest = GenerateFileRequest(this);
652   MOZ_ASSERT(fileRequest);
653 
654   StartRequest(fileRequest, params);
655 
656   if (aAppend) {
657     mLocation = UINT64_MAX;
658   } else {
659     mLocation += aDataLength;
660   }
661 
662   return fileRequest;
663 }
664 
SendFinish()665 void IDBFileHandle::SendFinish() {
666   AssertIsOnOwningThread();
667   MOZ_ASSERT(!mAborted);
668   MOZ_ASSERT(IsFinishingOrDone());
669   MOZ_ASSERT(!mSentFinishOrAbort);
670   MOZ_ASSERT(!mPendingRequestCount);
671 
672   MOZ_ASSERT(mBackgroundActor);
673   mBackgroundActor->SendFinish();
674 
675 #ifdef DEBUG
676   mSentFinishOrAbort = true;
677 #endif
678 }
679 
SendAbort()680 void IDBFileHandle::SendAbort() {
681   AssertIsOnOwningThread();
682   MOZ_ASSERT(mAborted);
683   MOZ_ASSERT(IsFinishingOrDone());
684   MOZ_ASSERT(!mSentFinishOrAbort);
685 
686   MOZ_ASSERT(mBackgroundActor);
687   mBackgroundActor->SendAbort();
688 
689 #ifdef DEBUG
690   mSentFinishOrAbort = true;
691 #endif
692 }
693 
NS_IMPL_ADDREF_INHERITED(IDBFileHandle,DOMEventTargetHelper)694 NS_IMPL_ADDREF_INHERITED(IDBFileHandle, DOMEventTargetHelper)
695 NS_IMPL_RELEASE_INHERITED(IDBFileHandle, DOMEventTargetHelper)
696 
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)
701 
702 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFileHandle)
703 
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
708 
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
714 
715 NS_IMETHODIMP
716 IDBFileHandle::Run() {
717   AssertIsOnOwningThread();
718 
719   // We're back at the event loop, no longer newborn.
720   mCreating = false;
721 
722   // Maybe finish if there were no requests generated.
723   if (mReadyState == INITIAL) {
724     mReadyState = DONE;
725 
726     SendFinish();
727   }
728 
729   return NS_OK;
730 }
731 
GetEventTargetParent(EventChainPreVisitor & aVisitor)732 void IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
733   AssertIsOnOwningThread();
734 
735   aVisitor.mCanHandle = true;
736   aVisitor.SetParentTarget(mMutableFile, false);
737 }
738 
739 // virtual
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)740 JSObject* IDBFileHandle::WrapObject(JSContext* aCx,
741                                     JS::Handle<JSObject*> aGivenProto) {
742   AssertIsOnOwningThread();
743 
744   return IDBFileHandle_Binding::Wrap(aCx, this, aGivenProto);
745 }
746 
747 }  // namespace mozilla::dom
748