summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB/IDBFileHandle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB/IDBFileHandle.cpp')
-rw-r--r--dom/indexedDB/IDBFileHandle.cpp743
1 files changed, 743 insertions, 0 deletions
diff --git a/dom/indexedDB/IDBFileHandle.cpp b/dom/indexedDB/IDBFileHandle.cpp
new file mode 100644
index 0000000000..4be6208e6a
--- /dev/null
+++ b/dom/indexedDB/IDBFileHandle.cpp
@@ -0,0 +1,743 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "IDBFileHandle.h"
+
+#include "ActorsChild.h"
+#include "BackgroundChildImpl.h"
+#include "IDBEvents.h"
+#include "IDBMutableFile.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/IDBFileHandleBinding.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+#include "mozilla/dom/PBackgroundFileHandle.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "nsContentUtils.h"
+#include "nsQueryObject.h"
+#include "nsServiceManagerUtils.h"
+#include "nsWidgetsCID.h"
+
+namespace mozilla::dom {
+
+using namespace mozilla::dom::indexedDB;
+using namespace mozilla::ipc;
+
+namespace {
+
+RefPtr<IDBFileRequest> GenerateFileRequest(IDBFileHandle* aFileHandle) {
+ MOZ_ASSERT(aFileHandle);
+ aFileHandle->AssertIsOnOwningThread();
+
+ return IDBFileRequest::Create(aFileHandle, /* aWrapAsDOMRequest */ false);
+}
+
+} // namespace
+
+IDBFileHandle::IDBFileHandle(IDBMutableFile* aMutableFile, FileMode aMode)
+ : DOMEventTargetHelper(aMutableFile),
+ mMutableFile(aMutableFile),
+ mBackgroundActor(nullptr),
+ mLocation(0),
+ mPendingRequestCount(0),
+ mReadyState(INITIAL),
+ mMode(aMode),
+ mAborted(false),
+ mCreating(false)
+#ifdef DEBUG
+ ,
+ mSentFinishOrAbort(false),
+ mFiredCompleteOrAbort(false)
+#endif
+{
+ MOZ_ASSERT(aMutableFile);
+ aMutableFile->AssertIsOnOwningThread();
+}
+
+IDBFileHandle::~IDBFileHandle() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mPendingRequestCount);
+ MOZ_ASSERT(!mCreating);
+ MOZ_ASSERT(mSentFinishOrAbort);
+ MOZ_ASSERT_IF(mBackgroundActor, mFiredCompleteOrAbort);
+
+ mMutableFile->UnregisterFileHandle(this);
+
+ if (mBackgroundActor) {
+ mBackgroundActor->SendDeleteMeInternal();
+
+ MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
+ }
+}
+
+// static
+RefPtr<IDBFileHandle> IDBFileHandle::Create(IDBMutableFile* aMutableFile,
+ FileMode aMode) {
+ MOZ_ASSERT(aMutableFile);
+ aMutableFile->AssertIsOnOwningThread();
+ MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite);
+
+ RefPtr<IDBFileHandle> fileHandle = new IDBFileHandle(aMutableFile, aMode);
+
+ // XXX Fix!
+ MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
+
+ nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
+ nsContentUtils::AddPendingIDBTransaction(runnable.forget());
+
+ fileHandle->mCreating = true;
+
+ aMutableFile->RegisterFileHandle(fileHandle);
+
+ return fileHandle;
+}
+
+// static
+IDBFileHandle* IDBFileHandle::GetCurrent() {
+ MOZ_ASSERT(BackgroundChild::GetForCurrentThread());
+
+ BackgroundChildImpl::ThreadLocal* threadLocal =
+ BackgroundChildImpl::GetThreadLocalForCurrentThread();
+ MOZ_ASSERT(threadLocal);
+
+ return threadLocal->mCurrentFileHandle;
+}
+
+#ifdef DEBUG
+
+void IDBFileHandle::AssertIsOnOwningThread() const {
+ MOZ_ASSERT(mMutableFile);
+ mMutableFile->AssertIsOnOwningThread();
+}
+
+#endif // DEBUG
+
+void IDBFileHandle::SetBackgroundActor(BackgroundFileHandleChild* aActor) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(!mBackgroundActor);
+
+ mBackgroundActor = aActor;
+}
+
+void IDBFileHandle::StartRequest(IDBFileRequest* aFileRequest,
+ const FileRequestParams& aParams) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aFileRequest);
+ MOZ_ASSERT(aParams.type() != FileRequestParams::T__None);
+
+ BackgroundFileRequestChild* actor =
+ new BackgroundFileRequestChild(aFileRequest);
+
+ mBackgroundActor->SendPBackgroundFileRequestConstructor(actor, aParams);
+
+ // Balanced in BackgroundFileRequestChild::Recv__delete__().
+ OnNewRequest();
+}
+
+void IDBFileHandle::OnNewRequest() {
+ AssertIsOnOwningThread();
+
+ if (!mPendingRequestCount) {
+ MOZ_ASSERT(mReadyState == INITIAL);
+ mReadyState = LOADING;
+ }
+
+ ++mPendingRequestCount;
+}
+
+void IDBFileHandle::OnRequestFinished(bool aActorDestroyedNormally) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mPendingRequestCount);
+
+ --mPendingRequestCount;
+
+ if (!mPendingRequestCount && !mMutableFile->IsInvalidated()) {
+ mReadyState = FINISHING;
+
+ if (aActorDestroyedNormally) {
+ if (!mAborted) {
+ SendFinish();
+ } else {
+ SendAbort();
+ }
+ } else {
+ // Don't try to send any more messages to the parent if the request actor
+ // was killed.
+#ifdef DEBUG
+ MOZ_ASSERT(!mSentFinishOrAbort);
+ mSentFinishOrAbort = true;
+#endif
+ }
+ }
+}
+
+void IDBFileHandle::FireCompleteOrAbortEvents(bool aAborted) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mFiredCompleteOrAbort);
+
+ mReadyState = DONE;
+
+#ifdef DEBUG
+ mFiredCompleteOrAbort = true;
+#endif
+
+ // TODO: Why is it necessary to create the Event on the heap at all?
+ const auto event = CreateGenericEvent(
+ this,
+ aAborted ? nsDependentString(kAbortEventType)
+ : nsDependentString(kCompleteEventType),
+ aAborted ? eDoesBubble : eDoesNotBubble, eNotCancelable);
+ MOZ_ASSERT(event);
+
+ IgnoredErrorResult rv;
+ DispatchEvent(*event, rv);
+ if (rv.Failed()) {
+ NS_WARNING("DispatchEvent failed!");
+ }
+}
+
+bool IDBFileHandle::IsOpen() const {
+ AssertIsOnOwningThread();
+
+ // If we haven't started anything then we're open.
+ if (mReadyState == INITIAL) {
+ return true;
+ }
+
+ // If we've already started then we need to check to see if we still have the
+ // mCreating flag set. If we do (i.e. we haven't returned to the event loop
+ // from the time we were created) then we are open. Otherwise check the
+ // currently running file handles to see if it's the same. We only allow other
+ // requests to be made if this file handle is currently running.
+ if (mReadyState == LOADING && (mCreating || GetCurrent() == this)) {
+ return true;
+ }
+
+ return false;
+}
+
+void IDBFileHandle::Abort() {
+ AssertIsOnOwningThread();
+
+ if (IsFinishingOrDone()) {
+ // Already started (and maybe finished) the finish or abort so there is
+ // nothing to do here.
+ return;
+ }
+
+ const bool isInvalidated = mMutableFile->IsInvalidated();
+ bool needToSendAbort = mReadyState == INITIAL && !isInvalidated;
+
+#ifdef DEBUG
+ if (isInvalidated) {
+ mSentFinishOrAbort = true;
+ }
+#endif
+
+ mAborted = true;
+ mReadyState = DONE;
+
+ // Fire the abort event if there are no outstanding requests. Otherwise the
+ // abort event will be fired when all outstanding requests finish.
+ if (needToSendAbort) {
+ SendAbort();
+ }
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::GetMetadata(
+ const IDBFileMetadataParameters& aParameters, ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // Common state checking
+ if (!CheckState(aRv)) {
+ return nullptr;
+ }
+
+ // Argument checking for get metadata.
+ if (!aParameters.mSize && !aParameters.mLastModified) {
+ aRv.ThrowTypeError("Either size or lastModified should be true.");
+ return nullptr;
+ }
+
+ // Do nothing if the window is closed
+ if (!CheckWindow()) {
+ return nullptr;
+ }
+
+ FileRequestGetMetadataParams params;
+ params.size() = aParameters.mSize;
+ params.lastModified() = aParameters.mLastModified;
+
+ auto fileRequest = GenerateFileRequest(this);
+
+ StartRequest(fileRequest, params);
+
+ return fileRequest;
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::Truncate(const Optional<uint64_t>& aSize,
+ ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // State checking for write
+ if (!CheckStateForWrite(aRv)) {
+ return nullptr;
+ }
+
+ // Getting location and additional state checking for truncate
+ uint64_t location;
+ if (aSize.WasPassed()) {
+ // Cannot use UINT64_MAX as the truncation size, as this is used as a
+ // special value for the location to mark append mode. This is not really of
+ // practical relevance, as a file cannot actually have a size that large.
+
+ // XXX: Remove this check when removing the use of UINT64_MAX as a special
+ // value for the location to mark append mode?
+ if (aSize.Value() == UINT64_MAX) {
+ aRv.ThrowTypeError("UINT64_MAX is not a valid size");
+ return nullptr;
+ }
+ location = aSize.Value();
+ } else {
+ // Fail if we are in append mode.
+
+ // XXX: Is it really ok that truncate with a size parameter works when in
+ // append mode, but one without a size parameter does not?
+ if (mLocation == UINT64_MAX) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+ return nullptr;
+ }
+ location = mLocation;
+ }
+
+ // Do nothing if the window is closed
+ if (!CheckWindow()) {
+ return nullptr;
+ }
+
+ FileRequestTruncateParams params;
+ params.offset() = location;
+
+ auto fileRequest = GenerateFileRequest(this);
+
+ StartRequest(fileRequest, params);
+
+ if (aSize.WasPassed()) {
+ mLocation = aSize.Value();
+ }
+
+ return fileRequest;
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::Flush(ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // State checking for write
+ if (!CheckStateForWrite(aRv)) {
+ return nullptr;
+ }
+
+ // Do nothing if the window is closed
+ if (!CheckWindow()) {
+ return nullptr;
+ }
+
+ FileRequestFlushParams params;
+
+ auto fileRequest = GenerateFileRequest(this);
+
+ StartRequest(fileRequest, params);
+
+ return fileRequest;
+}
+
+void IDBFileHandle::Abort(ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // This method is special enough for not using generic state checking methods.
+
+ if (IsFinishingOrDone()) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+ return;
+ }
+
+ Abort();
+}
+
+bool IDBFileHandle::CheckState(ErrorResult& aRv) const {
+ if (!IsOpen()) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_INACTIVE_ERR);
+ return false;
+ }
+
+ return true;
+}
+
+bool IDBFileHandle::CheckStateAndArgumentsForRead(uint64_t aSize,
+ ErrorResult& aRv) {
+ // Common state checking
+ if (!CheckState(aRv)) {
+ return false;
+ }
+
+ // Additional state checking for read
+ if (mLocation == UINT64_MAX) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+ return false;
+ }
+
+ // Argument checking for read
+ if (!aSize) {
+ aRv.ThrowTypeError("0 (Zero) is not a valid read size.");
+ return false;
+ }
+
+ if (aSize > UINT32_MAX) {
+ aRv.ThrowTypeError("Data size for read is too large.");
+ return false;
+ }
+
+ return true;
+}
+
+bool IDBFileHandle::CheckStateForWrite(ErrorResult& aRv) {
+ // Common state checking
+ if (!CheckState(aRv)) {
+ return false;
+ }
+
+ // Additional state checking for write
+ if (mMode != FileMode::Readwrite) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_READ_ONLY_ERR);
+ return false;
+ }
+
+ return true;
+}
+
+bool IDBFileHandle::CheckStateForWriteOrAppend(bool aAppend, ErrorResult& aRv) {
+ // State checking for write
+ if (!CheckStateForWrite(aRv)) {
+ return false;
+ }
+
+ // Additional state checking for write
+ if (!aAppend && mLocation == UINT64_MAX) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_NOT_ALLOWED_ERR);
+ return false;
+ }
+
+ return true;
+}
+
+bool IDBFileHandle::CheckWindow() {
+ AssertIsOnOwningThread();
+
+ return GetOwner();
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::Read(uint64_t aSize, bool aHasEncoding,
+ const nsAString& aEncoding,
+ ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // State and argument checking for read
+ if (!CheckStateAndArgumentsForRead(aSize, aRv)) {
+ return nullptr;
+ }
+
+ // Do nothing if the window is closed
+ if (!CheckWindow()) {
+ return nullptr;
+ }
+
+ FileRequestReadParams params;
+ params.offset() = mLocation;
+ params.size() = aSize;
+
+ auto fileRequest = GenerateFileRequest(this);
+ if (aHasEncoding) {
+ fileRequest->SetEncoding(aEncoding);
+ }
+
+ StartRequest(fileRequest, params);
+
+ mLocation += aSize;
+
+ return fileRequest;
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(
+ const StringOrArrayBufferOrArrayBufferViewOrBlob& aValue, bool aAppend,
+ ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ if (aValue.IsString()) {
+ return WriteOrAppend(aValue.GetAsString(), aAppend, aRv);
+ }
+
+ if (aValue.IsArrayBuffer()) {
+ return WriteOrAppend(aValue.GetAsArrayBuffer(), aAppend, aRv);
+ }
+
+ if (aValue.IsArrayBufferView()) {
+ return WriteOrAppend(aValue.GetAsArrayBufferView(), aAppend, aRv);
+ }
+
+ MOZ_ASSERT(aValue.IsBlob());
+ return WriteOrAppend(aValue.GetAsBlob(), aAppend, aRv);
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(const nsAString& aValue,
+ bool aAppend,
+ ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // State checking for write or append
+ if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+ return nullptr;
+ }
+
+ NS_ConvertUTF16toUTF8 cstr(aValue);
+
+ uint64_t dataLength = cstr.Length();
+ ;
+ if (!dataLength) {
+ return nullptr;
+ }
+
+ FileRequestStringData stringData(cstr);
+
+ // Do nothing if the window is closed
+ if (!CheckWindow()) {
+ return nullptr;
+ }
+
+ return WriteInternal(stringData, dataLength, aAppend, aRv);
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(const ArrayBuffer& aValue,
+ bool aAppend,
+ ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // State checking for write or append
+ if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+ return nullptr;
+ }
+
+ aValue.ComputeState();
+
+ uint64_t dataLength = aValue.Length();
+ ;
+ if (!dataLength) {
+ return nullptr;
+ }
+
+ const char* data = reinterpret_cast<const char*>(aValue.Data());
+
+ FileRequestStringData stringData;
+ if (NS_WARN_IF(
+ !stringData.string().Assign(data, aValue.Length(), fallible_t()))) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+ return nullptr;
+ }
+
+ // Do nothing if the window is closed
+ if (!CheckWindow()) {
+ return nullptr;
+ }
+
+ return WriteInternal(stringData, dataLength, aAppend, aRv);
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(
+ const ArrayBufferView& aValue, bool aAppend, ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // State checking for write or append
+ if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+ return nullptr;
+ }
+
+ aValue.ComputeState();
+
+ uint64_t dataLength = aValue.Length();
+ ;
+ if (!dataLength) {
+ return nullptr;
+ }
+
+ const char* data = reinterpret_cast<const char*>(aValue.Data());
+
+ FileRequestStringData stringData;
+ if (NS_WARN_IF(
+ !stringData.string().Assign(data, aValue.Length(), fallible_t()))) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+ return nullptr;
+ }
+
+ // Do nothing if the window is closed
+ if (!CheckWindow()) {
+ return nullptr;
+ }
+
+ return WriteInternal(stringData, dataLength, aAppend, aRv);
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::WriteOrAppend(Blob& aValue, bool aAppend,
+ ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ // State checking for write or append
+ if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
+ return nullptr;
+ }
+
+ ErrorResult error;
+ uint64_t dataLength = aValue.GetSize(error);
+ if (NS_WARN_IF(error.Failed())) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+ return nullptr;
+ }
+
+ if (!dataLength) {
+ return nullptr;
+ }
+
+ IPCBlob ipcBlob;
+ nsresult rv = IPCBlobUtils::Serialize(aValue.Impl(), ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aRv.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
+ return nullptr;
+ }
+
+ FileRequestBlobData blobData;
+ blobData.blob() = ipcBlob;
+
+ // Do nothing if the window is closed
+ if (!CheckWindow()) {
+ return nullptr;
+ }
+
+ return WriteInternal(blobData, dataLength, aAppend, aRv);
+}
+
+RefPtr<IDBFileRequest> IDBFileHandle::WriteInternal(
+ const FileRequestData& aData, uint64_t aDataLength, bool aAppend,
+ ErrorResult& aRv) {
+ AssertIsOnOwningThread();
+
+ DebugOnly<ErrorResult> error;
+ MOZ_ASSERT(CheckStateForWrite(error));
+ MOZ_ASSERT_IF(!aAppend, mLocation != UINT64_MAX);
+ MOZ_ASSERT(aDataLength);
+ MOZ_ASSERT(CheckWindow());
+
+ FileRequestWriteParams params;
+ params.offset() = aAppend ? UINT64_MAX : mLocation;
+ params.data() = aData;
+ params.dataLength() = aDataLength;
+
+ auto fileRequest = GenerateFileRequest(this);
+ MOZ_ASSERT(fileRequest);
+
+ StartRequest(fileRequest, params);
+
+ if (aAppend) {
+ mLocation = UINT64_MAX;
+ } else {
+ mLocation += aDataLength;
+ }
+
+ return fileRequest;
+}
+
+void IDBFileHandle::SendFinish() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mAborted);
+ MOZ_ASSERT(IsFinishingOrDone());
+ MOZ_ASSERT(!mSentFinishOrAbort);
+ MOZ_ASSERT(!mPendingRequestCount);
+
+ MOZ_ASSERT(mBackgroundActor);
+ mBackgroundActor->SendFinish();
+
+#ifdef DEBUG
+ mSentFinishOrAbort = true;
+#endif
+}
+
+void IDBFileHandle::SendAbort() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mAborted);
+ MOZ_ASSERT(IsFinishingOrDone());
+ MOZ_ASSERT(!mSentFinishOrAbort);
+
+ MOZ_ASSERT(mBackgroundActor);
+ mBackgroundActor->SendAbort();
+
+#ifdef DEBUG
+ mSentFinishOrAbort = true;
+#endif
+}
+
+NS_IMPL_ADDREF_INHERITED(IDBFileHandle, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(IDBFileHandle, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFileHandle)
+ NS_INTERFACE_MAP_ENTRY(nsIRunnable)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFileHandle)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBFileHandle,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMutableFile)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBFileHandle,
+ DOMEventTargetHelper)
+ // Don't unlink mMutableFile!
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMETHODIMP
+IDBFileHandle::Run() {
+ AssertIsOnOwningThread();
+
+ // We're back at the event loop, no longer newborn.
+ mCreating = false;
+
+ // Maybe finish if there were no requests generated.
+ if (mReadyState == INITIAL) {
+ mReadyState = DONE;
+
+ SendFinish();
+ }
+
+ return NS_OK;
+}
+
+void IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
+ AssertIsOnOwningThread();
+
+ aVisitor.mCanHandle = true;
+ aVisitor.SetParentTarget(mMutableFile, false);
+}
+
+// virtual
+JSObject* IDBFileHandle::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ AssertIsOnOwningThread();
+
+ return IDBFileHandle_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace mozilla::dom