Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/file/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 19 kB image not shown  

Quelle  MutableBlobStorage.cpp   Sprache: 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 "EmptyBlobImpl.h"
#include "MutableBlobStorage.h"
#include "MemoryBlobImpl.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/dom/TemporaryIPCBlobChild.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/Preferences.h"
#include "mozilla/TaskQueue.h"
#include "File.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsNetCID.h"
#include "nsProxyRelease.h"

#define BLOB_MEMORY_TEMPORARY_FILE 1048576

namespace mozilla::dom {

namespace {

// This class uses the callback to inform when the Blob is created or when the
// error must be propagated.
class BlobCreationDoneRunnable final : public Runnable {
 public:
  BlobCreationDoneRunnable(MutableBlobStorage* aBlobStorage,
                           MutableBlobStorageCallback* aCallback,
                           BlobImpl* aBlobImpl, nsresult aRv)
      : Runnable("dom::BlobCreationDoneRunnable"),
        mBlobStorage(aBlobStorage),
        mCallback(aCallback),
        mBlobImpl(aBlobImpl),
        mRv(aRv) {
    MOZ_ASSERT(aBlobStorage);
    MOZ_ASSERT(aCallback);
    MOZ_ASSERT((NS_FAILED(aRv) && !aBlobImpl) ||
               (NS_SUCCEEDED(aRv) && aBlobImpl));
  }

  NS_IMETHOD
  Run() override {
    MOZ_ASSERT(NS_IsMainThread());
    MOZ_ASSERT(mBlobStorage);
    mCallback->BlobStoreCompleted(mBlobStorage, mBlobImpl, mRv);
    mCallback = nullptr;
    mBlobImpl = nullptr;
    return NS_OK;
  }

 private:
  ~BlobCreationDoneRunnable() override {
    MOZ_ASSERT(mBlobStorage);
    // If something when wrong, we still have to release these objects in the
    // correct thread.
    NS_ProxyRelease("BlobCreationDoneRunnable::mCallback",
                    mBlobStorage->EventTarget(), mCallback.forget());
  }

  RefPtr<MutableBlobStorage> mBlobStorage;
  RefPtr<MutableBlobStorageCallback> mCallback;
  RefPtr<BlobImpl> mBlobImpl;
  nsresult mRv;
};

// Simple runnable to propagate the error to the BlobStorage.
class ErrorPropagationRunnable final : public Runnable {
 public:
  ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv)
      : Runnable("dom::ErrorPropagationRunnable"),
        mBlobStorage(aBlobStorage),
        mRv(aRv) {}

  NS_IMETHOD
  Run() override {
    mBlobStorage->ErrorPropagated(mRv);
    return NS_OK;
  }

 private:
  RefPtr<MutableBlobStorage> mBlobStorage;
  nsresult mRv;
};

// This runnable moves a buffer to the IO thread and there, it writes it into
// the temporary file, if its File Descriptor has not been already closed.
class WriteRunnable final : public Runnable {
 public:
  static WriteRunnable* CopyBuffer(MutableBlobStorage* aBlobStorage,
                                   const void* aData, uint32_t aLength) {
    MOZ_ASSERT(aBlobStorage);
    MOZ_ASSERT(aData);

    // We have to take a copy of this buffer.
    void* data = malloc(aLength);
    if (!data) {
      return nullptr;
    }

    memcpy((char*)data, aData, aLength);
    return new WriteRunnable(aBlobStorage, data, aLength);
  }

  static WriteRunnable* AdoptBuffer(MutableBlobStorage* aBlobStorage,
                                    void* aData, uint32_t aLength) {
    MOZ_ASSERT(NS_IsMainThread());
    MOZ_ASSERT(aBlobStorage);
    MOZ_ASSERT(aData);

    return new WriteRunnable(aBlobStorage, aData, aLength);
  }

  NS_IMETHOD
  Run() override {
    MOZ_ASSERT(!NS_IsMainThread());
    MOZ_ASSERT(mBlobStorage);

    PRFileDesc* fd = mBlobStorage->GetFD();
    if (!fd) {
      // The file descriptor has been closed in the meantime.
      return NS_OK;
    }

    int32_t written = PR_Write(fd, mData, mLength);
    if (NS_WARN_IF(written < 0 || uint32_t(written) != mLength)) {
      mBlobStorage->CloseFD();
      return mBlobStorage->EventTarget()->Dispatch(
          new ErrorPropagationRunnable(mBlobStorage, NS_ERROR_FAILURE),
          NS_DISPATCH_NORMAL);
    }

    return NS_OK;
  }

 private:
  WriteRunnable(MutableBlobStorage* aBlobStorage, void* aData, uint32_t aLength)
      : Runnable("dom::WriteRunnable"),
        mBlobStorage(aBlobStorage),
        mData(aData),
        mLength(aLength) {
    MOZ_ASSERT(mBlobStorage);
    MOZ_ASSERT(aData);
  }

  ~WriteRunnable() override { free(mData); }

  RefPtr<MutableBlobStorage> mBlobStorage;
  void* mData;
  uint32_t mLength;
};

// This runnable closes the FD in case something goes wrong or the temporary
// file is not needed anymore.
class CloseFileRunnable final : public Runnable {
 public:
  explicit CloseFileRunnable(PRFileDesc* aFD)
      : Runnable("dom::CloseFileRunnable"), mFD(aFD) {}

  NS_IMETHOD
  Run() override {
    MOZ_ASSERT(!NS_IsMainThread());
    PR_Close(mFD);
    mFD = nullptr;
    return NS_OK;
  }

 private:
  ~CloseFileRunnable() override {
    if (mFD) {
      PR_Close(mFD);
    }
  }

  PRFileDesc* mFD;
};

// This runnable is dispatched to the main-thread from the IO thread and its
// task is to create the blob and inform the callback.
class CreateBlobRunnable final : public Runnable,
                                 public TemporaryIPCBlobChildCallback {
 public:
  // We need to always declare refcounting because
  // TemporaryIPCBlobChildCallback has pure-virtual refcounting.
  NS_DECL_ISUPPORTS_INHERITED

  CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
                     const nsACString& aContentType,
                     already_AddRefed<MutableBlobStorageCallback> aCallback)
      : Runnable("dom::CreateBlobRunnable"),
        mBlobStorage(aBlobStorage),
        mContentType(aContentType),
        mCallback(aCallback) {
    MOZ_ASSERT(!NS_IsMainThread());
    MOZ_ASSERT(aBlobStorage);
  }

  NS_IMETHOD
  Run() override {
    MOZ_ASSERT(NS_IsMainThread());
    MOZ_ASSERT(mBlobStorage);
    mBlobStorage->AskForBlob(this, mContentType);
    return NS_OK;
  }

  void OperationSucceeded(BlobImpl* aBlobImpl) override {
    RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback));
    callback->BlobStoreCompleted(mBlobStorage, aBlobImpl, NS_OK);
  }

  void OperationFailed(nsresult aRv) override {
    RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback));
    callback->BlobStoreCompleted(mBlobStorage, nullptr, aRv);
  }

 private:
  ~CreateBlobRunnable() override {
    MOZ_ASSERT(mBlobStorage);
    // If something when wrong, we still have to release data in the correct
    // thread.
    NS_ProxyRelease("CreateBlobRunnable::mCallback",
                    mBlobStorage->EventTarget(), mCallback.forget());
  }

  RefPtr<MutableBlobStorage> mBlobStorage;
  nsCString mContentType;
  RefPtr<MutableBlobStorageCallback> mCallback;
};

NS_IMPL_ISUPPORTS_INHERITED0(CreateBlobRunnable, Runnable)

// This task is used to know when the writing is completed. From the IO thread
// it dispatches a CreateBlobRunnable to the main-thread.
class LastRunnable final : public Runnable {
 public:
  LastRunnable(MutableBlobStorage* aBlobStorage, const nsACString& aContentType,
               MutableBlobStorageCallback* aCallback)
      : Runnable("dom::LastRunnable"),
        mBlobStorage(aBlobStorage),
        mContentType(aContentType),
        mCallback(aCallback) {
    MOZ_ASSERT(NS_IsMainThread());
    MOZ_ASSERT(mBlobStorage);
    MOZ_ASSERT(aCallback);
  }

  NS_IMETHOD
  Run() override {
    MOZ_ASSERT(!NS_IsMainThread());

    RefPtr<Runnable> runnable =
        new CreateBlobRunnable(mBlobStorage, mContentType, mCallback.forget());
    return mBlobStorage->EventTarget()->Dispatch(runnable, NS_DISPATCH_NORMAL);
  }

 private:
  ~LastRunnable() override {
    MOZ_ASSERT(mBlobStorage);
    // If something when wrong, we still have to release data in the correct
    // thread.
    NS_ProxyRelease("LastRunnable::mCallback", mBlobStorage->EventTarget(),
                    mCallback.forget());
  }

  RefPtr<MutableBlobStorage> mBlobStorage;
  nsCString mContentType;
  RefPtr<MutableBlobStorageCallback> mCallback;
};

}  // anonymous namespace

MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
                                       nsIEventTarget* aEventTarget,
                                       uint32_t aMaxMemory)
    : mMutex("MutableBlobStorage::mMutex"),
      mData(nullptr),
      mDataLen(0),
      mDataBufferLen(0),
      mStorageState(aType == eOnlyInMemory ? eKeepInMemory : eInMemory),
      mFD(nullptr),
      mErrorResult(NS_OK),
      mEventTarget(aEventTarget),
      mMaxMemory(aMaxMemory) {
  MOZ_ASSERT(NS_IsMainThread());

  if (!mEventTarget) {
    mEventTarget = GetMainThreadSerialEventTarget();
  }

  if (aMaxMemory == 0 && aType == eCouldBeInTemporaryFile) {
    mMaxMemory = Preferences::GetUint("dom.blob.memoryToTemporaryFile",
                                      BLOB_MEMORY_TEMPORARY_FILE);
  }

  MOZ_ASSERT(mEventTarget);
}

MutableBlobStorage::~MutableBlobStorage() {
  free(mData);

  if (mFD) {
    RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
    (void)DispatchToIOThread(runnable.forget());
  }

  if (mTaskQueue) {
    mTaskQueue->BeginShutdown();
  }

  if (mActor) {
    NS_ProxyRelease("MutableBlobStorage::mActor", EventTarget(),
                    mActor.forget());
  }
}

void MutableBlobStorage::GetBlobImplWhenReady(
    const nsACString& aContentType, MutableBlobStorageCallback* aCallback) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aCallback);

  MutexAutoLock lock(mMutex);

  // GetBlob can be called just once.
  MOZ_ASSERT(mStorageState != eClosed);
  StorageState previousState = mStorageState;
  mStorageState = eClosed;

  if (previousState == eInTemporaryFile) {
    if (NS_FAILED(mErrorResult)) {
      MOZ_ASSERT(!mActor);

      RefPtr<Runnable> runnable =
          new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult);
      EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
      return;
    }

    // We want to wait until all the WriteRunnable are completed. The way we do
    // this is to go to the I/O thread and then we come back: the runnables are
    // executed in order and this LastRunnable will be... the last one.
    // This Runnable will also close the FD on the I/O thread.
    RefPtr<Runnable> runnable = new LastRunnable(this, aContentType, aCallback);

    // If the dispatching fails, we are shutting down and it's fine to do not
    // run the callback.
    (void)DispatchToIOThread(runnable.forget());
    return;
  }

  // If we are waiting for the temporary file, it's better to wait...
  if (previousState == eWaitingForTemporaryFile) {
    mPendingContentType = aContentType;
    mPendingCallback = aCallback;
    return;
  }

  RefPtr<BlobImpl> blobImpl;

  if (mData) {
    blobImpl = new MemoryBlobImpl(mData, mDataLen,
                                  NS_ConvertUTF8toUTF16(aContentType));

    mData = nullptr;  // The MemoryBlobImpl takes ownership of the buffer
    mDataLen = 0;
    mDataBufferLen = 0;
  } else {
    blobImpl = new EmptyBlobImpl(NS_ConvertUTF8toUTF16(aContentType));
  }

  RefPtr<BlobCreationDoneRunnable> runnable =
      new BlobCreationDoneRunnable(this, aCallback, blobImpl, NS_OK);

  nsresult error =
      EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
  if (NS_WARN_IF(NS_FAILED(error))) {
    return;
  }
}

nsresult MutableBlobStorage::Append(const void* aData, uint32_t aLength) {
  // This method can be called on any thread.

  MutexAutoLock lock(mMutex);
  MOZ_ASSERT(mStorageState != eClosed);
  NS_ENSURE_ARG_POINTER(aData);

  if (!aLength) {
    return NS_OK;
  }

  // If eInMemory is the current Storage state, we could maybe migrate to
  // a temporary file.
  if (mStorageState == eInMemory && ShouldBeTemporaryStorage(lock, aLength) &&
      !MaybeCreateTemporaryFile(lock)) {
    return NS_ERROR_FAILURE;
  }

  // If we are already in the temporaryFile mode, we have to dispatch a
  // runnable.
  if (mStorageState == eInTemporaryFile) {
    // If a previous operation failed, let's return that error now.
    if (NS_FAILED(mErrorResult)) {
      return mErrorResult;
    }

    RefPtr<WriteRunnable> runnable =
        WriteRunnable::CopyBuffer(this, aData, aLength);
    if (NS_WARN_IF(!runnable)) {
      return NS_ERROR_OUT_OF_MEMORY;
    }

    nsresult rv = DispatchToIOThread(runnable.forget());
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    mDataLen += aLength;
    return NS_OK;
  }

  // By default, we store in memory.

  uint64_t offset = mDataLen;

  if (!ExpandBufferSize(lock, aLength)) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  memcpy((char*)mData + offset, aData, aLength);
  return NS_OK;
}

bool MutableBlobStorage::ExpandBufferSize(const MutexAutoLock& aProofOfLock,
                                          uint64_t aSize) {
  MOZ_ASSERT(mStorageState < eInTemporaryFile);

  if (mDataBufferLen >= mDataLen + aSize) {
    mDataLen += aSize;
    return true;
  }

  // Start at 1 or we'll loop forever.
  CheckedUint32 bufferLen =
      std::max<uint32_t>(static_cast<uint32_t>(mDataBufferLen), 1);
  while (bufferLen.isValid() && bufferLen.value() < mDataLen + aSize) {
    bufferLen *= 2;
  }

  if (!bufferLen.isValid()) {
    return false;
  }

  void* data = realloc(mData, bufferLen.value());
  if (!data) {
    return false;
  }

  mData = data;
  mDataBufferLen = bufferLen.value();
  mDataLen += aSize;
  return true;
}

bool MutableBlobStorage::ShouldBeTemporaryStorage(
    const MutexAutoLock& aProofOfLock, uint64_t aSize) const {
  MOZ_ASSERT(mStorageState == eInMemory);

  CheckedUint32 bufferSize = mDataLen;
  bufferSize += aSize;

  if (!bufferSize.isValid()) {
    return false;
  }

  return bufferSize.value() >= mMaxMemory;
}

bool MutableBlobStorage::MaybeCreateTemporaryFile(
    const MutexAutoLock& aProofOfLock) {
  mStorageState = eWaitingForTemporaryFile;

  if (!NS_IsMainThread()) {
    RefPtr<MutableBlobStorage> self = this;
    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
        "MutableBlobStorage::MaybeCreateTemporaryFile", [self]() {
          MutexAutoLock lock(self->mMutex);
          self->MaybeCreateTemporaryFileOnMainThread(lock);
          if (!self->mActor) {
            self->ErrorPropagated(NS_ERROR_FAILURE);
          }
        });
    EventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    return true;
  }

  MaybeCreateTemporaryFileOnMainThread(aProofOfLock);
  return !!mActor;
}

void MutableBlobStorage::MaybeCreateTemporaryFileOnMainThread(
    const MutexAutoLock& aProofOfLock) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!mActor);

  mozilla::ipc::PBackgroundChild* actorChild =
      mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
  if (NS_WARN_IF(!actorChild)) {
    return;
  }

  mActor = new TemporaryIPCBlobChild(this);
  actorChild->SendPTemporaryIPCBlobConstructor(mActor);

  // We need manually to increase the reference for this actor because the
  // IPC allocator method is not triggered. The Release() is called by IPDL
  // when the actor is deleted.
  mActor.get()->AddRef();

  // The actor will call us when the FileDescriptor is received.
}

void MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD) {
  MOZ_ASSERT(NS_IsMainThread());

  MutexAutoLock lock(mMutex);
  MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
             mStorageState == eClosed);
  MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
  MOZ_ASSERT(mActor);
  MOZ_ASSERT(aFD);

  // If the object has been already closed and we don't need to execute a
  // callback, we need just to close the file descriptor in the correct thread.
  if (mStorageState == eClosed && !mPendingCallback) {
    RefPtr<Runnable> runnable = new CloseFileRunnable(aFD);

    // If this dispatching fails, CloseFileRunnable will close the FD in the
    // DTOR on the current thread.
    (void)DispatchToIOThread(runnable.forget());

    // Let's inform the parent that we have nothing else to do.
    mActor->SendOperationFailed();
    mActor = nullptr;
    return;
  }

  // If we still receiving data, we can proceed in temporary-file mode.
  if (mStorageState == eWaitingForTemporaryFile) {
    mStorageState = eInTemporaryFile;
  }

  mFD = aFD;
  MOZ_ASSERT(NS_SUCCEEDED(mErrorResult));

  // This runnable takes the ownership of mData and it will write this buffer
  // into the temporary file.
  RefPtr<WriteRunnable> runnable =
      WriteRunnable::AdoptBuffer(this, mData, mDataLen);
  MOZ_ASSERT(runnable);

  mData = nullptr;

  nsresult rv = DispatchToIOThread(runnable.forget());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    // Shutting down, we cannot continue.
    return;
  }

  // If we are closed, it means that GetBlobImplWhenReady() has been called when
  // we were already waiting for a temporary file-descriptor. Finally we are
  // here, AdoptBuffer runnable is going to write the current buffer into this
  // file. After that, there is nothing else to write, and we dispatch
  // LastRunnable which ends up calling mPendingCallback via CreateBlobRunnable.
  if (mStorageState == eClosed) {
    MOZ_ASSERT(mPendingCallback);

    RefPtr<Runnable> runnable =
        new LastRunnable(this, mPendingContentType, mPendingCallback);
    (void)DispatchToIOThread(runnable.forget());

    mPendingCallback = nullptr;
  }
}

void MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
                                    const nsACString& aContentType) {
  MOZ_ASSERT(NS_IsMainThread());

  MutexAutoLock lock(mMutex);
  MOZ_ASSERT(mStorageState == eClosed);
  MOZ_ASSERT(aCallback);

  // If something went wrong in one of the previous WriteRunnable runnables,
  // it's time to report the error.
  if (NS_FAILED(mErrorResult)) {
    MOZ_ASSERT(!mFD);
    MOZ_ASSERT(!mActor);
    aCallback->OperationFailed(mErrorResult);
    return;
  }

  MOZ_ASSERT(mFD);
  MOZ_ASSERT(mActor);

  // Let's pass the FileDescriptor to the parent actor in order to keep the file
  // locked on windows.
  mActor->AskForBlob(aCallback, aContentType, mFD);

  // The previous operation has duplicated the file descriptor. Now we can close
  // mFD. The parent will take care of closing the duplicated file descriptor on
  // its side.
  RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
  (void)DispatchToIOThread(runnable.forget());

  mFD = nullptr;
  mActor = nullptr;
}

void MutableBlobStorage::ErrorPropagated(nsresult aRv) {
  MOZ_ASSERT(NS_IsMainThread());

  MutexAutoLock lock(mMutex);
  mErrorResult = aRv;

  if (mActor) {
    mActor->SendOperationFailed();
    mActor = nullptr;
  }
}

nsresult MutableBlobStorage::DispatchToIOThread(
    already_AddRefed<nsIRunnable> aRunnable) {
  if (!mTaskQueue) {
    nsCOMPtr<nsIEventTarget> target =
        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
    MOZ_ASSERT(target);

    mTaskQueue = TaskQueue::Create(target.forget(), "BlobStorage");
  }

  nsCOMPtr<nsIRunnable> runnable(aRunnable);
  nsresult rv = mTaskQueue->Dispatch(runnable.forget());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  return NS_OK;
}

size_t MutableBlobStorage::SizeOfCurrentMemoryBuffer() {
  MOZ_ASSERT(NS_IsMainThread());
  MutexAutoLock lock(mMutex);
  return mStorageState < eInTemporaryFile ? mDataLen : 0;
}

PRFileDesc* MutableBlobStorage::GetFD() {
  MOZ_ASSERT(!NS_IsMainThread());
  MutexAutoLock lock(mMutex);
  return mFD;
}

void MutableBlobStorage::CloseFD() {
  MOZ_ASSERT(!NS_IsMainThread());
  MutexAutoLock lock(mMutex);
  MOZ_ASSERT(mFD);

  PR_Close(mFD);
  mFD = nullptr;
}

}  // namespace mozilla::dom

Messung V0.5
C=91 H=100 G=95

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.