forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			803 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			803 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 {
 | |
| namespace dom {
 | |
| 
 | |
| using namespace mozilla::dom::indexedDB;
 | |
| using namespace mozilla::ipc;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| already_AddRefed<IDBFileRequest>
 | |
| GenerateFileRequest(IDBFileHandle* aFileHandle)
 | |
| {
 | |
|   MOZ_ASSERT(aFileHandle);
 | |
|   aFileHandle->AssertIsOnOwningThread();
 | |
| 
 | |
|   RefPtr<IDBFileRequest> fileRequest =
 | |
|     IDBFileRequest::Create(aFileHandle, /* aWrapAsDOMRequest */ false);
 | |
|   MOZ_ASSERT(fileRequest);
 | |
| 
 | |
|   return fileRequest.forget();
 | |
| }
 | |
| 
 | |
| } // namespace
 | |
| 
 | |
| IDBFileHandle::IDBFileHandle(IDBMutableFile* aMutableFile,
 | |
|                              FileMode aMode)
 | |
|   : 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
 | |
| already_AddRefed<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);
 | |
| 
 | |
|   fileHandle->BindToOwner(aMutableFile);
 | |
| 
 | |
|   // 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.forget();
 | |
| }
 | |
| 
 | |
| // 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
 | |
| 
 | |
|   nsCOMPtr<nsIDOMEvent> event;
 | |
|   if (aAborted) {
 | |
|     event = CreateGenericEvent(this, nsDependentString(kAbortEventType),
 | |
|                                eDoesBubble, eNotCancelable);
 | |
|   } else {
 | |
|     event = CreateGenericEvent(this, nsDependentString(kCompleteEventType),
 | |
|                                eDoesNotBubble, eNotCancelable);
 | |
|   }
 | |
|   if (NS_WARN_IF(!event)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   bool dummy;
 | |
|   if (NS_FAILED(DispatchEvent(event, &dummy))) {
 | |
|     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();
 | |
|   }
 | |
| }
 | |
| 
 | |
| already_AddRefed<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<MSG_METADATA_NOT_CONFIGURED>();
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|  // Do nothing if the window is closed
 | |
|   if (!CheckWindow()) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   FileRequestGetMetadataParams params;
 | |
|   params.size() = aParameters.mSize;
 | |
|   params.lastModified() = aParameters.mLastModified;
 | |
| 
 | |
|   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
 | |
| 
 | |
|   StartRequest(fileRequest, params);
 | |
| 
 | |
|   return fileRequest.forget();
 | |
| }
 | |
| 
 | |
| already_AddRefed<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()) {
 | |
|     // Just in case someone calls us from C++
 | |
|     MOZ_ASSERT(aSize.Value() != UINT64_MAX, "Passed wrong size!");
 | |
|     location = aSize.Value();
 | |
|   } else {
 | |
|     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;
 | |
| 
 | |
|   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
 | |
| 
 | |
|   StartRequest(fileRequest, params);
 | |
| 
 | |
|   if (aSize.WasPassed()) {
 | |
|     mLocation = aSize.Value();
 | |
|   }
 | |
| 
 | |
|   return fileRequest.forget();
 | |
| }
 | |
| 
 | |
| already_AddRefed<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;
 | |
| 
 | |
|   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
 | |
| 
 | |
|   StartRequest(fileRequest, params);
 | |
| 
 | |
|   return fileRequest.forget();
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
|   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<MSG_INVALID_READ_SIZE>();
 | |
|     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();
 | |
| }
 | |
| 
 | |
| already_AddRefed<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;
 | |
| 
 | |
|   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
 | |
|   if (aHasEncoding) {
 | |
|     fileRequest->SetEncoding(aEncoding);
 | |
|   }
 | |
| 
 | |
|   StartRequest(fileRequest, params);
 | |
| 
 | |
|   mLocation += aSize;
 | |
| 
 | |
|   return fileRequest.forget();
 | |
| }
 | |
| 
 | |
| already_AddRefed<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);
 | |
| }
 | |
| 
 | |
| already_AddRefed<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);
 | |
| }
 | |
| 
 | |
| already_AddRefed<IDBFileRequest>
 | |
| IDBFileHandle::WriteOrAppend(const ArrayBuffer& aValue,
 | |
|                              bool aAppend,
 | |
|                              ErrorResult& aRv)
 | |
| {
 | |
|   AssertIsOnOwningThread();
 | |
| 
 | |
|   // State checking for write or append
 | |
|   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   aValue.ComputeLengthAndData();
 | |
| 
 | |
|   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);
 | |
| }
 | |
| 
 | |
| already_AddRefed<IDBFileRequest>
 | |
| IDBFileHandle::WriteOrAppend(const ArrayBufferView& aValue,
 | |
|                              bool aAppend,
 | |
|                              ErrorResult& aRv)
 | |
| {
 | |
|   AssertIsOnOwningThread();
 | |
| 
 | |
|   // State checking for write or append
 | |
|   if (!CheckStateForWriteOrAppend(aAppend, aRv)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   aValue.ComputeLengthAndData();
 | |
| 
 | |
|   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);
 | |
| }
 | |
| 
 | |
| already_AddRefed<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;
 | |
|   }
 | |
| 
 | |
|   PBackgroundChild* backgroundActor = BackgroundChild::GetForCurrentThread();
 | |
|   MOZ_ASSERT(backgroundActor);
 | |
| 
 | |
|   IPCBlob ipcBlob;
 | |
|   nsresult rv =
 | |
|     IPCBlobUtils::Serialize(aValue.Impl(), backgroundActor, 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);
 | |
| }
 | |
| 
 | |
| already_AddRefed<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;
 | |
| 
 | |
|   RefPtr<IDBFileRequest> fileRequest = GenerateFileRequest(this);
 | |
|   MOZ_ASSERT(fileRequest);
 | |
| 
 | |
|   StartRequest(fileRequest, params);
 | |
| 
 | |
|   if (aAppend) {
 | |
|     mLocation = UINT64_MAX;
 | |
|   }
 | |
|   else {
 | |
|     mLocation += aDataLength;
 | |
|   }
 | |
| 
 | |
|   return fileRequest.forget();
 | |
| }
 | |
| 
 | |
| 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_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;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| IDBFileHandle::GetEventTargetParent(EventChainPreVisitor& aVisitor)
 | |
| {
 | |
|   AssertIsOnOwningThread();
 | |
| 
 | |
|   aVisitor.mCanHandle = true;
 | |
|   aVisitor.SetParentTarget(mMutableFile, false);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // virtual
 | |
| JSObject*
 | |
| IDBFileHandle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 | |
| {
 | |
|   AssertIsOnOwningThread();
 | |
| 
 | |
|   return IDBFileHandleBinding::Wrap(aCx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| } // namespace dom
 | |
| } // namespace mozilla
 | 
