For the Quatum DOM project, it's better to work in terms of event targets than threads. This patch converts DOM code to operate on event targets rather than threads, when possible. MozReview-Commit-ID: 5FgvpKadUA2
459 lines
11 KiB
C++
459 lines
11 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 "IPCBlobInputStream.h"
|
|
#include "IPCBlobInputStreamChild.h"
|
|
#include "IPCBlobInputStreamStorage.h"
|
|
#include "mozilla/ipc/InputStreamParams.h"
|
|
#include "nsIAsyncInputStream.h"
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
namespace {
|
|
|
|
class CallbackRunnable final : public CancelableRunnable
|
|
{
|
|
public:
|
|
static void
|
|
Execute(nsIInputStreamCallback* aCallback,
|
|
nsIEventTarget* aEventTarget,
|
|
IPCBlobInputStream* aStream)
|
|
{
|
|
RefPtr<CallbackRunnable> runnable =
|
|
new CallbackRunnable(aCallback, aStream);
|
|
|
|
nsCOMPtr<nsIEventTarget> target = aEventTarget;
|
|
if (!target) {
|
|
target = NS_GetCurrentThread();
|
|
}
|
|
|
|
target->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
|
}
|
|
|
|
NS_IMETHOD
|
|
Run() override
|
|
{
|
|
mCallback->OnInputStreamReady(mStream);
|
|
mCallback = nullptr;
|
|
mStream = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
CallbackRunnable(nsIInputStreamCallback* aCallback,
|
|
IPCBlobInputStream* aStream)
|
|
: mCallback(aCallback)
|
|
, mStream(aStream)
|
|
{
|
|
MOZ_ASSERT(mCallback);
|
|
MOZ_ASSERT(mStream);
|
|
}
|
|
|
|
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
|
RefPtr<IPCBlobInputStream> mStream;
|
|
};
|
|
|
|
} // anonymous
|
|
|
|
NS_IMPL_ADDREF(IPCBlobInputStream);
|
|
NS_IMPL_RELEASE(IPCBlobInputStream);
|
|
|
|
NS_INTERFACE_MAP_BEGIN(IPCBlobInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
|
|
NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
|
|
NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
|
|
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileMetadata, IsFileMetadata())
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
IPCBlobInputStream::IPCBlobInputStream(IPCBlobInputStreamChild* aActor)
|
|
: mActor(aActor)
|
|
, mState(eInit)
|
|
{
|
|
MOZ_ASSERT(aActor);
|
|
|
|
if (XRE_IsParentProcess()) {
|
|
nsCOMPtr<nsIInputStream> stream;
|
|
IPCBlobInputStreamStorage::Get()->GetStream(mActor->ID(),
|
|
getter_AddRefs(stream));
|
|
if (stream) {
|
|
mState = eRunning;
|
|
mRemoteStream = stream;
|
|
}
|
|
}
|
|
}
|
|
|
|
IPCBlobInputStream::~IPCBlobInputStream()
|
|
{
|
|
Close();
|
|
}
|
|
|
|
// nsIInputStream interface
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Available(uint64_t* aLength)
|
|
{
|
|
// We don't have a remoteStream yet. Let's return the full known size.
|
|
if (mState == eInit || mState == ePending) {
|
|
*aLength = mActor->Size();
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mState == eRunning) {
|
|
MOZ_ASSERT(mRemoteStream);
|
|
return mRemoteStream->Available(aLength);
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eClosed);
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
|
|
{
|
|
// Read is not available is we don't have a remoteStream.
|
|
if (mState == eInit || mState == ePending) {
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
}
|
|
|
|
if (mState == eRunning) {
|
|
return mRemoteStream->Read(aBuffer, aCount, aReadCount);
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eClosed);
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
|
|
uint32_t aCount, uint32_t *aResult)
|
|
{
|
|
// ReadSegments is not available is we don't have a remoteStream.
|
|
if (mState == eInit || mState == ePending) {
|
|
return NS_BASE_STREAM_WOULD_BLOCK;
|
|
}
|
|
|
|
if (mState == eRunning) {
|
|
return mRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eClosed);
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking)
|
|
{
|
|
*aNonBlocking = true;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Close()
|
|
{
|
|
if (mActor) {
|
|
mActor->ForgetStream(this);
|
|
mActor = nullptr;
|
|
}
|
|
|
|
if (mRemoteStream) {
|
|
mRemoteStream->Close();
|
|
mRemoteStream = nullptr;
|
|
}
|
|
|
|
mCallback = nullptr;
|
|
|
|
mState = eClosed;
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsICloneableInputStream interface
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::GetCloneable(bool* aCloneable)
|
|
{
|
|
*aCloneable = mState != eClosed;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Clone(nsIInputStream** aResult)
|
|
{
|
|
if (mState == eClosed) {
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
|
|
MOZ_ASSERT(mActor);
|
|
|
|
nsCOMPtr<nsIInputStream> stream = mActor->CreateStream();
|
|
if (!stream) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
stream.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIAsyncInputStream interface
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::CloseWithStatus(nsresult aStatus)
|
|
{
|
|
return Close();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
|
|
uint32_t aFlags, uint32_t aRequestedCount,
|
|
nsIEventTarget* aEventTarget)
|
|
{
|
|
// See IPCBlobInputStream.h for more information about this state machine.
|
|
|
|
switch (mState) {
|
|
// First call, we need to retrieve the stream from the parent actor.
|
|
case eInit:
|
|
MOZ_ASSERT(mActor);
|
|
|
|
mCallback = aCallback;
|
|
mCallbackEventTarget = aEventTarget;
|
|
mState = ePending;
|
|
|
|
mActor->StreamNeeded(this, aEventTarget);
|
|
return NS_OK;
|
|
|
|
// We are still waiting for the remote inputStream
|
|
case ePending:
|
|
if (mCallback && aCallback) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mCallback = aCallback;
|
|
mCallbackEventTarget = aEventTarget;
|
|
return NS_OK;
|
|
|
|
// We have the remote inputStream, let's check if we can execute the callback.
|
|
case eRunning:
|
|
return MaybeExecuteCallback(aCallback, aEventTarget);
|
|
|
|
// Stream is closed.
|
|
default:
|
|
MOZ_ASSERT(mState == eClosed);
|
|
return NS_BASE_STREAM_CLOSED;
|
|
}
|
|
}
|
|
|
|
void
|
|
IPCBlobInputStream::StreamReady(nsIInputStream* aInputStream)
|
|
{
|
|
// We have been closed in the meantime.
|
|
if (mState == eClosed) {
|
|
if (aInputStream) {
|
|
aInputStream->Close();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// If aInputStream is null, it means that the serialization went wrong or the
|
|
// stream is not available anymore. We keep the state as pending just to block
|
|
// any additional operation.
|
|
|
|
nsCOMPtr<nsIInputStreamCallback> callback;
|
|
callback.swap(mCallback);
|
|
|
|
nsCOMPtr<nsIEventTarget> callbackEventTarget;
|
|
callbackEventTarget.swap(mCallbackEventTarget);
|
|
|
|
if (aInputStream && callback) {
|
|
MOZ_ASSERT(mState == ePending);
|
|
|
|
mRemoteStream = aInputStream;
|
|
mState = eRunning;
|
|
|
|
MaybeExecuteCallback(callback, callbackEventTarget);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
IPCBlobInputStream::MaybeExecuteCallback(nsIInputStreamCallback* aCallback,
|
|
nsIEventTarget* aCallbackEventTarget)
|
|
{
|
|
MOZ_ASSERT(mState == eRunning);
|
|
MOZ_ASSERT(mRemoteStream);
|
|
|
|
// If the stream supports nsIAsyncInputStream, we need to call its AsyncWait
|
|
// and wait for OnInputStreamReady.
|
|
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(mRemoteStream);
|
|
if (asyncStream) {
|
|
// If the callback has been already set, we return an error.
|
|
if (mCallback && aCallback) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mCallback = aCallback;
|
|
mCallbackEventTarget = aCallbackEventTarget;
|
|
|
|
if (!mCallback) {
|
|
return NS_OK;
|
|
}
|
|
|
|
RefPtr<nsIEventTarget> target = GetCurrentThreadEventTarget();
|
|
return asyncStream->AsyncWait(this, 0, 0, target);
|
|
}
|
|
|
|
MOZ_ASSERT(!mCallback);
|
|
MOZ_ASSERT(!mCallbackEventTarget);
|
|
|
|
if (!aCallback) {
|
|
return NS_OK;
|
|
}
|
|
|
|
CallbackRunnable::Execute(aCallback, aCallbackEventTarget, this);
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIInputStreamCallback
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
|
{
|
|
// We have been closed in the meantime.
|
|
if (mState == eClosed) {
|
|
return NS_OK;
|
|
}
|
|
|
|
MOZ_ASSERT(mState == eRunning);
|
|
MOZ_ASSERT(mRemoteStream == aStream);
|
|
|
|
// The callback has been canceled in the meantime.
|
|
if (!mCallback) {
|
|
return NS_OK;
|
|
}
|
|
|
|
CallbackRunnable::Execute(mCallback, mCallbackEventTarget, this);
|
|
|
|
mCallback = nullptr;
|
|
mCallbackEventTarget = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// nsIIPCSerializableInputStream
|
|
|
|
void
|
|
IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
|
|
FileDescriptorArray& aFileDescriptors)
|
|
{
|
|
mozilla::ipc::IPCBlobInputStreamParams params;
|
|
params.id() = mActor->ID();
|
|
|
|
aParams = params;
|
|
}
|
|
|
|
bool
|
|
IPCBlobInputStream::Deserialize(const mozilla::ipc::InputStreamParams& aParams,
|
|
const FileDescriptorArray& aFileDescriptors)
|
|
{
|
|
MOZ_CRASH("This should never be called.");
|
|
return false;
|
|
}
|
|
|
|
mozilla::Maybe<uint64_t>
|
|
IPCBlobInputStream::ExpectedSerializedLength()
|
|
{
|
|
return mozilla::Nothing();
|
|
}
|
|
|
|
// nsIFileMetadata
|
|
|
|
bool
|
|
IPCBlobInputStream::IsFileMetadata() const
|
|
{
|
|
// We are nsIFileMetadata only if we have the remote stream and that is a
|
|
// nsIFileMetadata.
|
|
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
|
|
return !!fileMetadata;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::GetSize(int64_t* aRetval)
|
|
{
|
|
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
|
|
if (!fileMetadata) {
|
|
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return fileMetadata->GetSize(aRetval);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::GetLastModified(int64_t* aRetval)
|
|
{
|
|
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
|
|
if (!fileMetadata) {
|
|
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return fileMetadata->GetLastModified(aRetval);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::GetFileDescriptor(PRFileDesc** aRetval)
|
|
{
|
|
nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
|
|
if (!fileMetadata) {
|
|
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return fileMetadata->GetFileDescriptor(aRetval);
|
|
}
|
|
|
|
// nsISeekableStream
|
|
|
|
bool
|
|
IPCBlobInputStream::IsSeekableStream() const
|
|
{
|
|
// We are nsISeekableStream only if we have the remote stream and that is a
|
|
// nsISeekableStream.
|
|
nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mRemoteStream);
|
|
return !!seekableStream;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Seek(int32_t aWhence, int64_t aOffset)
|
|
{
|
|
nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mRemoteStream);
|
|
if (!seekableStream) {
|
|
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return seekableStream->Seek(aWhence, aOffset);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::Tell(int64_t *aResult)
|
|
{
|
|
nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mRemoteStream);
|
|
if (!seekableStream) {
|
|
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return seekableStream->Tell(aResult);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
IPCBlobInputStream::SetEOF()
|
|
{
|
|
// This is a read-only stream.
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|