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 {
26 namespace dom {
27 
28 using namespace mozilla::dom::indexedDB;
29 using namespace mozilla::ipc;
30 
31 namespace {
32 
GenerateFileRequest(IDBFileHandle * aFileHandle)33 already_AddRefed<IDBFileRequest> GenerateFileRequest(
34     IDBFileHandle* aFileHandle) {
35   MOZ_ASSERT(aFileHandle);
36   aFileHandle->AssertIsOnOwningThread();
37 
38   RefPtr<IDBFileRequest> fileRequest =
39       IDBFileRequest::Create(aFileHandle, /* aWrapAsDOMRequest */ false);
40   MOZ_ASSERT(fileRequest);
41 
42   return fileRequest.forget();
43 }
44 
45 }  // namespace
46 
IDBFileHandle(IDBMutableFile * aMutableFile,FileMode aMode)47 IDBFileHandle::IDBFileHandle(IDBMutableFile* aMutableFile, FileMode aMode)
48     : mMutableFile(aMutableFile),
49       mBackgroundActor(nullptr),
50       mLocation(0),
51       mPendingRequestCount(0),
52       mReadyState(INITIAL),
53       mMode(aMode),
54       mAborted(false),
55       mCreating(false)
56 #ifdef DEBUG
57       ,
58       mSentFinishOrAbort(false),
59       mFiredCompleteOrAbort(false)
60 #endif
61 {
62   MOZ_ASSERT(aMutableFile);
63   aMutableFile->AssertIsOnOwningThread();
64 }
65 
~IDBFileHandle()66 IDBFileHandle::~IDBFileHandle() {
67   AssertIsOnOwningThread();
68   MOZ_ASSERT(!mPendingRequestCount);
69   MOZ_ASSERT(!mCreating);
70   MOZ_ASSERT(mSentFinishOrAbort);
71   MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort);
72 
73   mMutableFile->UnregisterFileHandle(this);
74 
75   if (mBackgroundActor) {
76     mBackgroundActor->SendDeleteMeInternal();
77 
78     MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
79   }
80 }
81 
82 // static
Create(IDBMutableFile * aMutableFile,FileMode aMode)83 already_AddRefed<IDBFileHandle> IDBFileHandle::Create(
84     IDBMutableFile* aMutableFile, FileMode aMode) {
85   MOZ_ASSERT(aMutableFile);
86   aMutableFile->AssertIsOnOwningThread();
87   MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
88 
89   RefPtr<IDBFileHandle> fileHandle = new IDBFileHandle(aMutableFile, aMode);
90 
91   fileHandle->BindToOwner(aMutableFile);
92 
93   // XXX Fix!
94   MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
95 
96   nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
97   nsContentUtils::AddPendingIDBTransaction(runnable.forget());
98 
99   fileHandle->mCreating = true;
100 
101   aMutableFile->RegisterFileHandle(fileHandle);
102 
103   return fileHandle.forget();
104 }
105 
106 // static
GetCurrent()107 IDBFileHandle* IDBFileHandle::GetCurrent() {
108   MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
109 
110   BackgroundChildImpl::ThreadLocal* threadLocal =
111       BackgroundChildImpl::GetThreadLocalForCurrentThread();
112   MOZ_ASSERT(threadLocal);
113 
114   return threadLocal->mCurrentFileHandle;
115 }
116 
117 #ifdef DEBUG
118 
AssertIsOnOwningThread() const119 void IDBFileHandle::AssertIsOnOwningThread() const {
120   MOZ_ASSERT(mMutableFile);
121   mMutableFile->AssertIsOnOwningThread();
122 }
123 
124 #endif  // DEBUG
125 
SetBackgroundActor(BackgroundFileHandleChild * aActor)126 void IDBFileHandle::SetBackgroundActor(BackgroundFileHandleChild* aActor) {
127   AssertIsOnOwningThread();
128   MOZ_ASSERT(aActor);
129   MOZ_ASSERT(!mBackgroundActor);
130 
131   mBackgroundActor = aActor;
132 }
133 
StartRequest(IDBFileRequest * aFileRequest,const FileRequestParams & aParams)134 void IDBFileHandle::StartRequest(IDBFileRequest* aFileRequest,
135                                  const FileRequestParams& aParams) {
136   AssertIsOnOwningThread();
137   MOZ_ASSERT(aFileRequest);
138   MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
139 
140   BackgroundFileRequestChild* actor =
141       new BackgroundFileRequestChild(aFileRequest);
142 
143   mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams);
144 
145   // Balanced in BackgroundFileRequestChild::Recv__delete__().
146   OnNewRequest();
147 }
148 
OnNewRequest()149 void IDBFileHandle::OnNewRequest() {
150   AssertIsOnOwningThread();
151 
152   if (!mPendingRequestCount) {
153     MOZ_ASSERT(mReadyState == INITIAL);
154     mReadyState = LOADING;
155   }
156 
157   ++mPendingRequestCount;
158 }
159 
OnRequestFinished(bool aActorDestroyedNormally)160 void IDBFileHandle::OnRequestFinished(bool aActorDestroyedNormally) {
161   AssertIsOnOwningThread();
162   MOZ_ASSERT(mPendingRequestCount);
163 
164   --mPendingRequestCount;
165 
166   if (!mPendingRequestCount && !mMutableFile->IsInvalidated()) {
167     mReadyState = FINISHING;
168 
169     if (aActorDestroyedNormally) {
170       if (!mAborted) {
171         SendFinish();
172       } else {
173         SendAbort();
174       }
175     } else {
176     // Don't try to send any more messages to the parent if the request actor
177     // was killed.
178 #ifdef DEBUG
179       MOZ_ASSERT(!mSentFinishOrAbort);
180       mSentFinishOrAbort = true;
181 #endif
182     }
183   }
184 }
185 
FireCompleteOrAbortEvents(bool aAborted)186 void IDBFileHandle::FireCompleteOrAbortEvents(bool aAborted) {
187   AssertIsOnOwningThread();
188   MOZ_ASSERT(!mFiredCompleteOrAbort);
189 
190   mReadyState = DONE;
191 
192 #ifdef DEBUG
193   mFiredCompleteOrAbort = true;
194 #endif
195 
196   nsCOMPtr<nsIDOMEvent> event;
197   if (aAborted) {
198     event = CreateGenericEvent(this, nsDependentString(kAbortEventType),
199                                eDoesBubble, eNotCancelable);
200   } else {
201     event = CreateGenericEvent(this, nsDependentString(kCompleteEventType),
202                                eDoesNotBubble, eNotCancelable);
203   }
204   if (NS_WARN_IF(!event)) {
205     return;
206   }
207 
208   bool dummy;
209   if (NS_FAILED(DispatchEvent(event, &dummy))) {
210     NS_WARNING("DispatchEvent failed!");
211   }
212 }
213 
IsOpen() const214 bool IDBFileHandle::IsOpen() const {
215   AssertIsOnOwningThread();
216 
217   // If we haven't started anything then we're open.
218   if (mReadyState == INITIAL) {
219     return true;
220   }
221 
222   // If we've already started then we need to check to see if we still have the
223   // mCreating flag set. If we do (i.e. we haven't returned to the event loop
224   // from the time we were created) then we are open. Otherwise check the
225   // currently running file handles to see if it's the same. We only allow other
226   // requests to be made if this file handle is currently running.
227   if (mReadyState == LOADING && (mCreating || GetCurrent() == this)) {
228     return true;
229   }
230 
231   return false;
232 }
233 
Abort()234 void IDBFileHandle::Abort() {
235   AssertIsOnOwningThread();
236 
237   if (IsFinishingOrDone()) {
238     // Already started (and maybe finished) the finish or abort so there is
239     // nothing to do here.
240     return;
241   }
242 
243   const bool isInvalidated = mMutableFile->IsInvalidated();
244   bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
245 
246 #ifdef DEBUG
247   if (isInvalidated) {
248     mSentFinishOrAbort = true;
249   }
250 #endif
251 
252   mAborted = true;
253   mReadyState = DONE;
254 
255   // Fire the abort event if there are no outstanding requests. Otherwise the
256   // abort event will be fired when all outstanding requests finish.
257   if (needToSendAbort) {
258     SendAbort();
259   }
260 }
261 
GetMetadata(const IDBFileMetadataParameters & aParameters,ErrorResult & aRv)262 already_AddRefed<IDBFileRequest> IDBFileHandle::GetMetadata(
263     const IDBFileMetadataParameters& aParameters, ErrorResult& aRv) {
264   AssertIsOnOwningThread();
265 
266   // Common state checking
267   if (!CheckState(aRv)) {
268     return nullptr;
269   }
270 
271   // Argument checking for get metadata.
272   if (!aParameters.mSize && !aParameters.mLastModified) {
273     aRv.ThrowTypeError<MSG_METADATA_NOT_CONFIGURED>();
274     return nullptr;
275   }
276 
277   // Do nothing if the window is closed
278   if (!CheckWindow()) {
279     return nullptr;
280   }
281 
282   FileRequestGetMetadataParams params;
283   params.size() = aParameters.mSize;
284   params.lastModified() = aParameters.mLastModified;
285 
286   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
287 
288   StartRequest(fileRequest, params);
289 
290   return fileRequest.forget();
291 }
292 
Truncate(const Optional<uint64_t> & aSize,ErrorResult & aRv)293 already_AddRefed<IDBFileRequest> IDBFileHandle::Truncate(
294     const Optional<uint64_t>& aSize, ErrorResult& aRv) {
295   AssertIsOnOwningThread();
296 
297   // State checking for write
298   if (!CheckStateForWrite(aRv)) {
299     return nullptr;
300   }
301 
302   // Getting location and additional state checking for truncate
303   uint64_t location;
304   if (aSize.WasPassed()) {
305     // Just in case someone calls us from C++
306     MOZ_ASSERT(aSize.Value() != UINT64_MAX, "Passed wrong size!");
307     location = aSize.Value();
308   } else {
309     if (mLocation == UINT64_MAX) {
310       aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
311       return nullptr;
312     }
313     location = mLocation;
314   }
315 
316   // Do nothing if the window is closed
317   if (!CheckWindow()) {
318     return nullptr;
319   }
320 
321   FileRequestTruncateParams params;
322   params.offset() = location;
323 
324   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
325 
326   StartRequest(fileRequest, params);
327 
328   if (aSize.WasPassed()) {
329     mLocation = aSize.Value();
330   }
331 
332   return fileRequest.forget();
333 }
334 
Flush(ErrorResult & aRv)335 already_AddRefed<IDBFileRequest> IDBFileHandle::Flush(ErrorResult& aRv) {
336   AssertIsOnOwningThread();
337 
338   // State checking for write
339   if (!CheckStateForWrite(aRv)) {
340     return nullptr;
341   }
342 
343   // Do nothing if the window is closed
344   if (!CheckWindow()) {
345     return nullptr;
346   }
347 
348   FileRequestFlushParams params;
349 
350   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
351 
352   StartRequest(fileRequest, params);
353 
354   return fileRequest.forget();
355 }
356 
Abort(ErrorResult & aRv)357 void IDBFileHandle::Abort(ErrorResult& aRv) {
358   AssertIsOnOwningThread();
359 
360   // This method is special enough for not using generic state checking methods.
361 
362   if (IsFinishingOrDone()) {
363     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
364     return;
365   }
366 
367   Abort();
368 }
369 
CheckState(ErrorResult & aRv)370 bool IDBFileHandle::CheckState(ErrorResult& aRv) {
371   if (!IsOpen()) {
372     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
373     return false;
374   }
375 
376   return true;
377 }
378 
CheckStateAndArgumentsForRead(uint64_t aSize,ErrorResult & aRv)379 bool IDBFileHandle::CheckStateAndArgumentsForRead(uint64_t aSize,
380                                                   ErrorResult& aRv) {
381   // Common state checking
382   if (!CheckState(aRv)) {
383     return false;
384   }
385 
386   // Additional state checking for read
387   if (mLocation == UINT64_MAX) {
388     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
389     return false;
390   }
391 
392   // Argument checking for read
393   if (!aSize) {
394     aRv.ThrowTypeError<MSG_INVALID_READ_SIZE>();
395     return false;
396   }
397 
398   return true;
399 }
400 
CheckStateForWrite(ErrorResult & aRv)401 bool IDBFileHandle::CheckStateForWrite(ErrorResult& aRv) {
402   // Common state checking
403   if (!CheckState(aRv)) {
404     return false;
405   }
406 
407   // Additional state checking for write
408   if (mMode != FileMode::Readwrite) {
409     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
410     return false;
411   }
412 
413   return true;
414 }
415 
CheckStateForWriteOrAppend(bool aAppend,ErrorResult & aRv)416 bool IDBFileHandle::CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv) {
417   // State checking for write
418   if (!CheckStateForWrite(aRv)) {
419     return false;
420   }
421 
422   // Additional state checking for write
423   if (!aAppend && mLocation == UINT64_MAX) {
424     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
425     return false;
426   }
427 
428   return true;
429 }
430 
CheckWindow()431 bool IDBFileHandle::CheckWindow() {
432   AssertIsOnOwningThread();
433 
434   return GetOwner();
435 }
436 
Read(uint64_t aSize,bool aHasEncoding,const nsAString & aEncoding,ErrorResult & aRv)437 already_AddRefed<IDBFileRequest> IDBFileHandle::Read(uint64_t aSize,
438                                                      bool aHasEncoding,
439                                                      const nsAString& aEncoding,
440                                                      ErrorResult& aRv) {
441   AssertIsOnOwningThread();
442 
443   // State and argument checking for read
444   if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
445     return nullptr;
446   }
447 
448   // Do nothing if the window is closed
449   if (!CheckWindow()) {
450     return nullptr;
451   }
452 
453   FileRequestReadParams params;
454   params.offset() = mLocation;
455   params.size() = aSize;
456 
457   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
458   if (aHasEncoding) {
459     fileRequest->SetEncoding(aEncoding);
460   }
461 
462   StartRequest(fileRequest, params);
463 
464   mLocation += aSize;
465 
466   return fileRequest.forget();
467 }
468 
WriteOrAppend(const StringOrArrayBufferOrArrayBufferViewOrBlob & aValue,bool aAppend,ErrorResult & aRv)469 already_AddRefed<IDBFileRequest> IDBFileHandle::WriteOrAppend(
470     const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue, bool aAppend,
471     ErrorResult& aRv) {
472   AssertIsOnOwningThread();
473 
474   if (aValue.IsString()) {
475     return WriteOrAppend(aValue.GetAsString(), aAppend, aRv);
476   }
477 
478   if (aValue.IsArrayBuffer()) {
479     return WriteOrAppend(aValue.GetAsArrayBuffer(), aAppend, aRv);
480   }
481 
482   if (aValue.IsArrayBufferView()) {
483     return WriteOrAppend(aValue.GetAsArrayBufferView(), aAppend, aRv);
484   }
485 
486   MOZ_ASSERT(aValue.IsBlob());
487   return WriteOrAppend(aValue.GetAsBlob(), aAppend, aRv);
488 }
489 
WriteOrAppend(const nsAString & aValue,bool aAppend,ErrorResult & aRv)490 already_AddRefed<IDBFileRequest> IDBFileHandle::WriteOrAppend(
491     const nsAString& aValue, bool aAppend, ErrorResult& aRv) {
492   AssertIsOnOwningThread();
493 
494   // State checking for write or append
495   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
496     return nullptr;
497   }
498 
499   NS_ConvertUTF16toUTF8 cstr(aValue);
500 
501   uint64_t dataLength = cstr.Length();
502   ;
503   if (!dataLength) {
504     return nullptr;
505   }
506 
507   FileRequestStringData stringData(cstr);
508 
509   // Do nothing if the window is closed
510   if (!CheckWindow()) {
511     return nullptr;
512   }
513 
514   return WriteInternal(stringData, dataLength, aAppend, aRv);
515 }
516 
WriteOrAppend(const ArrayBuffer & aValue,bool aAppend,ErrorResult & aRv)517 already_AddRefed<IDBFileRequest> IDBFileHandle::WriteOrAppend(
518     const ArrayBuffer& aValue, bool aAppend, ErrorResult& aRv) {
519   AssertIsOnOwningThread();
520 
521   // State checking for write or append
522   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
523     return nullptr;
524   }
525 
526   aValue.ComputeLengthAndData();
527 
528   uint64_t dataLength = aValue.Length();
529   ;
530   if (!dataLength) {
531     return nullptr;
532   }
533 
534   const char* data = reinterpret_cast<const char*>(aValue.Data());
535 
536   FileRequestStringData stringData;
537   if (NS_WARN_IF(
538           !stringData.string().Assign(data, aValue.Length(), fallible_t()))) {
539     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
540     return nullptr;
541   }
542 
543   // Do nothing if the window is closed
544   if (!CheckWindow()) {
545     return nullptr;
546   }
547 
548   return WriteInternal(stringData, dataLength, aAppend, aRv);
549 }
550 
WriteOrAppend(const ArrayBufferView & aValue,bool aAppend,ErrorResult & aRv)551 already_AddRefed<IDBFileRequest> IDBFileHandle::WriteOrAppend(
552     const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv) {
553   AssertIsOnOwningThread();
554 
555   // State checking for write or append
556   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
557     return nullptr;
558   }
559 
560   aValue.ComputeLengthAndData();
561 
562   uint64_t dataLength = aValue.Length();
563   ;
564   if (!dataLength) {
565     return nullptr;
566   }
567 
568   const char* data = reinterpret_cast<const char*>(aValue.Data());
569 
570   FileRequestStringData stringData;
571   if (NS_WARN_IF(
572           !stringData.string().Assign(data, aValue.Length(), fallible_t()))) {
573     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
574     return nullptr;
575   }
576 
577   // Do nothing if the window is closed
578   if (!CheckWindow()) {
579     return nullptr;
580   }
581 
582   return WriteInternal(stringData, dataLength, aAppend, aRv);
583 }
584 
WriteOrAppend(Blob & aValue,bool aAppend,ErrorResult & aRv)585 already_AddRefed<IDBFileRequest> IDBFileHandle::WriteOrAppend(
586     Blob& aValue, bool aAppend, ErrorResult& aRv) {
587   AssertIsOnOwningThread();
588 
589   // State checking for write or append
590   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
591     return nullptr;
592   }
593 
594   ErrorResult error;
595   uint64_t dataLength = aValue.GetSize(error);
596   if (NS_WARN_IF(error.Failed())) {
597     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
598     return nullptr;
599   }
600 
601   if (!dataLength) {
602     return nullptr;
603   }
604 
605   PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread();
606   MOZ_ASSERT(backgroundActor);
607 
608   IPCBlob ipcBlob;
609   nsresult rv =
610       IPCBlobUtils::Serialize(aValue.Impl(), backgroundActor, ipcBlob);
611   if (NS_WARN_IF(NS_FAILED(rv))) {
612     aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
613     return nullptr;
614   }
615 
616   FileRequestBlobData blobData;
617   blobData.blob() = ipcBlob;
618 
619   // Do nothing if the window is closed
620   if (!CheckWindow()) {
621     return nullptr;
622   }
623 
624   return WriteInternal(blobData, dataLength, aAppend, aRv);
625 }
626 
WriteInternal(const FileRequestData & aData,uint64_t aDataLength,bool aAppend,ErrorResult & aRv)627 already_AddRefed<IDBFileRequest> IDBFileHandle::WriteInternal(
628     const FileRequestData& aData, uint64_t aDataLength, bool aAppend,
629     ErrorResult& aRv) {
630   AssertIsOnOwningThread();
631 
632   DebugOnly<ErrorResult> error;
633   MOZ_ASSERT(CheckStateForWrite(error));
634   MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
635   MOZ_ASSERT(aDataLength);
636   MOZ_ASSERT(CheckWindow());
637 
638   FileRequestWriteParams params;
639   params.offset() = aAppend ? UINT64_MAX : mLocation;
640   params.data() = aData;
641   params.dataLength() = aDataLength;
642 
643   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
644   MOZ_ASSERT(fileRequest);
645 
646   StartRequest(fileRequest, params);
647 
648   if (aAppend) {
649     mLocation = UINT64_MAX;
650   } else {
651     mLocation += aDataLength;
652   }
653 
654   return fileRequest.forget();
655 }
656 
SendFinish()657 void IDBFileHandle::SendFinish() {
658   AssertIsOnOwningThread();
659   MOZ_ASSERT(!mAborted);
660   MOZ_ASSERT(IsFinishingOrDone());
661   MOZ_ASSERT(!mSentFinishOrAbort);
662   MOZ_ASSERT(!mPendingRequestCount);
663 
664   MOZ_ASSERT(mBackgroundActor);
665   mBackgroundActor->SendFinish();
666 
667 #ifdef DEBUG
668   mSentFinishOrAbort = true;
669 #endif
670 }
671 
SendAbort()672 void IDBFileHandle::SendAbort() {
673   AssertIsOnOwningThread();
674   MOZ_ASSERT(mAborted);
675   MOZ_ASSERT(IsFinishingOrDone());
676   MOZ_ASSERT(!mSentFinishOrAbort);
677 
678   MOZ_ASSERT(mBackgroundActor);
679   mBackgroundActor->SendAbort();
680 
681 #ifdef DEBUG
682   mSentFinishOrAbort = true;
683 #endif
684 }
685 
NS_IMPL_ADDREF_INHERITED(IDBFileHandle,DOMEventTargetHelper)686 NS_IMPL_ADDREF_INHERITED(IDBFileHandle, DOMEventTargetHelper)
687 NS_IMPL_RELEASE_INHERITED(IDBFileHandle, DOMEventTargetHelper)
688 
689 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFileHandle)
690   NS_INTERFACE_MAP_ENTRY(nsIRunnable)
691   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
692 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
693 
694 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFileHandle)
695 
696 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBFileHandle,
697                                                   DOMEventTargetHelper)
698   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMutableFile)
699 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
700 
701 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBFileHandle,
702                                                 DOMEventTargetHelper)
703   // Don't unlink mMutableFile!
704 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
705 
706 NS_IMETHODIMP
707 IDBFileHandle::Run() {
708   AssertIsOnOwningThread();
709 
710   // We're back at the event loop, no longer newborn.
711   mCreating = false;
712 
713   // Maybe finish if there were no requests generated.
714   if (mReadyState == INITIAL) {
715     mReadyState = DONE;
716 
717     SendFinish();
718   }
719 
720   return NS_OK;
721 }
722 
GetEventTargetParent(EventChainPreVisitor & aVisitor)723 nsresult IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
724   AssertIsOnOwningThread();
725 
726   aVisitor.mCanHandle = true;
727   aVisitor.SetParentTarget(mMutableFile, false);
728   return NS_OK;
729 }
730 
731 // virtual
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)732 JSObject* IDBFileHandle::WrapObject(JSContext* aCx,
733                                     JS::Handle<JSObject*> aGivenProto) {
734   AssertIsOnOwningThread();
735 
736   return IDBFileHandleBinding::Wrap(aCx, this, aGivenProto);
737 }
738 
739 }  // namespace dom
740 }  // namespace mozilla
741