diff options
Diffstat (limited to 'netwerk/protocol/ftp/FTPChannelChild.cpp')
-rw-r--r-- | netwerk/protocol/ftp/FTPChannelChild.cpp | 932 |
1 files changed, 932 insertions, 0 deletions
diff --git a/netwerk/protocol/ftp/FTPChannelChild.cpp b/netwerk/protocol/ftp/FTPChannelChild.cpp new file mode 100644 index 000000000..f8284aae3 --- /dev/null +++ b/netwerk/protocol/ftp/FTPChannelChild.cpp @@ -0,0 +1,932 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et 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 "mozilla/net/NeckoChild.h" +#include "mozilla/net/ChannelDiverterChild.h" +#include "mozilla/net/FTPChannelChild.h" +#include "mozilla/dom/TabChild.h" +#include "nsFtpProtocolHandler.h" +#include "nsITabChild.h" +#include "nsStringStream.h" +#include "nsNetUtil.h" +#include "base/compiler_specific.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/ipc/URIUtils.h" +#include "SerializedLoadContext.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "nsIPrompt.h" + +using namespace mozilla::ipc; + +#undef LOG +#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args) + +namespace mozilla { +namespace net { + +FTPChannelChild::FTPChannelChild(nsIURI* uri) +: mIPCOpen(false) +, mUnknownDecoderInvolved(false) +, mCanceled(false) +, mSuspendCount(0) +, mIsPending(false) +, mLastModifiedTime(0) +, mStartPos(0) +, mDivertingToParent(false) +, mFlushedForDiversion(false) +, mSuspendSent(false) +{ + LOG(("Creating FTPChannelChild @%x\n", this)); + // grab a reference to the handler to ensure that it doesn't go away. + NS_ADDREF(gFtpHandler); + SetURI(uri); + mEventQ = new ChannelEventQueue(static_cast<nsIFTPChannel*>(this)); + + // We could support thread retargeting, but as long as we're being driven by + // IPDL on the main thread it doesn't buy us anything. + DisallowThreadRetargeting(); +} + +FTPChannelChild::~FTPChannelChild() +{ + LOG(("Destroying FTPChannelChild @%x\n", this)); + gFtpHandler->Release(); +} + +void +FTPChannelChild::AddIPDLReference() +{ + MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference"); + mIPCOpen = true; + AddRef(); +} + +void +FTPChannelChild::ReleaseIPDLReference() +{ + MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference"); + mIPCOpen = false; + Release(); +} + +//----------------------------------------------------------------------------- +// FTPChannelChild::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS_INHERITED(FTPChannelChild, + nsBaseChannel, + nsIFTPChannel, + nsIUploadChannel, + nsIResumableChannel, + nsIProxiedChannel, + nsIChildChannel, + nsIDivertableChannel) + +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +FTPChannelChild::GetLastModifiedTime(PRTime* lastModifiedTime) +{ + *lastModifiedTime = mLastModifiedTime; + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::SetLastModifiedTime(PRTime lastModifiedTime) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +FTPChannelChild::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID) +{ + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + mStartPos = aStartPos; + mEntityID = aEntityID; + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::GetEntityID(nsACString& entityID) +{ + entityID = mEntityID; + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo) +{ + DROP_DEAD(); +} + +NS_IMETHODIMP +FTPChannelChild::SetUploadStream(nsIInputStream* stream, + const nsACString& contentType, + int64_t contentLength) +{ + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + mUploadStream = stream; + // NOTE: contentLength is intentionally ignored here. + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::GetUploadStream(nsIInputStream** stream) +{ + NS_ENSURE_ARG_POINTER(stream); + *stream = mUploadStream; + NS_IF_ADDREF(*stream); + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext) +{ + LOG(("FTPChannelChild::AsyncOpen [this=%p]\n", this)); + + NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE); + NS_ENSURE_ARG_POINTER(listener); + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); + + // Port checked in parent, but duplicate here so we can return with error + // immediately, as we've done since before e10s. + nsresult rv; + rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate, + // because in the child ipdl, + // a typedef URI is defined... + if (NS_FAILED(rv)) + return rv; + + mozilla::dom::TabChild* tabChild = nullptr; + nsCOMPtr<nsITabChild> iTabChild; + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, + NS_GET_IID(nsITabChild), + getter_AddRefs(iTabChild)); + GetCallback(iTabChild); + if (iTabChild) { + tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); + } + if (MissingRequiredTabChild(tabChild, "ftp")) { + return NS_ERROR_ILLEGAL_VALUE; + } + + mListener = listener; + mListenerContext = aContext; + + // add ourselves to the load group. + if (mLoadGroup) + mLoadGroup->AddRequest(this, nullptr); + + OptionalInputStreamParams uploadStream; + nsTArray<mozilla::ipc::FileDescriptor> fds; + SerializeInputStream(mUploadStream, uploadStream, fds); + + MOZ_ASSERT(fds.IsEmpty()); + + FTPChannelOpenArgs openArgs; + SerializeURI(nsBaseChannel::URI(), openArgs.uri()); + openArgs.startPos() = mStartPos; + openArgs.entityID() = mEntityID; + openArgs.uploadStream() = uploadStream; + + nsCOMPtr<nsILoadInfo> loadInfo; + GetLoadInfo(getter_AddRefs(loadInfo)); + rv = mozilla::ipc::LoadInfoToLoadInfoArgs(loadInfo, &openArgs.loadInfo()); + NS_ENSURE_SUCCESS(rv, rv); + + gNeckoChild-> + SendPFTPChannelConstructor(this, tabChild, IPC::SerializedLoadContext(this), + openArgs); + + // The socket transport layer in the chrome process now has a logical ref to + // us until OnStopRequest is called. + AddIPDLReference(); + + mIsPending = true; + mWasOpened = true; + + return rv; +} + +NS_IMETHODIMP +FTPChannelChild::IsPending(bool* result) +{ + *result = mIsPending; + return NS_OK; +} + +nsresult +FTPChannelChild::OpenContentStream(bool async, + nsIInputStream** stream, + nsIChannel** channel) +{ + NS_RUNTIMEABORT("FTPChannel*Child* should never have OpenContentStream called!"); + return NS_OK; +} + +//----------------------------------------------------------------------------- +// FTPChannelChild::PFTPChannelChild +//----------------------------------------------------------------------------- + +class FTPStartRequestEvent : public ChannelEvent +{ +public: + FTPStartRequestEvent(FTPChannelChild* aChild, + const nsresult& aChannelStatus, + const int64_t& aContentLength, + const nsCString& aContentType, + const PRTime& aLastModified, + const nsCString& aEntityID, + const URIParams& aURI) + : mChild(aChild) + , mChannelStatus(aChannelStatus) + , mContentLength(aContentLength) + , mContentType(aContentType) + , mLastModified(aLastModified) + , mEntityID(aEntityID) + , mURI(aURI) + { + } + void Run() + { + mChild->DoOnStartRequest(mChannelStatus, mContentLength, mContentType, + mLastModified, mEntityID, mURI); + } + +private: + FTPChannelChild* mChild; + nsresult mChannelStatus; + int64_t mContentLength; + nsCString mContentType; + PRTime mLastModified; + nsCString mEntityID; + URIParams mURI; +}; + +bool +FTPChannelChild::RecvOnStartRequest(const nsresult& aChannelStatus, + const int64_t& aContentLength, + const nsCString& aContentType, + const PRTime& aLastModified, + const nsCString& aEntityID, + const URIParams& aURI) +{ + // mFlushedForDiversion and mDivertingToParent should NEVER be set at this + // stage, as they are set in the listener's OnStartRequest. + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, + "mFlushedForDiversion should be unset before OnStartRequest!"); + MOZ_RELEASE_ASSERT(!mDivertingToParent, + "mDivertingToParent should be unset before OnStartRequest!"); + + LOG(("FTPChannelChild::RecvOnStartRequest [this=%p]\n", this)); + + mEventQ->RunOrEnqueue(new FTPStartRequestEvent(this, aChannelStatus, + aContentLength, aContentType, + aLastModified, aEntityID, + aURI)); + return true; +} + +void +FTPChannelChild::DoOnStartRequest(const nsresult& aChannelStatus, + const int64_t& aContentLength, + const nsCString& aContentType, + const PRTime& aLastModified, + const nsCString& aEntityID, + const URIParams& aURI) +{ + LOG(("FTPChannelChild::DoOnStartRequest [this=%p]\n", this)); + + // mFlushedForDiversion and mDivertingToParent should NEVER be set at this + // stage, as they are set in the listener's OnStartRequest. + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, + "mFlushedForDiversion should be unset before OnStartRequest!"); + MOZ_RELEASE_ASSERT(!mDivertingToParent, + "mDivertingToParent should be unset before OnStartRequest!"); + + if (!mCanceled && NS_SUCCEEDED(mStatus)) { + mStatus = aChannelStatus; + } + + mContentLength = aContentLength; + SetContentType(aContentType); + mLastModifiedTime = aLastModified; + mEntityID = aEntityID; + + nsCString spec; + nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); + nsresult rv = uri->GetSpec(spec); + if (NS_SUCCEEDED(rv)) { + rv = nsBaseChannel::URI()->SetSpec(spec); + if (NS_FAILED(rv)) { + Cancel(rv); + } + } else { + Cancel(rv); + } + + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + rv = mListener->OnStartRequest(this, mListenerContext); + if (NS_FAILED(rv)) + Cancel(rv); + + if (mDivertingToParent) { + mListener = nullptr; + mListenerContext = nullptr; + if (mLoadGroup) { + mLoadGroup->RemoveRequest(this, nullptr, mStatus); + } + } +} + +class FTPDataAvailableEvent : public ChannelEvent +{ +public: + FTPDataAvailableEvent(FTPChannelChild* aChild, + const nsresult& aChannelStatus, + const nsCString& aData, + const uint64_t& aOffset, + const uint32_t& aCount) + : mChild(aChild) + , mChannelStatus(aChannelStatus) + , mData(aData) + , mOffset(aOffset) + , mCount(aCount) + { + } + void Run() + { + mChild->DoOnDataAvailable(mChannelStatus, mData, mOffset, mCount); + } + +private: + FTPChannelChild* mChild; + nsresult mChannelStatus; + nsCString mData; + uint64_t mOffset; + uint32_t mCount; +}; + +bool +FTPChannelChild::RecvOnDataAvailable(const nsresult& channelStatus, + const nsCString& data, + const uint64_t& offset, + const uint32_t& count) +{ + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, + "Should not be receiving any more callbacks from parent!"); + + LOG(("FTPChannelChild::RecvOnDataAvailable [this=%p]\n", this)); + + mEventQ->RunOrEnqueue(new FTPDataAvailableEvent(this, channelStatus, data, + offset, count), + mDivertingToParent); + + return true; +} + +class MaybeDivertOnDataFTPEvent : public ChannelEvent +{ + public: + MaybeDivertOnDataFTPEvent(FTPChannelChild* child, + const nsCString& data, + const uint64_t& offset, + const uint32_t& count) + : mChild(child) + , mData(data) + , mOffset(offset) + , mCount(count) {} + + void Run() + { + mChild->MaybeDivertOnData(mData, mOffset, mCount); + } + + private: + FTPChannelChild* mChild; + nsCString mData; + uint64_t mOffset; + uint32_t mCount; +}; + +void +FTPChannelChild::MaybeDivertOnData(const nsCString& data, + const uint64_t& offset, + const uint32_t& count) +{ + if (mDivertingToParent) { + SendDivertOnDataAvailable(data, offset, count); + } +} + +void +FTPChannelChild::DoOnDataAvailable(const nsresult& channelStatus, + const nsCString& data, + const uint64_t& offset, + const uint32_t& count) +{ + LOG(("FTPChannelChild::DoOnDataAvailable [this=%p]\n", this)); + + if (!mCanceled && NS_SUCCEEDED(mStatus)) { + mStatus = channelStatus; + } + + if (mDivertingToParent) { + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, + "Should not be processing any more callbacks from parent!"); + + SendDivertOnDataAvailable(data, offset, count); + return; + } + + if (mCanceled) + return; + + if (mUnknownDecoderInvolved) { + mUnknownDecoderEventQ.AppendElement( + MakeUnique<MaybeDivertOnDataFTPEvent>(this, data, offset, count)); + } + + // NOTE: the OnDataAvailable contract requires the client to read all the data + // in the inputstream. This code relies on that ('data' will go away after + // this function). Apparently the previous, non-e10s behavior was to actually + // support only reading part of the data, allowing later calls to read the + // rest. + nsCOMPtr<nsIInputStream> stringStream; + nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), + data.get(), + count, + NS_ASSIGNMENT_DEPEND); + if (NS_FAILED(rv)) { + Cancel(rv); + return; + } + + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + rv = mListener->OnDataAvailable(this, mListenerContext, + stringStream, offset, count); + if (NS_FAILED(rv)) + Cancel(rv); + stringStream->Close(); +} + +class FTPStopRequestEvent : public ChannelEvent +{ +public: + FTPStopRequestEvent(FTPChannelChild* aChild, + const nsresult& aChannelStatus, + const nsCString &aErrorMsg, + bool aUseUTF8) + : mChild(aChild) + , mChannelStatus(aChannelStatus) + , mErrorMsg(aErrorMsg) + , mUseUTF8(aUseUTF8) + { + } + void Run() + { + mChild->DoOnStopRequest(mChannelStatus, mErrorMsg, mUseUTF8); + } + +private: + FTPChannelChild* mChild; + nsresult mChannelStatus; + nsCString mErrorMsg; + bool mUseUTF8; +}; + +bool +FTPChannelChild::RecvOnStopRequest(const nsresult& aChannelStatus, + const nsCString &aErrorMsg, + const bool &aUseUTF8) +{ + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, + "Should not be receiving any more callbacks from parent!"); + + LOG(("FTPChannelChild::RecvOnStopRequest [this=%p status=%x]\n", + this, aChannelStatus)); + + mEventQ->RunOrEnqueue(new FTPStopRequestEvent(this, aChannelStatus, aErrorMsg, + aUseUTF8)); + return true; +} + +class nsFtpChildAsyncAlert : public Runnable +{ +public: + nsFtpChildAsyncAlert(nsIPrompt *aPrompter, nsString aResponseMsg) + : mPrompter(aPrompter) + , mResponseMsg(aResponseMsg) + { + MOZ_COUNT_CTOR(nsFtpChildAsyncAlert); + } +protected: + virtual ~nsFtpChildAsyncAlert() + { + MOZ_COUNT_DTOR(nsFtpChildAsyncAlert); + } +public: + NS_IMETHOD Run() override + { + if (mPrompter) { + mPrompter->Alert(nullptr, mResponseMsg.get()); + } + return NS_OK; + } +private: + nsCOMPtr<nsIPrompt> mPrompter; + nsString mResponseMsg; +}; + +class MaybeDivertOnStopFTPEvent : public ChannelEvent +{ + public: + MaybeDivertOnStopFTPEvent(FTPChannelChild* child, + const nsresult& aChannelStatus) + : mChild(child) + , mChannelStatus(aChannelStatus) {} + + void Run() + { + mChild->MaybeDivertOnStop(mChannelStatus); + } + + private: + FTPChannelChild* mChild; + nsresult mChannelStatus; +}; + +void +FTPChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus) +{ + if (mDivertingToParent) { + SendDivertOnStopRequest(aChannelStatus); + } +} + +void +FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus, + const nsCString &aErrorMsg, + bool aUseUTF8) +{ + LOG(("FTPChannelChild::DoOnStopRequest [this=%p status=%x]\n", + this, aChannelStatus)); + + if (mDivertingToParent) { + MOZ_RELEASE_ASSERT(!mFlushedForDiversion, + "Should not be processing any more callbacks from parent!"); + + SendDivertOnStopRequest(aChannelStatus); + return; + } + + if (!mCanceled) + mStatus = aChannelStatus; + + if (mUnknownDecoderInvolved) { + mUnknownDecoderEventQ.AppendElement( + MakeUnique<MaybeDivertOnStopFTPEvent>(this, aChannelStatus)); + } + + { // Ensure that all queued ipdl events are dispatched before + // we initiate protocol deletion below. + mIsPending = false; + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + (void)mListener->OnStopRequest(this, mListenerContext, aChannelStatus); + + if (NS_FAILED(aChannelStatus) && !aErrorMsg.IsEmpty()) { + nsCOMPtr<nsIPrompt> prompter; + GetCallback(prompter); + if (prompter) { + nsCOMPtr<nsIRunnable> alertEvent; + if (aUseUTF8) { + alertEvent = new nsFtpChildAsyncAlert(prompter, + NS_ConvertUTF8toUTF16(aErrorMsg)); + } else { + alertEvent = new nsFtpChildAsyncAlert(prompter, + NS_ConvertASCIItoUTF16(aErrorMsg)); + } + NS_DispatchToMainThread(alertEvent); + } + } + + mListener = nullptr; + mListenerContext = nullptr; + + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nullptr, aChannelStatus); + } + + // This calls NeckoChild::DeallocPFTPChannelChild(), which deletes |this| if IPDL + // holds the last reference. Don't rely on |this| existing after here! + Send__delete__(this); +} + +class FTPFailedAsyncOpenEvent : public ChannelEvent +{ + public: + FTPFailedAsyncOpenEvent(FTPChannelChild* aChild, nsresult aStatus) + : mChild(aChild), mStatus(aStatus) {} + void Run() { mChild->DoFailedAsyncOpen(mStatus); } + private: + FTPChannelChild* mChild; + nsresult mStatus; +}; + +bool +FTPChannelChild::RecvFailedAsyncOpen(const nsresult& statusCode) +{ + LOG(("FTPChannelChild::RecvFailedAsyncOpen [this=%p status=%x]\n", + this, statusCode)); + mEventQ->RunOrEnqueue(new FTPFailedAsyncOpenEvent(this, statusCode)); + return true; +} + +void +FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode) +{ + LOG(("FTPChannelChild::DoFailedAsyncOpen [this=%p status=%x]\n", + this, statusCode)); + mStatus = statusCode; + + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nullptr, statusCode); + + if (mListener) { + mListener->OnStartRequest(this, mListenerContext); + mIsPending = false; + mListener->OnStopRequest(this, mListenerContext, statusCode); + } else { + mIsPending = false; + } + + mListener = nullptr; + mListenerContext = nullptr; + + if (mIPCOpen) + Send__delete__(this); +} + +class FTPFlushedForDiversionEvent : public ChannelEvent +{ + public: + explicit FTPFlushedForDiversionEvent(FTPChannelChild* aChild) + : mChild(aChild) + { + MOZ_RELEASE_ASSERT(aChild); + } + + void Run() + { + mChild->FlushedForDiversion(); + } + private: + FTPChannelChild* mChild; +}; + +bool +FTPChannelChild::RecvFlushedForDiversion() +{ + LOG(("FTPChannelChild::RecvFlushedForDiversion [this=%p]\n", this)); + MOZ_ASSERT(mDivertingToParent); + + mEventQ->RunOrEnqueue(new FTPFlushedForDiversionEvent(this), true); + return true; +} + +void +FTPChannelChild::FlushedForDiversion() +{ + LOG(("FTPChannelChild::FlushedForDiversion [this=%p]\n", this)); + MOZ_RELEASE_ASSERT(mDivertingToParent); + + // Once this is set, it should not be unset before FTPChannelChild is taken + // down. After it is set, no OnStart/OnData/OnStop callbacks should be + // received from the parent channel, nor dequeued from the ChannelEventQueue. + mFlushedForDiversion = true; + + SendDivertComplete(); +} + +bool +FTPChannelChild::RecvDivertMessages() +{ + LOG(("FTPChannelChild::RecvDivertMessages [this=%p]\n", this)); + MOZ_RELEASE_ASSERT(mDivertingToParent); + MOZ_RELEASE_ASSERT(mSuspendCount > 0); + + // DivertTo() has been called on parent, so we can now start sending queued + // IPDL messages back to parent listener. + if (NS_WARN_IF(NS_FAILED(Resume()))) { + return false; + } + return true; +} + +class FTPDeleteSelfEvent : public ChannelEvent +{ + public: + explicit FTPDeleteSelfEvent(FTPChannelChild* aChild) + : mChild(aChild) {} + void Run() { mChild->DoDeleteSelf(); } + private: + FTPChannelChild* mChild; +}; + +bool +FTPChannelChild::RecvDeleteSelf() +{ + mEventQ->RunOrEnqueue(new FTPDeleteSelfEvent(this)); + return true; +} + +void +FTPChannelChild::DoDeleteSelf() +{ + if (mIPCOpen) + Send__delete__(this); +} + +NS_IMETHODIMP +FTPChannelChild::Cancel(nsresult status) +{ + LOG(("FTPChannelChild::Cancel [this=%p]\n", this)); + if (mCanceled) + return NS_OK; + + mCanceled = true; + mStatus = status; + if (mIPCOpen) + SendCancel(status); + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::Suspend() +{ + NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); + + LOG(("FTPChannelChild::Suspend [this=%p]\n", this)); + + // SendSuspend only once, when suspend goes from 0 to 1. + // Don't SendSuspend at all if we're diverting callbacks to the parent; + // suspend will be called at the correct time in the parent itself. + if (!mSuspendCount++ && !mDivertingToParent) { + SendSuspend(); + mSuspendSent = true; + } + mEventQ->Suspend(); + + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::Resume() +{ + NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); + + LOG(("FTPChannelChild::Resume [this=%p]\n", this)); + + // SendResume only once, when suspend count drops to 0. + // Don't SendResume at all if we're diverting callbacks to the parent (unless + // suspend was sent earlier); otherwise, resume will be called at the correct + // time in the parent itself. + if (!--mSuspendCount && (!mDivertingToParent || mSuspendSent)) { + SendResume(); + } + mEventQ->Resume(); + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// FTPChannelChild::nsIChildChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +FTPChannelChild::ConnectParent(uint32_t id) +{ + LOG(("FTPChannelChild::ConnectParent [this=%p]\n", this)); + + mozilla::dom::TabChild* tabChild = nullptr; + nsCOMPtr<nsITabChild> iTabChild; + NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, + NS_GET_IID(nsITabChild), + getter_AddRefs(iTabChild)); + GetCallback(iTabChild); + if (iTabChild) { + tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); + } + + // The socket transport in the chrome process now holds a logical ref to us + // until OnStopRequest, or we do a redirect, or we hit an IPDL error. + AddIPDLReference(); + + FTPChannelConnectArgs connectArgs(id); + + if (!gNeckoChild->SendPFTPChannelConstructor(this, tabChild, + IPC::SerializedLoadContext(this), + connectArgs)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, + nsISupports *aContext) +{ + LOG(("FTPChannelChild::CompleteRedirectSetup [this=%p]\n", this)); + + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); + + mIsPending = true; + mWasOpened = true; + mListener = listener; + mListenerContext = aContext; + + // add ourselves to the load group. + if (mLoadGroup) + mLoadGroup->AddRequest(this, nullptr); + + // We already have an open IPDL connection to the parent. If on-modify-request + // listeners or load group observers canceled us, let the parent handle it + // and send it back to us naturally. + return NS_OK; +} + +//----------------------------------------------------------------------------- +// FTPChannelChild::nsIDivertableChannel +//----------------------------------------------------------------------------- +NS_IMETHODIMP +FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild) +{ + MOZ_RELEASE_ASSERT(aChild); + MOZ_RELEASE_ASSERT(gNeckoChild); + MOZ_RELEASE_ASSERT(!mDivertingToParent); + + LOG(("FTPChannelChild::DivertToParent [this=%p]\n", this)); + + // We must fail DivertToParent() if there's no parent end of the channel (and + // won't be!) due to early failure. + if (NS_FAILED(mStatus) && !mIPCOpen) { + return mStatus; + } + + nsresult rv = Suspend(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // Once this is set, it should not be unset before the child is taken down. + mDivertingToParent = true; + + PChannelDiverterChild* diverter = + gNeckoChild->SendPChannelDiverterConstructor(this); + MOZ_RELEASE_ASSERT(diverter); + + *aChild = static_cast<ChannelDiverterChild*>(diverter); + + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::UnknownDecoderInvolvedKeepData() +{ + mUnknownDecoderInvolved = true; + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelChild::UnknownDecoderInvolvedOnStartRequestCalled() +{ + mUnknownDecoderInvolved = false; + + nsresult rv = NS_OK; + + if (mDivertingToParent) { + rv = mEventQ->PrependEvents(mUnknownDecoderEventQ); + } + mUnknownDecoderEventQ.Clear(); + + return rv; +} + +NS_IMETHODIMP +FTPChannelChild::GetDivertingToParent(bool* aDiverting) +{ + NS_ENSURE_ARG_POINTER(aDiverting); + *aDiverting = mDivertingToParent; + return NS_OK; +} + +} // namespace net +} // namespace mozilla + |