зеркало из https://github.com/mozilla/gecko-dev.git
Bug 975338 - Implement nsI|ADivertableChannel in FTPChannelChild|Parent r=jduell
This commit is contained in:
Родитель
54ba73d1af
Коммит
b040432bc0
|
@ -156,7 +156,7 @@ nsBaseChannel::ContinueRedirect()
|
|||
bool
|
||||
nsBaseChannel::HasContentTypeHint() const
|
||||
{
|
||||
NS_ASSERTION(!IsPending(), "HasContentTypeHint called too late");
|
||||
NS_ASSERTION(!Pending(), "HasContentTypeHint called too late");
|
||||
return !mContentType.EqualsLiteral(UNKNOWN_CONTENT_TYPE);
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ nsBaseChannel::BeginPumpingData()
|
|||
return rv;
|
||||
}
|
||||
|
||||
// By assigning mPump, we flag this channel as pending (see IsPending). It's
|
||||
// By assigning mPump, we flag this channel as pending (see Pending). It's
|
||||
// important that the pending flag is set when we call into the stream (the
|
||||
// call to AsyncRead results in the stream's AsyncWait method being called)
|
||||
// and especially when we call into the loadgroup. Our caller takes care to
|
||||
|
@ -312,7 +312,7 @@ nsBaseChannel::GetName(nsACString &result)
|
|||
NS_IMETHODIMP
|
||||
nsBaseChannel::IsPending(bool *result)
|
||||
{
|
||||
*result = IsPending();
|
||||
*result = Pending();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -725,7 +725,7 @@ nsBaseChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt,
|
|||
if (NS_SUCCEEDED(mStatus))
|
||||
mStatus = status;
|
||||
|
||||
// Cause IsPending to return false.
|
||||
// Cause Pending to return false.
|
||||
mPump = nullptr;
|
||||
|
||||
if (mListener) // null in case of redirect
|
||||
|
|
|
@ -157,7 +157,7 @@ public:
|
|||
}
|
||||
|
||||
// This is a short-cut to calling nsIRequest::IsPending()
|
||||
bool IsPending() const {
|
||||
virtual bool Pending() const {
|
||||
return mPump || mWaitingOnAsyncRedirect;
|
||||
}
|
||||
|
||||
|
|
|
@ -431,7 +431,7 @@ nsFileChannel::SetUploadStream(nsIInputStream *stream,
|
|||
const nsACString &contentType,
|
||||
int64_t contentLength)
|
||||
{
|
||||
NS_ENSURE_TRUE(!IsPending(), NS_ERROR_IN_PROGRESS);
|
||||
NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
|
||||
|
||||
if ((mUploadStream = stream)) {
|
||||
mUploadLength = contentLength;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* 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"
|
||||
|
@ -33,6 +34,9 @@ FTPChannelChild::FTPChannelChild(nsIURI* uri)
|
|||
, mWasOpened(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.
|
||||
|
@ -67,13 +71,14 @@ FTPChannelChild::ReleaseIPDLReference()
|
|||
// FTPChannelChild::nsISupports
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED5(FTPChannelChild,
|
||||
NS_IMPL_ISUPPORTS_INHERITED6(FTPChannelChild,
|
||||
nsBaseChannel,
|
||||
nsIFTPChannel,
|
||||
nsIUploadChannel,
|
||||
nsIResumableChannel,
|
||||
nsIProxiedChannel,
|
||||
nsIChildChannel)
|
||||
nsIChildChannel,
|
||||
nsIDivertableChannel)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
|
@ -242,6 +247,13 @@ FTPChannelChild::RecvOnStartRequest(const int64_t& aContentLength,
|
|||
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!");
|
||||
|
||||
if (mEventQ->ShouldEnqueue()) {
|
||||
mEventQ->Enqueue(new FTPStartRequestEvent(this, aContentLength, aContentType,
|
||||
aLastModified, aEntityID, aURI));
|
||||
|
@ -261,6 +273,13 @@ FTPChannelChild::DoOnStartRequest(const int64_t& aContentLength,
|
|||
{
|
||||
LOG(("FTPChannelChild::RecvOnStartRequest [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!");
|
||||
|
||||
mContentLength = aContentLength;
|
||||
SetContentType(aContentType);
|
||||
mLastModifiedTime = aLastModified;
|
||||
|
@ -275,6 +294,14 @@ FTPChannelChild::DoOnStartRequest(const int64_t& aContentLength,
|
|||
nsresult 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
|
||||
|
@ -296,9 +323,15 @@ FTPChannelChild::RecvOnDataAvailable(const nsCString& data,
|
|||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be receiving any more callbacks from parent!");
|
||||
|
||||
if (mEventQ->ShouldEnqueue()) {
|
||||
mEventQ->Enqueue(new FTPDataAvailableEvent(this, data, offset, count));
|
||||
} else {
|
||||
MOZ_RELEASE_ASSERT(!mDivertingToParent,
|
||||
"ShouldEnqueue when diverting to parent!");
|
||||
|
||||
DoOnDataAvailable(data, offset, count);
|
||||
}
|
||||
return true;
|
||||
|
@ -311,6 +344,14 @@ FTPChannelChild::DoOnDataAvailable(const nsCString& data,
|
|||
{
|
||||
LOG(("FTPChannelChild::RecvOnDataAvailable [this=%p]\n", this));
|
||||
|
||||
if (mDivertingToParent) {
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be processing any more callbacks from parent!");
|
||||
|
||||
SendDivertOnDataAvailable(data, offset, count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
|
@ -351,6 +392,9 @@ class FTPStopRequestEvent : public ChannelEvent
|
|||
bool
|
||||
FTPChannelChild::RecvOnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be receiving any more callbacks from parent!");
|
||||
|
||||
if (mEventQ->ShouldEnqueue()) {
|
||||
mEventQ->Enqueue(new FTPStopRequestEvent(this, statusCode));
|
||||
} else {
|
||||
|
@ -365,6 +409,14 @@ FTPChannelChild::DoOnStopRequest(const nsresult& statusCode)
|
|||
LOG(("FTPChannelChild::RecvOnStopRequest [this=%p status=%u]\n",
|
||||
this, statusCode));
|
||||
|
||||
if (mDivertingToParent) {
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be processing any more callbacks from parent!");
|
||||
|
||||
SendDivertOnStopRequest(statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mCanceled)
|
||||
mStatus = statusCode;
|
||||
|
||||
|
@ -430,17 +482,62 @@ FTPChannelChild::DoFailedAsyncOpen(const nsresult& statusCode)
|
|||
Send__delete__(this);
|
||||
}
|
||||
|
||||
class FTPFlushedForDiversionEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
FTPFlushedForDiversionEvent(FTPChannelChild* aChild)
|
||||
: mChild(aChild)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aChild);
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->FlushedForDiversion();
|
||||
}
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
};
|
||||
|
||||
bool
|
||||
FTPChannelChild::RecvFlushedForDiversion()
|
||||
{
|
||||
return false;
|
||||
MOZ_ASSERT(mDivertingToParent);
|
||||
|
||||
if (mEventQ->ShouldEnqueue()) {
|
||||
mEventQ->Enqueue(new FTPFlushedForDiversionEvent(this));
|
||||
} else {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::FlushedForDiversion()
|
||||
{
|
||||
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()
|
||||
{
|
||||
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
|
||||
{
|
||||
|
@ -487,8 +584,13 @@ NS_IMETHODIMP
|
|||
FTPChannelChild::Suspend()
|
||||
{
|
||||
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
|
||||
if (!mSuspendCount++) {
|
||||
|
||||
// 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();
|
||||
|
||||
|
@ -500,7 +602,11 @@ FTPChannelChild::Resume()
|
|||
{
|
||||
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
if (!--mSuspendCount) {
|
||||
// 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();
|
||||
|
@ -564,6 +670,39 @@ FTPChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelChild::nsIDivertableChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aChild);
|
||||
MOZ_RELEASE_ASSERT(gNeckoChild);
|
||||
MOZ_RELEASE_ASSERT(!mDivertingToParent);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "nsIProxiedChannel.h"
|
||||
#include "nsIResumableChannel.h"
|
||||
#include "nsIChildChannel.h"
|
||||
#include "nsIDivertableChannel.h"
|
||||
|
||||
#include "nsIStreamListener.h"
|
||||
#include "PrivateBrowsingChannel.h"
|
||||
|
@ -35,6 +36,7 @@ class FTPChannelChild : public PFTPChannelChild
|
|||
, public nsIResumableChannel
|
||||
, public nsIProxiedChannel
|
||||
, public nsIChildChannel
|
||||
, public nsIDivertableChannel
|
||||
{
|
||||
public:
|
||||
typedef ::nsIStreamListener nsIStreamListener;
|
||||
|
@ -45,6 +47,7 @@ public:
|
|||
NS_DECL_NSIRESUMABLECHANNEL
|
||||
NS_DECL_NSIPROXIEDCHANNEL
|
||||
NS_DECL_NSICHILDCHANNEL
|
||||
NS_DECL_NSIDIVERTABLECHANNEL
|
||||
|
||||
NS_IMETHOD Cancel(nsresult status);
|
||||
NS_IMETHOD Suspend();
|
||||
|
@ -68,6 +71,8 @@ public:
|
|||
|
||||
bool IsSuspended();
|
||||
|
||||
void FlushedForDiversion();
|
||||
|
||||
protected:
|
||||
bool RecvOnStartRequest(const int64_t& aContentLength,
|
||||
const nsCString& aContentType,
|
||||
|
@ -114,6 +119,15 @@ private:
|
|||
PRTime mLastModifiedTime;
|
||||
uint64_t mStartPos;
|
||||
nsCString mEntityID;
|
||||
|
||||
// Once set, OnData and possibly OnStop will be diverted to the parent.
|
||||
bool mDivertingToParent;
|
||||
// Once set, no OnStart/OnData/OnStop callbacks should be received from the
|
||||
// parent channel, nor dequeued from the ChannelEventQueue.
|
||||
bool mFlushedForDiversion;
|
||||
// Set if SendSuspend is called. Determines if SendResume is needed when
|
||||
// diverting callbacks to parent.
|
||||
bool mSuspendSent;
|
||||
};
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsFtpProtocolHandler.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "mozilla/ipc/URIUtils.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "SerializedLoadContext.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
@ -25,6 +26,10 @@ FTPChannelParent::FTPChannelParent(nsILoadContext* aLoadContext, PBOverrideStatu
|
|||
: mIPCClosed(false)
|
||||
, mLoadContext(aLoadContext)
|
||||
, mPBOverride(aOverrideStatus)
|
||||
, mStatus(NS_OK)
|
||||
, mDivertingFromChild(false)
|
||||
, mDivertedOnStartRequest(false)
|
||||
, mSuspendedForDiversion(false)
|
||||
{
|
||||
nsIProtocolHandler* handler;
|
||||
CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
|
||||
|
@ -184,21 +189,82 @@ 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;
|
||||
}
|
||||
|
||||
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 true;
|
||||
}
|
||||
|
||||
rv = OnDataAvailable(mChannel, nullptr, stringStream, offset, count);
|
||||
|
||||
stringStream->Close();
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(rv);
|
||||
}
|
||||
mStatus = rv;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
mChannel->ForcePending(false);
|
||||
}
|
||||
|
||||
OnStopRequest(mChannel, nullptr, status);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FTPChannelParent::RecvDivertComplete()
|
||||
{
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot RecvDivertComplete if diverting is not set!");
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = ResumeForDiversion();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// FTPChannelParent::nsIRequestObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -208,6 +274,12 @@ 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);
|
||||
|
@ -255,6 +327,12 @@ FTPChannelParent::OnStopRequest(nsIRequest* aRequest,
|
|||
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)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
@ -275,6 +353,13 @@ FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
|
|||
{
|
||||
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))
|
||||
|
@ -316,6 +401,195 @@ FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
|
|||
return QueryInterface(uuid, result);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// 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.
|
||||
DebugOnly<nsresult> rv = mChannel->Suspend();
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (mSuspendedForDiversion) {
|
||||
nsresult rv = mChannel->Resume();
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
NS_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) {
|
||||
mChannel->ForcePending(true);
|
||||
}
|
||||
|
||||
// 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 nsRunnable
|
||||
{
|
||||
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()
|
||||
{
|
||||
mChannelParent->NotifyDiversionFailed(mErrorCode, mSkipResume);
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<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);
|
||||
|
||||
mChannel->ForcePending(false);
|
||||
|
||||
bool isPending = false;
|
||||
nsresult rv = mChannel->IsPending(&isPending);
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
// Resume only we suspended earlier.
|
||||
if (mSuspendedForDiversion) {
|
||||
mChannel->Resume();
|
||||
}
|
||||
// Channel has already sent OnStartRequest to the child, so ensure that we
|
||||
// call it here if it hasn't already been called.
|
||||
if (!mDivertedOnStartRequest) {
|
||||
mChannel->ForcePending(true);
|
||||
mDivertToListener->OnStartRequest(mChannel, nullptr);
|
||||
mChannel->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();
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#ifndef mozilla_net_FTPChannelParent_h
|
||||
#define mozilla_net_FTPChannelParent_h
|
||||
|
||||
#include "ADivertableParentChannel.h"
|
||||
#include "mozilla/net/PFTPChannelParent.h"
|
||||
#include "mozilla/net/NeckoParent.h"
|
||||
#include "nsIParentChannel.h"
|
||||
|
@ -22,6 +23,7 @@ namespace net {
|
|||
class FTPChannelParent : public PFTPChannelParent
|
||||
, public nsIParentChannel
|
||||
, public nsIInterfaceRequestor
|
||||
, public ADivertableParentChannel
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
@ -35,7 +37,26 @@ public:
|
|||
|
||||
bool Init(const FTPChannelCreationArgs& aOpenArgs);
|
||||
|
||||
// ADivertableParentChannel functions.
|
||||
void DivertTo(nsIStreamListener *aListener) MOZ_OVERRIDE;
|
||||
nsresult SuspendForDiversion() MOZ_OVERRIDE;
|
||||
|
||||
// Calls OnStartRequest for "DivertTo" listener, then notifies child channel
|
||||
// that it should divert OnDataAvailable and OnStopRequest calls to this
|
||||
// parent channel.
|
||||
void StartDiversion();
|
||||
|
||||
// Handles calling OnStart/Stop if there are errors during diversion.
|
||||
// Called asynchronously from FailDiversion.
|
||||
void NotifyDiversionFailed(nsresult aErrorCode, bool aSkipResume = true);
|
||||
|
||||
protected:
|
||||
// private, supporting function for ADivertableParentChannel.
|
||||
nsresult ResumeForDiversion();
|
||||
|
||||
// Asynchronously calls NotifyDiversionFailed.
|
||||
void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);
|
||||
|
||||
bool DoAsyncOpen(const URIParams& aURI, const uint64_t& aStartPos,
|
||||
const nsCString& aEntityID,
|
||||
const OptionalInputStreamParams& aUploadStream);
|
||||
|
@ -62,6 +83,22 @@ protected:
|
|||
nsCOMPtr<nsILoadContext> mLoadContext;
|
||||
|
||||
PBOverrideStatus mPBOverride;
|
||||
|
||||
// If OnStart/OnData/OnStop have been diverted from the child, forward them to
|
||||
// this listener.
|
||||
nsCOMPtr<nsIStreamListener> mDivertToListener;
|
||||
// Set to the canceled status value if the main channel was canceled.
|
||||
nsresult mStatus;
|
||||
// Once set, no OnStart/OnData/OnStop calls should be accepted; conversely, it
|
||||
// must be set when RecvDivertOnData/~DivertOnStop/~DivertComplete are
|
||||
// received from the child channel.
|
||||
bool mDivertingFromChild;
|
||||
// Set if OnStart|StopRequest was called during a diversion from the child.
|
||||
bool mDivertedOnStartRequest;
|
||||
|
||||
// Set if we successfully suspended the nsHttpChannel for diversion. Unset
|
||||
// when we call ResumeForDiversion.
|
||||
bool mSuspendedForDiversion;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
|
|
@ -38,7 +38,7 @@ nsFtpChannel::SetUploadStream(nsIInputStream *stream,
|
|||
const nsACString &contentType,
|
||||
int64_t contentLength)
|
||||
{
|
||||
NS_ENSURE_TRUE(!IsPending(), NS_ERROR_IN_PROGRESS);
|
||||
NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
|
||||
|
||||
mUploadStream = stream;
|
||||
|
||||
|
@ -61,7 +61,7 @@ nsFtpChannel::GetUploadStream(nsIInputStream **stream)
|
|||
NS_IMETHODIMP
|
||||
nsFtpChannel::ResumeAt(uint64_t aStartPos, const nsACString& aEntityID)
|
||||
{
|
||||
NS_ENSURE_TRUE(!IsPending(), NS_ERROR_IN_PROGRESS);
|
||||
NS_ENSURE_TRUE(!Pending(), NS_ERROR_IN_PROGRESS);
|
||||
mEntityID = aEntityID;
|
||||
mStartPos = aStartPos;
|
||||
mResumeRequested = (mStartPos || !mEntityID.IsEmpty());
|
||||
|
@ -196,3 +196,26 @@ nsFtpChannel::GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult)
|
|||
}
|
||||
aResult = mFTPEventSink;
|
||||
}
|
||||
|
||||
void
|
||||
nsFtpChannel::ForcePending(bool aForcePending)
|
||||
{
|
||||
// Set true here so IsPending will return true.
|
||||
// Required for callback diversion from child back to parent. In such cases
|
||||
// OnStopRequest can be called in the parent before callbacks are diverted
|
||||
// back from the child to the listener in the parent.
|
||||
mForcePending = aForcePending;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFtpChannel::IsPending(bool *result)
|
||||
{
|
||||
*result = Pending();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsFtpChannel::Pending() const
|
||||
{
|
||||
return nsBaseChannel::Pending() || mForcePending;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ public:
|
|||
, mStartPos(0)
|
||||
, mResumeRequested(false)
|
||||
, mLastModifiedTime(0)
|
||||
, mForcePending(false)
|
||||
{
|
||||
SetURI(uri);
|
||||
}
|
||||
|
@ -49,6 +50,12 @@ public:
|
|||
mProxyInfo = pi;
|
||||
}
|
||||
|
||||
NS_IMETHOD IsPending(bool *result) MOZ_OVERRIDE;
|
||||
|
||||
// This is a short-cut to calling nsIRequest::IsPending().
|
||||
// Overrides Pending in nsBaseChannel.
|
||||
bool Pending() const MOZ_OVERRIDE;
|
||||
|
||||
// Were we asked to resume a download?
|
||||
bool ResumeRequested() { return mResumeRequested; }
|
||||
|
||||
|
@ -81,6 +88,9 @@ public:
|
|||
// Helper function for getting the nsIFTPEventSink.
|
||||
void GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult);
|
||||
|
||||
public: /* Internal Necko use only. */
|
||||
void ForcePending(bool aForcePending);
|
||||
|
||||
protected:
|
||||
virtual ~nsFtpChannel() {}
|
||||
virtual nsresult OpenContentStream(bool async, nsIInputStream **result,
|
||||
|
@ -96,6 +106,7 @@ private:
|
|||
nsCString mEntityID;
|
||||
bool mResumeRequested;
|
||||
PRTime mLastModifiedTime;
|
||||
bool mForcePending;
|
||||
};
|
||||
|
||||
#endif /* nsFTPChannel_h___ */
|
||||
|
|
Загрузка…
Ссылка в новой задаче