forked from mirrors/gecko-dev
		
	Differential Revision: https://phabricator.services.mozilla.com/D45932 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			740 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			740 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
 | 
						|
 | 
						|
  RefPtr<Event> event;
 | 
						|
  if (aAborted) {
 | 
						|
    event = CreateGenericEvent(this, nsDependentString(kAbortEventType),
 | 
						|
                               eDoesBubble, eNotCancelable);
 | 
						|
  } else {
 | 
						|
    event = CreateGenericEvent(this, nsDependentString(kCompleteEventType),
 | 
						|
                               eDoesNotBubble, eNotCancelable);
 | 
						|
  }
 | 
						|
  if (NS_WARN_IF(!event)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  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();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
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(u"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;
 | 
						|
 | 
						|
  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(u"0 (Zero) is not a valid 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;
 | 
						|
}
 | 
						|
 | 
						|
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 dom
 | 
						|
}  // namespace mozilla
 |