diff options
Diffstat (limited to 'netwerk/protocol/ftp/FTPChannelParent.cpp')
-rw-r--r-- | netwerk/protocol/ftp/FTPChannelParent.cpp | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/netwerk/protocol/ftp/FTPChannelParent.cpp b/netwerk/protocol/ftp/FTPChannelParent.cpp new file mode 100644 index 000000000..cdd334971 --- /dev/null +++ b/netwerk/protocol/ftp/FTPChannelParent.cpp @@ -0,0 +1,896 @@ +/* -*- 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/FTPChannelParent.h" +#include "nsStringStream.h" +#include "mozilla/net/ChannelEventQueue.h" +#include "mozilla/dom/TabParent.h" +#include "nsFTPChannel.h" +#include "nsNetCID.h" +#include "nsNetUtil.h" +#include "nsQueryObject.h" +#include "nsFtpProtocolHandler.h" +#include "nsIAuthPrompt.h" +#include "nsIAuthPromptProvider.h" +#include "nsIEncodedChannel.h" +#include "nsIHttpChannelInternal.h" +#include "nsIForcePendingChannel.h" +#include "mozilla/ipc/InputStreamUtils.h" +#include "mozilla/ipc/URIUtils.h" +#include "mozilla/Unused.h" +#include "SerializedLoadContext.h" +#include "nsIContentPolicy.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/LoadInfo.h" + +using namespace mozilla::dom; +using namespace mozilla::ipc; + +#undef LOG +#define LOG(args) MOZ_LOG(gFTPLog, mozilla::LogLevel::Debug, args) + +namespace mozilla { +namespace net { + +FTPChannelParent::FTPChannelParent(const PBrowserOrId& aIframeEmbedding, + nsILoadContext* aLoadContext, + PBOverrideStatus aOverrideStatus) + : mIPCClosed(false) + , mLoadContext(aLoadContext) + , mPBOverride(aOverrideStatus) + , mStatus(NS_OK) + , mDivertingFromChild(false) + , mDivertedOnStartRequest(false) + , mSuspendedForDiversion(false) + , mUseUTF8(false) +{ + nsIProtocolHandler* handler; + CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler); + MOZ_ASSERT(handler, "no ftp handler"); + + if (aIframeEmbedding.type() == PBrowserOrId::TPBrowserParent) { + mTabParent = static_cast<dom::TabParent*>(aIframeEmbedding.get_PBrowserParent()); + } + + mEventQ = new ChannelEventQueue(static_cast<nsIParentChannel*>(this)); +} + +FTPChannelParent::~FTPChannelParent() +{ + gFtpHandler->Release(); +} + +void +FTPChannelParent::ActorDestroy(ActorDestroyReason why) +{ + // We may still have refcount>0 if the channel hasn't called OnStopRequest + // yet, but we must not send any more msgs to child. + mIPCClosed = true; +} + +//----------------------------------------------------------------------------- +// FTPChannelParent::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS(FTPChannelParent, + nsIStreamListener, + nsIParentChannel, + nsIInterfaceRequestor, + nsIRequestObserver, + nsIChannelEventSink, + nsIFTPChannelParentInternal) + +//----------------------------------------------------------------------------- +// FTPChannelParent::PFTPChannelParent +//----------------------------------------------------------------------------- + +//----------------------------------------------------------------------------- +// FTPChannelParent methods +//----------------------------------------------------------------------------- + +bool +FTPChannelParent::Init(const FTPChannelCreationArgs& aArgs) +{ + switch (aArgs.type()) { + case FTPChannelCreationArgs::TFTPChannelOpenArgs: + { + const FTPChannelOpenArgs& a = aArgs.get_FTPChannelOpenArgs(); + return DoAsyncOpen(a.uri(), a.startPos(), a.entityID(), a.uploadStream(), + a.loadInfo()); + } + case FTPChannelCreationArgs::TFTPChannelConnectArgs: + { + const FTPChannelConnectArgs& cArgs = aArgs.get_FTPChannelConnectArgs(); + return ConnectChannel(cArgs.channelId()); + } + default: + NS_NOTREACHED("unknown open type"); + return false; + } +} + +bool +FTPChannelParent::DoAsyncOpen(const URIParams& aURI, + const uint64_t& aStartPos, + const nsCString& aEntityID, + const OptionalInputStreamParams& aUploadStream, + const OptionalLoadInfoArgs& aLoadInfoArgs) +{ + nsresult rv; + + nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); + if (!uri) + return false; + +#ifdef DEBUG + LOG(("FTPChannelParent DoAsyncOpen [this=%p uri=%s]\n", + this, uri->GetSpecOrDefault().get())); +#endif + + nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv)); + if (NS_FAILED(rv)) { + return SendFailedAsyncOpen(rv); + } + + nsCOMPtr<nsILoadInfo> loadInfo; + rv = mozilla::ipc::LoadInfoArgsToLoadInfo(aLoadInfoArgs, + getter_AddRefs(loadInfo)); + if (NS_FAILED(rv)) { + return SendFailedAsyncOpen(rv); + } + + NeckoOriginAttributes attrs; + rv = loadInfo->GetOriginAttributes(&attrs); + if (NS_FAILED(rv)) { + return SendFailedAsyncOpen(rv); + } + + nsCOMPtr<nsIChannel> chan; + rv = NS_NewChannelInternal(getter_AddRefs(chan), uri, loadInfo, + nullptr, nullptr, + nsIRequest::LOAD_NORMAL, ios); + + if (NS_FAILED(rv)) + return SendFailedAsyncOpen(rv); + + mChannel = chan; + + // later on mChannel may become an HTTP channel (we'll be redirected to one + // if we're using a proxy), but for now this is safe + nsFtpChannel* ftpChan = static_cast<nsFtpChannel*>(mChannel.get()); + + if (mPBOverride != kPBOverride_Unset) { + ftpChan->SetPrivate(mPBOverride == kPBOverride_Private ? true : false); + } + rv = ftpChan->SetNotificationCallbacks(this); + if (NS_FAILED(rv)) + return SendFailedAsyncOpen(rv); + + nsTArray<mozilla::ipc::FileDescriptor> fds; + nsCOMPtr<nsIInputStream> upload = DeserializeInputStream(aUploadStream, fds); + if (upload) { + // contentType and contentLength are ignored + rv = ftpChan->SetUploadStream(upload, EmptyCString(), 0); + if (NS_FAILED(rv)) + return SendFailedAsyncOpen(rv); + } + + rv = ftpChan->ResumeAt(aStartPos, aEntityID); + if (NS_FAILED(rv)) + return SendFailedAsyncOpen(rv); + + if (loadInfo && loadInfo->GetEnforceSecurity()) { + rv = ftpChan->AsyncOpen2(this); + } + else { + rv = ftpChan->AsyncOpen(this, nullptr); + } + + if (NS_FAILED(rv)) + return SendFailedAsyncOpen(rv); + + return true; +} + +bool +FTPChannelParent::ConnectChannel(const uint32_t& channelId) +{ + nsresult rv; + + LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId)); + + nsCOMPtr<nsIChannel> channel; + rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel)); + if (NS_SUCCEEDED(rv)) + mChannel = channel; + + LOG((" found channel %p, rv=%08x", mChannel.get(), rv)); + + return true; +} + +bool +FTPChannelParent::RecvCancel(const nsresult& status) +{ + if (mChannel) + mChannel->Cancel(status); + + return true; +} + +bool +FTPChannelParent::RecvSuspend() +{ + if (mChannel) { + SuspendChannel(); + } + return true; +} + +bool +FTPChannelParent::RecvResume() +{ + if (mChannel) { + ResumeChannel(); + } + return true; +} + +class FTPDivertDataAvailableEvent : public ChannelEvent +{ +public: + FTPDivertDataAvailableEvent(FTPChannelParent* aParent, + const nsCString& data, + const uint64_t& offset, + const uint32_t& count) + : mParent(aParent) + , mData(data) + , mOffset(offset) + , mCount(count) + { + } + + void Run() + { + mParent->DivertOnDataAvailable(mData, mOffset, mCount); + } + +private: + FTPChannelParent* mParent; + nsCString mData; + uint64_t mOffset; + uint32_t mCount; +}; + +bool +FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data, + const uint64_t& offset, + const uint32_t& count) +{ + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot RecvDivertOnDataAvailable if diverting is not set!"); + FailDiversion(NS_ERROR_UNEXPECTED); + return false; + } + + // Drop OnDataAvailables if the parent was canceled already. + if (NS_FAILED(mStatus)) { + return true; + } + + mEventQ->RunOrEnqueue(new FTPDivertDataAvailableEvent(this, data, offset, + count)); + return true; +} + +void +FTPChannelParent::DivertOnDataAvailable(const nsCString& data, + const uint64_t& offset, + const uint32_t& count) +{ + LOG(("FTPChannelParent::DivertOnDataAvailable [this=%p]\n", this)); + + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot DivertOnDataAvailable if diverting is not set!"); + FailDiversion(NS_ERROR_UNEXPECTED); + return; + } + + // Drop OnDataAvailables if the parent was canceled already. + if (NS_FAILED(mStatus)) { + return; + } + + nsCOMPtr<nsIInputStream> stringStream; + nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(), + count, NS_ASSIGNMENT_DEPEND); + if (NS_FAILED(rv)) { + if (mChannel) { + mChannel->Cancel(rv); + } + mStatus = rv; + return; + } + + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + + rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count); + + stringStream->Close(); + if (NS_FAILED(rv)) { + if (mChannel) { + mChannel->Cancel(rv); + } + mStatus = rv; + } +} + +class FTPDivertStopRequestEvent : public ChannelEvent +{ +public: + FTPDivertStopRequestEvent(FTPChannelParent* aParent, + const nsresult& statusCode) + : mParent(aParent) + , mStatusCode(statusCode) + { + } + + void Run() { + mParent->DivertOnStopRequest(mStatusCode); + } + +private: + FTPChannelParent* mParent; + nsresult mStatusCode; +}; + +bool +FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode) +{ + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot RecvDivertOnStopRequest if diverting is not set!"); + FailDiversion(NS_ERROR_UNEXPECTED); + return false; + } + + mEventQ->RunOrEnqueue(new FTPDivertStopRequestEvent(this, statusCode)); + return true; +} + +void +FTPChannelParent::DivertOnStopRequest(const nsresult& statusCode) +{ + LOG(("FTPChannelParent::DivertOnStopRequest [this=%p]\n", this)); + + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot DivertOnStopRequest if diverting is not set!"); + FailDiversion(NS_ERROR_UNEXPECTED); + return; + } + + // Honor the channel's status even if the underlying transaction completed. + nsresult status = NS_FAILED(mStatus) ? mStatus : statusCode; + + // Reset fake pending status in case OnStopRequest has already been called. + if (mChannel) { + nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel); + if (forcePendingIChan) { + forcePendingIChan->ForcePending(false); + } + } + + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + OnStopRequest(mChannel, nullptr, status); +} + +class FTPDivertCompleteEvent : public ChannelEvent +{ +public: + explicit FTPDivertCompleteEvent(FTPChannelParent* aParent) + : mParent(aParent) + { + } + + void Run() { + mParent->DivertComplete(); + } + +private: + FTPChannelParent* mParent; +}; + +bool +FTPChannelParent::RecvDivertComplete() +{ + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot RecvDivertComplete if diverting is not set!"); + FailDiversion(NS_ERROR_UNEXPECTED); + return false; + } + + mEventQ->RunOrEnqueue(new FTPDivertCompleteEvent(this)); + return true; +} + +void +FTPChannelParent::DivertComplete() +{ + LOG(("FTPChannelParent::DivertComplete [this=%p]\n", this)); + + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot DivertComplete if diverting is not set!"); + FailDiversion(NS_ERROR_UNEXPECTED); + return; + } + + nsresult rv = ResumeForDiversion(); + if (NS_WARN_IF(NS_FAILED(rv))) { + FailDiversion(NS_ERROR_UNEXPECTED); + } +} + +//----------------------------------------------------------------------------- +// FTPChannelParent::nsIRequestObserver +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) +{ + LOG(("FTPChannelParent::OnStartRequest [this=%p]\n", this)); + + if (mDivertingFromChild) { + MOZ_RELEASE_ASSERT(mDivertToListener, + "Cannot divert if listener is unset!"); + return mDivertToListener->OnStartRequest(aRequest, aContext); + } + + nsCOMPtr<nsIChannel> chan = do_QueryInterface(aRequest); + MOZ_ASSERT(chan); + NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED); + + int64_t contentLength; + chan->GetContentLength(&contentLength); + nsCString contentType; + chan->GetContentType(contentType); + + nsCString entityID; + nsCOMPtr<nsIResumableChannel> resChan = do_QueryInterface(aRequest); + MOZ_ASSERT(resChan); // both FTP and HTTP should implement nsIResumableChannel + if (resChan) { + resChan->GetEntityID(entityID); + } + + PRTime lastModified = 0; + nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(aRequest); + if (ftpChan) { + ftpChan->GetLastModifiedTime(&lastModified); + } + nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(aRequest); + if (httpChan) { + httpChan->GetLastModifiedTime(&lastModified); + } + + URIParams uriparam; + nsCOMPtr<nsIURI> uri; + chan->GetURI(getter_AddRefs(uri)); + SerializeURI(uri, uriparam); + + if (mIPCClosed || !SendOnStartRequest(mStatus, contentLength, contentType, + lastModified, entityID, uriparam)) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelParent::OnStopRequest(nsIRequest* aRequest, + nsISupports* aContext, + nsresult aStatusCode) +{ + LOG(("FTPChannelParent::OnStopRequest: [this=%p status=%ul]\n", + this, aStatusCode)); + + if (mDivertingFromChild) { + MOZ_RELEASE_ASSERT(mDivertToListener, + "Cannot divert if listener is unset!"); + return mDivertToListener->OnStopRequest(aRequest, aContext, aStatusCode); + } + + if (mIPCClosed || !SendOnStopRequest(aStatusCode, mErrorMsg, mUseUTF8)) { + return NS_ERROR_UNEXPECTED; + } + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// FTPChannelParent::nsIStreamListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +FTPChannelParent::OnDataAvailable(nsIRequest* aRequest, + nsISupports* aContext, + nsIInputStream* aInputStream, + uint64_t aOffset, + uint32_t aCount) +{ + LOG(("FTPChannelParent::OnDataAvailable [this=%p]\n", this)); + + if (mDivertingFromChild) { + MOZ_RELEASE_ASSERT(mDivertToListener, + "Cannot divert if listener is unset!"); + return mDivertToListener->OnDataAvailable(aRequest, aContext, aInputStream, + aOffset, aCount); + } + + nsCString data; + nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount); + if (NS_FAILED(rv)) + return rv; + + if (mIPCClosed || !SendOnDataAvailable(mStatus, data, aOffset, aCount)) + return NS_ERROR_UNEXPECTED; + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// FTPChannelParent::nsIParentChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +FTPChannelParent::SetParentListener(HttpChannelParentListener* aListener) +{ + // Do not need ptr to HttpChannelParentListener. + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelParent::NotifyTrackingProtectionDisabled() +{ + // One day, this should probably be filled in. + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelParent::Delete() +{ + if (mIPCClosed || !SendDeleteSelf()) + return NS_ERROR_UNEXPECTED; + + return NS_OK; +} + +//----------------------------------------------------------------------------- +// FTPChannelParent::nsIInterfaceRequestor +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +FTPChannelParent::GetInterface(const nsIID& uuid, void** result) +{ + if (uuid.Equals(NS_GET_IID(nsIAuthPromptProvider)) || + uuid.Equals(NS_GET_IID(nsISecureBrowserUI))) { + if (mTabParent) { + return mTabParent->QueryInterface(uuid, result); + } + } else if (uuid.Equals(NS_GET_IID(nsIAuthPrompt)) || + uuid.Equals(NS_GET_IID(nsIAuthPrompt2))) { + nsCOMPtr<nsIAuthPromptProvider> provider(do_QueryObject(mTabParent)); + if (provider) { + return provider->GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL, + uuid, + result); + } + } + + // Only support nsILoadContext if child channel's callbacks did too + if (uuid.Equals(NS_GET_IID(nsILoadContext)) && mLoadContext) { + nsCOMPtr<nsILoadContext> copy = mLoadContext; + copy.forget(result); + return NS_OK; + } + + return QueryInterface(uuid, result); +} + +nsresult +FTPChannelParent::SuspendChannel() +{ + nsCOMPtr<nsIChannelWithDivertableParentListener> chan = + do_QueryInterface(mChannel); + if (chan) { + return chan->SuspendInternal(); + } else { + return mChannel->Suspend(); + } +} + +nsresult +FTPChannelParent::ResumeChannel() +{ + nsCOMPtr<nsIChannelWithDivertableParentListener> chan = + do_QueryInterface(mChannel); + if (chan) { + return chan->ResumeInternal(); + } else { + return mChannel->Resume(); + } +} + +//----------------------------------------------------------------------------- +// FTPChannelParent::ADivertableParentChannel +//----------------------------------------------------------------------------- +nsresult +FTPChannelParent::SuspendForDiversion() +{ + MOZ_ASSERT(mChannel); + if (NS_WARN_IF(mDivertingFromChild)) { + MOZ_ASSERT(!mDivertingFromChild, "Already suspended for diversion!"); + return NS_ERROR_UNEXPECTED; + } + + // Try suspending the channel. Allow it to fail, since OnStopRequest may have + // been called and thus the channel may not be pending. + nsresult rv = SuspendChannel(); + MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE); + mSuspendedForDiversion = NS_SUCCEEDED(rv); + + // Once this is set, no more OnStart/OnData/OnStop callbacks should be sent + // to the child. + mDivertingFromChild = true; + + nsCOMPtr<nsIChannelWithDivertableParentListener> chan = + do_QueryInterface(mChannel); + if (chan) { + chan->MessageDiversionStarted(this); + } + + return NS_OK; +} + +/* private, supporting function for ADivertableParentChannel */ +nsresult +FTPChannelParent::ResumeForDiversion() +{ + MOZ_ASSERT(mChannel); + MOZ_ASSERT(mDivertToListener); + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot ResumeForDiversion if not diverting!"); + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr<nsIChannelWithDivertableParentListener> chan = + do_QueryInterface(mChannel); + if (chan) { + chan->MessageDiversionStop(); + } + + if (mSuspendedForDiversion) { + nsresult rv = ResumeChannel(); + if (NS_WARN_IF(NS_FAILED(rv))) { + FailDiversion(NS_ERROR_UNEXPECTED, true); + return rv; + } + mSuspendedForDiversion = false; + } + + // Delete() will tear down IPDL, but ref from underlying nsFTPChannel will + // keep us alive if there's more data to be delivered to listener. + if (NS_WARN_IF(NS_FAILED(Delete()))) { + FailDiversion(NS_ERROR_UNEXPECTED); + return NS_ERROR_UNEXPECTED; + } + return NS_OK; +} + +nsresult +FTPChannelParent::SuspendMessageDiversion() +{ + // This only need to suspend message queue. + mEventQ->Suspend(); + return NS_OK; +} + +nsresult +FTPChannelParent::ResumeMessageDiversion() +{ + // This only need to resumes message queue. + mEventQ->Resume(); + return NS_OK; +} + +void +FTPChannelParent::DivertTo(nsIStreamListener *aListener) +{ + MOZ_ASSERT(aListener); + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot DivertTo new listener if diverting is not set!"); + return; + } + + if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) { + FailDiversion(NS_ERROR_UNEXPECTED); + return; + } + + mDivertToListener = aListener; + + // Call OnStartRequest and SendDivertMessages asynchronously to avoid + // reentering client context. + NS_DispatchToCurrentThread( + NewRunnableMethod(this, &FTPChannelParent::StartDiversion)); + return; +} + +void +FTPChannelParent::StartDiversion() +{ + if (NS_WARN_IF(!mDivertingFromChild)) { + MOZ_ASSERT(mDivertingFromChild, + "Cannot StartDiversion if diverting is not set!"); + return; + } + + // Fake pending status in case OnStopRequest has already been called. + if (mChannel) { + nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel); + if (forcePendingIChan) { + forcePendingIChan->ForcePending(true); + } + } + + { + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + // Call OnStartRequest for the "DivertTo" listener. + nsresult rv = OnStartRequest(mChannel, nullptr); + if (NS_FAILED(rv)) { + if (mChannel) { + mChannel->Cancel(rv); + } + mStatus = rv; + return; + } + } + + // After OnStartRequest has been called, tell FTPChannelChild to divert the + // OnDataAvailables and OnStopRequest to this FTPChannelParent. + if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) { + FailDiversion(NS_ERROR_UNEXPECTED); + return; + } +} + +class FTPFailDiversionEvent : public Runnable +{ +public: + FTPFailDiversionEvent(FTPChannelParent *aChannelParent, + nsresult aErrorCode, + bool aSkipResume) + : mChannelParent(aChannelParent) + , mErrorCode(aErrorCode) + , mSkipResume(aSkipResume) + { + MOZ_RELEASE_ASSERT(aChannelParent); + MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); + } + NS_IMETHOD Run() override + { + mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume); + return NS_OK; + } +private: + RefPtr<FTPChannelParent> mChannelParent; + nsresult mErrorCode; + bool mSkipResume; +}; + +void +FTPChannelParent::FailDiversion(nsresult aErrorCode, + bool aSkipResume) +{ + MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); + MOZ_RELEASE_ASSERT(mDivertingFromChild); + MOZ_RELEASE_ASSERT(mDivertToListener); + MOZ_RELEASE_ASSERT(mChannel); + + NS_DispatchToCurrentThread( + new FTPFailDiversionEvent(this, aErrorCode, aSkipResume)); +} + +void +FTPChannelParent::NotifyDiversionFailed(nsresult aErrorCode, + bool aSkipResume) +{ + MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode)); + MOZ_RELEASE_ASSERT(mDivertingFromChild); + MOZ_RELEASE_ASSERT(mDivertToListener); + MOZ_RELEASE_ASSERT(mChannel); + + mChannel->Cancel(aErrorCode); + nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel); + if (forcePendingIChan) { + forcePendingIChan->ForcePending(false); + } + + bool isPending = false; + nsresult rv = mChannel->IsPending(&isPending); + MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv)); + + // Resume only we suspended earlier. + if (mSuspendedForDiversion) { + ResumeChannel(); + } + // Channel has already sent OnStartRequest to the child, so ensure that we + // call it here if it hasn't already been called. + if (!mDivertedOnStartRequest) { + nsCOMPtr<nsIForcePendingChannel> forcePendingIChan = do_QueryInterface(mChannel); + if (forcePendingIChan) { + forcePendingIChan->ForcePending(true); + } + mDivertToListener->OnStartRequest(mChannel, nullptr); + + if (forcePendingIChan) { + forcePendingIChan->ForcePending(false); + } + } + // If the channel is pending, it will call OnStopRequest itself; otherwise, do + // it here. + if (!isPending) { + mDivertToListener->OnStopRequest(mChannel, nullptr, aErrorCode); + } + mDivertToListener = nullptr; + mChannel = nullptr; + + if (!mIPCClosed) { + Unused << SendDeleteSelf(); + } +} + +//----------------------------------------------------------------------------- +// FTPChannelParent::nsIChannelEventSink +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +FTPChannelParent::AsyncOnChannelRedirect( + nsIChannel *oldChannel, + nsIChannel *newChannel, + uint32_t redirectFlags, + nsIAsyncVerifyRedirectCallback* callback) +{ + nsCOMPtr<nsIFTPChannel> ftpChan = do_QueryInterface(newChannel); + if (!ftpChan) { + // when FTP is set to use HTTP proxying, we wind up getting redirected to an HTTP channel. + nsCOMPtr<nsIHttpChannel> httpChan = do_QueryInterface(newChannel); + if (!httpChan) + return NS_ERROR_UNEXPECTED; + } + mChannel = newChannel; + callback->OnRedirectVerifyCallback(NS_OK); + return NS_OK; +} + +NS_IMETHODIMP +FTPChannelParent::SetErrorMsg(const char *aMsg, bool aUseUTF8) +{ + mErrorMsg = aMsg; + mUseUTF8 = aUseUTF8; + return NS_OK; +} + +//--------------------- +} // namespace net +} // namespace mozilla + |