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