Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  BlobURLInputStream.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 "BlobURLInputStream.h"
#include "BlobURL.h"
#include "BlobURLChannel.h"
#include "BlobURLProtocolHandler.h"

#include "mozilla/ScopeExit.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/net/ContentRange.h"
#include "nsStreamUtils.h"
#include "nsMimeTypes.h"

namespace mozilla::dom {

NS_IMPL_ADDREF(BlobURLInputStream);
NS_IMPL_RELEASE(BlobURLInputStream);

NS_INTERFACE_MAP_BEGIN(BlobURLInputStream)
  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamLength)
  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStreamLength)
  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAsyncInputStream)
NS_INTERFACE_MAP_END

/* static */
already_AddRefed<nsIInputStream> BlobURLInputStream::Create(
    BlobURLChannel* const aChannel, BlobURL* const aBlobURL) {
  MOZ_ASSERT(NS_IsMainThread());

  if (NS_WARN_IF(!aChannel) || NS_WARN_IF(!aBlobURL)) {
    return nullptr;
  }

  nsAutoCString spec;

  nsresult rv = aBlobURL->GetSpec(spec);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return nullptr;
  }

  return MakeAndAddRef<BlobURLInputStream>(aChannel, spec);
}

// from nsIInputStream interface
NS_IMETHODIMP BlobURLInputStream::Close() {
  return CloseWithStatus(NS_BASE_STREAM_CLOSED);
}

NS_IMETHODIMP BlobURLInputStream::Available(uint64_t* aLength) {
  MutexAutoLock lock(mStateMachineMutex);

  if (mState == State::ERROR) {
    MOZ_ASSERT(NS_FAILED(mError));
    return mError;
  }

  if (mState == State::CLOSED) {
    return NS_BASE_STREAM_CLOSED;
  }

  if (mState == State::READY) {
    MOZ_ASSERT(mAsyncInputStream);
    return mAsyncInputStream->Available(aLength);
  }

  return NS_OK;
}

NS_IMETHODIMP BlobURLInputStream::StreamStatus() {
  MutexAutoLock lock(mStateMachineMutex);

  if (mState == State::ERROR) {
    MOZ_ASSERT(NS_FAILED(mError));
    return mError;
  }

  if (mState == State::CLOSED) {
    return NS_BASE_STREAM_CLOSED;
  }

  if (mState == State::READY) {
    MOZ_ASSERT(mAsyncInputStream);
    return mAsyncInputStream->StreamStatus();
  }

  return NS_OK;
}

NS_IMETHODIMP BlobURLInputStream::Read(char* aBuffer, uint32_t aCount,
                                       uint32_t* aReadCount) {
  MutexAutoLock lock(mStateMachineMutex);
  if (mState == State::ERROR) {
    MOZ_ASSERT(NS_FAILED(mError));
    return mError;
  }

  // Read() should not return NS_BASE_STREAM_CLOSED if stream is closed.
  // A read count of 0 should indicate closed or consumed stream.
  // See:
  // https://searchfox.org/mozilla-central/rev/559b25eb41c1cbffcb90a34e008b8288312fcd25/xpcom/io/nsIInputStream.idl#104
  if (mState == State::CLOSED) {
    *aReadCount = 0;
    return NS_OK;
  }

  if (mState == State::READY) {
    MOZ_ASSERT(mAsyncInputStream);
    nsresult rv = mAsyncInputStream->Read(aBuffer, aCount, aReadCount);
    if (NS_SUCCEEDED(rv) && aReadCount && !*aReadCount) {
      mState = State::CLOSED;
      ReleaseUnderlyingStream(lock);
    }
    return rv;
  }

  return NS_BASE_STREAM_WOULD_BLOCK;
}

NS_IMETHODIMP BlobURLInputStream::ReadSegments(nsWriteSegmentFun aWriter,
                                               void* aClosure, uint32_t aCount,
                                               uint32_t* aResult) {
  // This means the caller will have to wrap the stream in an
  // nsBufferedInputStream in order to use ReadSegments
  return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP BlobURLInputStream::IsNonBlocking(bool* aNonBlocking) {
  *aNonBlocking = true;
  return NS_OK;
}

// from nsIAsyncInputStream interface
NS_IMETHODIMP BlobURLInputStream::CloseWithStatus(nsresult aStatus) {
  MutexAutoLock lock(mStateMachineMutex);
  if (mState == State::READY) {
    MOZ_ASSERT(mAsyncInputStream);
    mAsyncInputStream->CloseWithStatus(aStatus);
  }

  mState = State::CLOSED;
  ReleaseUnderlyingStream(lock);
  return NS_OK;
}

NS_IMETHODIMP BlobURLInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
                                            uint32_t aFlags,
                                            uint32_t aRequestedCount,
                                            nsIEventTarget* aEventTarget) {
  MutexAutoLock lock(mStateMachineMutex);

  if (mState == State::ERROR) {
    MOZ_ASSERT(NS_FAILED(mError));
    return NS_ERROR_FAILURE;
  }

  // Pre-empting a valid callback with another is not allowed.
  if (NS_WARN_IF(mAsyncWaitCallback && aCallback &&
                 mAsyncWaitCallback != aCallback)) {
    return NS_ERROR_FAILURE;
  }

  mAsyncWaitTarget = aEventTarget;
  mAsyncWaitRequestedCount = aRequestedCount;
  mAsyncWaitFlags = aFlags;
  mAsyncWaitCallback = aCallback;

  if (mState == State::INITIAL) {
    mState = State::WAITING;
    // RetrieveBlobData will execute NotifyWWaitTarget() when retrieve succeeds
    // or fails
    if (NS_IsMainThread()) {
      RetrieveBlobData(lock);
      return NS_OK;
    }

    nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod(
        "BlobURLInputStream::CallRetrieveBlobData"this,
        &BlobURLInputStream::CallRetrieveBlobData);
    NS_DispatchToMainThread(runnable.forget(), NS_DISPATCH_NORMAL);
    return NS_OK;
  }

  if (mState == State::WAITING) {
    // RetrieveBlobData is already in progress and will execute
    // NotifyWaitTargets when retrieve succeeds or fails
    return NS_OK;
  }

  if (mState == State::READY) {
    // Ask the blob's input stream if reading is possible or not
    return mAsyncInputStream->AsyncWait(
        mAsyncWaitCallback ? this : nullptr, mAsyncWaitFlags,
        mAsyncWaitRequestedCount, mAsyncWaitTarget);
  }

  MOZ_ASSERT(mState == State::CLOSED);
  NotifyWaitTargets(lock);
  return NS_OK;
}

// from nsIInputStreamLength interface
NS_IMETHODIMP BlobURLInputStream::Length(int64_t* aLength) {
  MutexAutoLock lock(mStateMachineMutex);

  if (mState == State::CLOSED) {
    return NS_BASE_STREAM_CLOSED;
  }

  if (mState == State::ERROR) {
    MOZ_ASSERT(NS_FAILED(mError));
    return NS_ERROR_FAILURE;
  }

  if (mState == State::READY) {
    *aLength = mBlobSize;
    return NS_OK;
  }
  return NS_BASE_STREAM_WOULD_BLOCK;
}

// from nsIAsyncInputStreamLength interface
NS_IMETHODIMP BlobURLInputStream::AsyncLengthWait(
    nsIInputStreamLengthCallback* aCallback, nsIEventTarget* aEventTarget) {
  MutexAutoLock lock(mStateMachineMutex);

  if (mState == State::ERROR) {
    MOZ_ASSERT(NS_FAILED(mError));
    return mError;
  }

  // Pre-empting a valid callback with another is not allowed.
  if (mAsyncLengthWaitCallback && aCallback) {
    return NS_ERROR_FAILURE;
  }

  mAsyncLengthWaitTarget = aEventTarget;
  mAsyncLengthWaitCallback = aCallback;

  if (mState == State::INITIAL) {
    mState = State::WAITING;
    // RetrieveBlobData will execute NotifyWWaitTarget() when retrieve succeeds
    // or fails
    if (NS_IsMainThread()) {
      RetrieveBlobData(lock);
      return NS_OK;
    }

    nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod(
        "BlobURLInputStream::CallRetrieveBlobData"this,
        &BlobURLInputStream::CallRetrieveBlobData);
    NS_DispatchToMainThread(runnable.forget(), NS_DISPATCH_NORMAL);
    return NS_OK;
  }

  if (mState == State::WAITING) {
    // RetrieveBlobData is already in progress and will execute
    // NotifyWaitTargets when retrieve succeeds or fails
    return NS_OK;
  }

  // Since here the state must be READY (in which case the size of the blob is
  // already known) or CLOSED, callback can be called immediately
  NotifyWaitTargets(lock);
  return NS_OK;
}

// from nsIInputStreamCallback interface
NS_IMETHODIMP BlobURLInputStream::OnInputStreamReady(
    nsIAsyncInputStream* aStream) {
  nsCOMPtr<nsIInputStreamCallback> callback;

  {
    MutexAutoLock lock(mStateMachineMutex);
    MOZ_ASSERT_IF(mAsyncInputStream, aStream == mAsyncInputStream);

    // aborted in the meantime
    if (!mAsyncWaitCallback) {
      return NS_OK;
    }

    mAsyncWaitCallback.swap(callback);
    mAsyncWaitTarget = nullptr;
  }

  MOZ_ASSERT(callback);
  return callback->OnInputStreamReady(this);
}

// from nsIInputStreamLengthCallback interface
NS_IMETHODIMP BlobURLInputStream::OnInputStreamLengthReady(
    nsIAsyncInputStreamLength* aStream, int64_t aLength) {
  nsCOMPtr<nsIInputStreamLengthCallback> callback;
  {
    MutexAutoLock lock(mStateMachineMutex);

    // aborted in the meantime
    if (!mAsyncLengthWaitCallback) {
      return NS_OK;
    }

    mAsyncLengthWaitCallback.swap(callback);
    mAsyncLengthWaitCallback = nullptr;
  }

  return callback->OnInputStreamLengthReady(this, aLength);
}

// private:
BlobURLInputStream::~BlobURLInputStream() {
  if (mChannel) {
    NS_ReleaseOnMainThread("BlobURLInputStream::mChannel", mChannel.forget());
  }
}

BlobURLInputStream::BlobURLInputStream(BlobURLChannel* const aChannel,
                                       nsACString& aBlobURLSpec)
    : mChannel(aChannel),
      mBlobURLSpec(std::move(aBlobURLSpec)),
      mStateMachineMutex("BlobURLInputStream::mStateMachineMutex"),
      mState(State::INITIAL),
      mError(NS_OK),
      mBlobSize(-1),
      mAsyncWaitFlags(),
      mAsyncWaitRequestedCount() {}

void BlobURLInputStream::WaitOnUnderlyingStream(
    const MutexAutoLock& aProofOfLock) {
  if (mAsyncWaitCallback || mAsyncWaitTarget) {
    // AsyncWait should be called on the underlying stream
    mAsyncInputStream->AsyncWait(mAsyncWaitCallback ? this : nullptr,
                                 mAsyncWaitFlags, mAsyncWaitRequestedCount,
                                 mAsyncWaitTarget);
  }

  if (mAsyncLengthWaitCallback || mAsyncLengthWaitTarget) {
    // AsyncLengthWait should be called on the underlying stream
    nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
        do_QueryInterface(mAsyncInputStream);
    if (asyncStreamLength) {
      asyncStreamLength->AsyncLengthWait(
          mAsyncLengthWaitCallback ? this : nullptr, mAsyncLengthWaitTarget);
    }
  }
}

void BlobURLInputStream::CallRetrieveBlobData() {
  MutexAutoLock lock(mStateMachineMutex);
  RetrieveBlobData(lock);
}

void BlobURLInputStream::RetrieveBlobData(const MutexAutoLock& aProofOfLock) {
  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");

  MOZ_ASSERT(mState == State::WAITING);

  auto cleanupOnEarlyExit = MakeScopeExit([&] {
    mState = State::ERROR;
    mError = NS_ERROR_FAILURE;
    NS_ReleaseOnMainThread("BlobURLInputStream::mChannel", mChannel.forget());
    NotifyWaitTargets(aProofOfLock);
  });

  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
  nsCOMPtr<nsIPrincipal> loadingPrincipal;
  if (NS_WARN_IF(NS_FAILED(loadInfo->GetTriggeringPrincipal(
          getter_AddRefs(triggeringPrincipal)))) ||
      NS_WARN_IF(!triggeringPrincipal)) {
    NS_WARNING("Failed to get owning channel's triggering principal");
    return;
  }

  if (NS_WARN_IF(NS_FAILED(
          loadInfo->GetLoadingPrincipal(getter_AddRefs(loadingPrincipal))))) {
    NS_WARNING("Failed to get owning channel's loading principal");
    return;
  }

  nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
  loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));

  nsAutoString partKey;
  cookieJarSettings->GetPartitionKey(partKey);

  if (XRE_IsParentProcess() || !BlobURLSchemeIsHTTPOrHTTPS(mBlobURLSpec)) {
    RefPtr<BlobImpl> blobImpl;

    // Since revoked blobs are also retrieved, it is possible that the blob no
    // longer exists (due to the 5 second timeout) when execution reaches here
    if (!BlobURLProtocolHandler::GetDataEntry(
            mBlobURLSpec, getter_AddRefs(blobImpl), loadingPrincipal,
            triggeringPrincipal, loadInfo->GetOriginAttributes(),
            loadInfo->GetInnerWindowID(), NS_ConvertUTF16toUTF8(partKey),
            true /* AlsoIfRevoked */)) {
      NS_WARNING("Failed to get data entry principal. URL revoked?");
      return;
    }

    if (NS_WARN_IF(
            NS_FAILED(StoreBlobImplStream(blobImpl.forget(), aProofOfLock)))) {
      return;
    }

    mState = State::READY;

    // By design, execution can only reach here when a caller has called
    // AsyncWait or AsyncLengthWait on this stream. The underlying stream is
    // valid, but the caller should not be informed until that stream has data
    // to read or it is closed.
    WaitOnUnderlyingStream(aProofOfLock);

    cleanupOnEarlyExit.release();
    return;
  }

  ContentChild* contentChild{ContentChild::GetSingleton()};
  MOZ_ASSERT(contentChild);

  const RefPtr<BlobURLInputStream> self = this;

  cleanupOnEarlyExit.release();

  contentChild
      ->SendBlobURLDataRequest(
          mBlobURLSpec, WrapNotNull(triggeringPrincipal), loadingPrincipal,
          loadInfo->GetOriginAttributes(), loadInfo->GetInnerWindowID(),
          NS_ConvertUTF16toUTF8(partKey))
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [self](const BlobURLDataRequestResult& aResult) {
            MutexAutoLock lock(self->mStateMachineMutex);
            if (aResult.type() == BlobURLDataRequestResult::TIPCBlob) {
              if (self->mState == State::WAITING) {
                RefPtr<BlobImpl> blobImpl =
                    IPCBlobUtils::Deserialize(aResult.get_IPCBlob());
                if (blobImpl && self->StoreBlobImplStream(blobImpl.forget(),
                                                          lock) == NS_OK) {
                  self->mState = State::READY;
                  // By design, execution can only reach here when a caller has
                  // called AsyncWait or AsyncLengthWait on this stream. The
                  // underlying stream is valid, but the caller should not be
                  // informed until that stream has data to read or it is
                  // closed.
                  self->WaitOnUnderlyingStream(lock);
                  return;
                }
              } else {
                MOZ_ASSERT(self->mState == State::CLOSED);
                // Callback can be called immediately
                self->NotifyWaitTargets(lock);
                return;
              }
            }
            NS_WARNING("Blob data was not retrieved!");
            self->mState = State::ERROR;
            self->mError = aResult.type() == BlobURLDataRequestResult::Tnsresult
                               ? aResult.get_nsresult()
                               : NS_ERROR_FAILURE;
            NS_ReleaseOnMainThread("BlobURLInputStream::mChannel",
                                   self->mChannel.forget());
            self->NotifyWaitTargets(lock);
          },
          [self](mozilla::ipc::ResponseRejectReason aReason) {
            MutexAutoLock lock(self->mStateMachineMutex);
            NS_WARNING("IPC call to SendBlobURLDataRequest failed!");
            self->mState = State::ERROR;
            self->mError = NS_ERROR_FAILURE;
            NS_ReleaseOnMainThread("BlobURLInputStream::mChannel",
                                   self->mChannel.forget());
            self->NotifyWaitTargets(lock);
          });
}

nsresult BlobURLInputStream::StoreBlobImplStream(
    already_AddRefed<BlobImpl> aBlobImpl, const MutexAutoLock& aProofOfLock) {
  MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
  RefPtr<BlobImpl> blobImpl = aBlobImpl;
  nsAutoString blobContentType;
  nsAutoCString channelContentType;

  // If a Range header was in the request then fetch/XHR will have set a
  // ContentRange on the channel earlier so we may slice the blob now.
  blobImpl->GetType(blobContentType);
  const RefPtr<mozilla::net::ContentRange>& contentRange =
      mChannel->ContentRange();
  if (contentRange) {
    IgnoredErrorResult result;
    uint64_t start = contentRange->Start();
    uint64_t end = contentRange->End();
    RefPtr<BlobImpl> slice =
        blobImpl->CreateSlice(start, end - start + 1, blobContentType, result);
    if (!result.Failed()) {
      blobImpl = slice;
    }
  }

  mChannel->GetContentType(channelContentType);
  // A empty content type is the correct channel content type in the case of a
  // fetch of a blob where the type was not set. It is invalid in others cases
  // such as a XHR (See https://xhr.spec.whatwg.org/#response-mime-type). The
  // XMLHttpRequestMainThread will set the channel content type to the correct
  // fallback value before this point, so we need to be careful to only override
  // it when the blob type is valid.
  if (!blobContentType.IsEmpty() ||
      channelContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE)) {
    mChannel->SetContentType(NS_ConvertUTF16toUTF8(blobContentType));
  }

  auto cleanupOnExit = MakeScopeExit([&] { mChannel = nullptr; });

  if (blobImpl->IsFile()) {
    nsAutoString filename;
    blobImpl->GetName(filename);

    // Don't overwrite existing name.
    nsString ignored;
    bool hasName =
        NS_SUCCEEDED(mChannel->GetContentDispositionFilename(ignored));

    if (!filename.IsEmpty() && !hasName) {
      mChannel->SetContentDispositionFilename(filename);
    }
  }

  mozilla::ErrorResult errorResult;

  mBlobSize = blobImpl->GetSize(errorResult);

  if (NS_WARN_IF(errorResult.Failed())) {
    return errorResult.StealNSResult();
  }

  mChannel->SetContentLength(mBlobSize);

  nsCOMPtr<nsIInputStream> inputStream;
  blobImpl->CreateInputStream(getter_AddRefs(inputStream), errorResult);

  if (NS_WARN_IF(errorResult.Failed())) {
    return errorResult.StealNSResult();
  }

  if (NS_WARN_IF(!inputStream)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  nsresult rv = NS_MakeAsyncNonBlockingInputStream(
      inputStream.forget(), getter_AddRefs(mAsyncInputStream));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  if (NS_WARN_IF(!mAsyncInputStream)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  return NS_OK;
}

void BlobURLInputStream::NotifyWaitTargets(const MutexAutoLock& aProofOfLock) {
  if (mAsyncWaitCallback) {
    auto callback = mAsyncWaitTarget
                        ? NS_NewInputStreamReadyEvent(
                              "BlobURLInputStream::OnInputStreamReady",
                              mAsyncWaitCallback, mAsyncWaitTarget)
                        : mAsyncWaitCallback;

    mAsyncWaitCallback = nullptr;
    mAsyncWaitTarget = nullptr;
    callback->OnInputStreamReady(this);
  }

  if (mAsyncLengthWaitCallback) {
    const RefPtr<BlobURLInputStream> self = this;
    nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
        "BlobURLInputStream::OnInputStreamLengthReady", [self] {
          self->mAsyncLengthWaitCallback->OnInputStreamLengthReady(
              self, self->mBlobSize);
        });

    mAsyncLengthWaitCallback = nullptr;

    if (mAsyncLengthWaitTarget) {
      mAsyncLengthWaitTarget->Dispatch(runnable, NS_DISPATCH_NORMAL);
      mAsyncLengthWaitTarget = nullptr;
    } else {
      runnable->Run();
    }
  }
}

void BlobURLInputStream::ReleaseUnderlyingStream(
    const MutexAutoLock& aProofOfLock) {
  mAsyncInputStream = nullptr;
  mBlobSize = -1;
}

}  // namespace mozilla::dom

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

¤ Dauer der Verarbeitung: 0.2 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge