diff --git a/netwerk/protocol/ftp/FTPChannelParent.cpp b/netwerk/protocol/ftp/FTPChannelParent.cpp index 365bdc9130bb..99d4886bf973 100644 --- a/netwerk/protocol/ftp/FTPChannelParent.cpp +++ b/netwerk/protocol/ftp/FTPChannelParent.cpp @@ -55,6 +55,8 @@ FTPChannelParent::FTPChannelParent(const PBrowserOrId& aIframeEmbedding, } mObserver = new OfflineObserver(this); + + mEventQ = new ChannelEventQueue(static_cast(this)); } FTPChannelParent::~FTPChannelParent() @@ -243,6 +245,32 @@ FTPChannelParent::RecvResume() 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, @@ -260,6 +288,35 @@ FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data, return true; } + if (mEventQ->ShouldEnqueue()) { + mEventQ->Enqueue(new FTPDivertDataAvailableEvent(this, data, offset, + count)); + return true; + } + + DivertOnDataAvailable(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 stringStream; nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream), data.get(), count, NS_ASSIGNMENT_DEPEND); @@ -268,9 +325,11 @@ FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data, mChannel->Cancel(rv); } mStatus = rv; - return true; + return; } + AutoEventEnqueuer ensureSerialDispatch(mEventQ); + rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count); stringStream->Close(); @@ -280,9 +339,27 @@ FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data, } mStatus = rv; } - return true; } +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) { @@ -293,6 +370,27 @@ FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode) return false; } + if (mEventQ->ShouldEnqueue()) { + mEventQ->Enqueue(new FTPDivertStopRequestEvent(this, statusCode)); + return true; + } + + DivertOnStopRequest(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; @@ -304,10 +402,26 @@ FTPChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode) } } + AutoEventEnqueuer ensureSerialDispatch(mEventQ); OnStopRequest(mChannel, nullptr, status); - return true; } +class FTPDivertCompleteEvent : public ChannelEvent +{ +public: + explicit FTPDivertCompleteEvent(FTPChannelParent* aParent) + : mParent(aParent) + { + } + + void Run() { + mParent->DivertComplete(); + } + +private: + FTPChannelParent* mParent; +}; + bool FTPChannelParent::RecvDivertComplete() { @@ -318,13 +432,31 @@ FTPChannelParent::RecvDivertComplete() return false; } + if (mEventQ->ShouldEnqueue()) { + mEventQ->Enqueue(new FTPDivertCompleteEvent(this)); + return true; + } + + DivertComplete(); + 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); - return false; } - - return true; } //----------------------------------------------------------------------------- @@ -588,14 +720,17 @@ FTPChannelParent::StartDiversion() } } - // Call OnStartRequest for the "DivertTo" listener. - nsresult rv = OnStartRequest(mChannel, nullptr); - if (NS_FAILED(rv)) { - if (mChannel) { - mChannel->Cancel(rv); + { + 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; } - mStatus = rv; - return; } // After OnStartRequest has been called, tell FTPChannelChild to divert the diff --git a/netwerk/protocol/ftp/FTPChannelParent.h b/netwerk/protocol/ftp/FTPChannelParent.h index fcb2511acfb4..b4378f98719b 100644 --- a/netwerk/protocol/ftp/FTPChannelParent.h +++ b/netwerk/protocol/ftp/FTPChannelParent.h @@ -79,6 +79,16 @@ protected: // ChildChannel. Used during HTTP->FTP redirects. bool ConnectChannel(const uint32_t& channelId); + void DivertOnDataAvailable(const nsCString& data, + const uint64_t& offset, + const uint32_t& count); + void DivertOnStopRequest(const nsresult& statusCode); + void DivertComplete(); + + friend class FTPDivertDataAvailableEvent; + friend class FTPDivertStopRequestEvent; + friend class FTPDivertCompleteEvent; + virtual bool RecvCancel(const nsresult& status) override; virtual bool RecvSuspend() override; virtual bool RecvResume() override; @@ -119,6 +129,8 @@ protected: bool mSuspendedForDiversion; nsRefPtr mObserver; nsRefPtr mTabParent; + + nsRefPtr mEventQ; }; } // namespace net