Bug 975338 - Implement nsI|ADivertableChannel in FTPChannelChild|Parent r=jduell

This commit is contained in:
Steve Workman 2014-03-10 18:31:57 +01:00
Родитель 54ba73d1af
Коммит b040432bc0
9 изменённых файлов: 517 добавлений и 19 удалений

Просмотреть файл

@ -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,9 +157,9 @@ public:
}
// This is a short-cut to calling nsIRequest::IsPending()
bool IsPending() const {
virtual bool Pending() const {
return mPump || mWaitingOnAsyncRedirect;
}
}
// Helper function for querying the channel's notification callbacks.
template <class T> void GetCallback(nsCOMPtr<T> &result) {

Просмотреть файл

@ -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,16 +482,61 @@ 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()
{
return false;
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,19 +189,80 @@ FTPChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
{
return false;
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)
{
return false;
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()
{
return false;
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;
}
//-----------------------------------------------------------------------------
@ -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;
}
@ -274,7 +352,14 @@ FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
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))
@ -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___ */