mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-03 20:49:27 +00:00
Bug 975338 - Enable diverting of HTTP and FTP channels back to parent process from child r=jduell
This commit is contained in:
parent
6e87c814ed
commit
c0d484b966
@ -36,6 +36,7 @@ XPIDL_SOURCES += [
|
||||
'nsICryptoHMAC.idl',
|
||||
'nsIDashboard.idl',
|
||||
'nsIDashboardEventNotifier.idl',
|
||||
'nsIDivertableChannel.idl',
|
||||
'nsIDownloader.idl',
|
||||
'nsIEncodedChannel.idl',
|
||||
'nsIExternalProtocolHandler.idl',
|
||||
|
61
netwerk/base/public/nsIDivertableChannel.idl
Normal file
61
netwerk/base/public/nsIDivertableChannel.idl
Normal file
@ -0,0 +1,61 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 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 "nsISupports.idl"
|
||||
|
||||
%{C++
|
||||
//#include "mozilla/net/ChannelDiverterChild.h"
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
class ChannelDiverterChild;
|
||||
}
|
||||
}
|
||||
%}
|
||||
|
||||
[ptr] native ChannelDiverterChild(mozilla::net::ChannelDiverterChild);
|
||||
|
||||
interface nsIStreamListener;
|
||||
|
||||
/**
|
||||
* A channel implementing this interface allows diverting from an
|
||||
* nsIStreamListener in the child process to one in the parent.
|
||||
*/
|
||||
[uuid(4430e0d0-ff70-45f5-99dc-b5fd06943fc1)]
|
||||
interface nsIDivertableChannel : nsISupports
|
||||
{
|
||||
/**
|
||||
* CHILD ONLY.
|
||||
* Called by Necko client in child process during OnStartRequest to divert
|
||||
* nsIStreamListener and nsIRequest callbacks to the parent process.
|
||||
*
|
||||
* The process should look like the following:
|
||||
*
|
||||
* 1) divertToParent is called in the child process. It can only be called
|
||||
* during OnStartRequest().
|
||||
*
|
||||
* 2) The ChannelDiverterChild that is returned is an IPDL object. It should
|
||||
* be passed via some other IPDL method of the client's choosing to the
|
||||
* parent. On the parent the ChannelDiverterParent's divertTo() function
|
||||
* should be called with an nsIStreamListener that will then receive the
|
||||
* OnStartRequest/OnDataAvailable/OnStopRequest for the channel. The
|
||||
* ChannelDiverterParent can then be deleted (which will also destroy the
|
||||
* ChannelDiverterChild in the child).
|
||||
*
|
||||
* After divertToParent() has been called, NO further function calls
|
||||
* should be made on the channel. It is a dead object for all purposes.
|
||||
* The reference that the channel holds to the listener in the child is
|
||||
* released is once OnStartRequest completes, and no other
|
||||
* nsIStreamListener calls (OnDataAvailable, OnStopRequest) will be made
|
||||
* to it.
|
||||
*
|
||||
* @return ChannelDiverterChild IPDL actor to be passed to parent process by
|
||||
* client IPDL message, e.g. PClient.DivertUsing(PDiverterChild).
|
||||
*
|
||||
* @throws exception if the channel was canceled early. Throws status code of
|
||||
* canceled channel.
|
||||
*/
|
||||
ChannelDiverterChild divertToParent();
|
||||
};
|
38
netwerk/base/src/ADivertableParentChannel.h
Normal file
38
netwerk/base/src/ADivertableParentChannel.h
Normal file
@ -0,0 +1,38 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 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/. */
|
||||
|
||||
#ifndef _adivertablechannelparent_h_
|
||||
#define _adivertablechannelparent_h_
|
||||
|
||||
#include "nsISupports.h"
|
||||
|
||||
class nsIStreamListener;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// To be implemented by a channel's parent actors, e.g. HttpChannelParent
|
||||
// and FTPChannelParent. Used by ChannelDiverterParent to divert
|
||||
// nsIStreamListener callbacks from the child process to a new
|
||||
// listener in the parent process.
|
||||
class ADivertableParentChannel : public nsISupports
|
||||
{
|
||||
public:
|
||||
// Called by ChannelDiverterParent::DivertTo(nsIStreamListener*).
|
||||
// The listener should now be used to received nsIStreamListener callbacks,
|
||||
// i.e. OnStartRequest, OnDataAvailable and OnStopRequest, as if it had been
|
||||
// passed to AsyncOpen for the channel. A reference to the listener will be
|
||||
// added and kept until OnStopRequest has completed.
|
||||
virtual void DivertTo(nsIStreamListener *aListener) = 0;
|
||||
|
||||
// Called to suspend parent channel in ChannelDiverterParent constructor.
|
||||
virtual nsresult SuspendForDiversion() = 0;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
27
netwerk/base/src/ChannelDiverterChild.cpp
Normal file
27
netwerk/base/src/ChannelDiverterChild.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 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/ChannelDiverterChild.h"
|
||||
#include "mozilla/net/NeckoChannelParams.h"
|
||||
#include "mozilla/net/HttpChannelChild.h"
|
||||
#include "mozilla/net/FTPChannelChild.h"
|
||||
#include "mozilla/net/PHttpChannelChild.h"
|
||||
#include "mozilla/net/PFTPChannelChild.h"
|
||||
#include "nsIDivertableChannel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
ChannelDiverterChild::ChannelDiverterChild()
|
||||
{
|
||||
}
|
||||
|
||||
ChannelDiverterChild::~ChannelDiverterChild()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
30
netwerk/base/src/ChannelDiverterChild.h
Normal file
30
netwerk/base/src/ChannelDiverterChild.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 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/. */
|
||||
|
||||
#ifndef _channeldiverterchild_h_
|
||||
#define _channeldiverterchild_h_
|
||||
|
||||
#include "mozilla/net/PChannelDiverterChild.h"
|
||||
|
||||
class nsIDivertableChannel;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class ChannelDiverterArgs;
|
||||
|
||||
class ChannelDiverterChild :
|
||||
public PChannelDiverterChild
|
||||
{
|
||||
public:
|
||||
ChannelDiverterChild();
|
||||
virtual ~ChannelDiverterChild();
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* _channeldiverterchild_h_ */
|
65
netwerk/base/src/ChannelDiverterParent.cpp
Normal file
65
netwerk/base/src/ChannelDiverterParent.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 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/ChannelDiverterParent.h"
|
||||
#include "mozilla/net/NeckoChannelParams.h"
|
||||
#include "mozilla/net/HttpChannelParent.h"
|
||||
#include "mozilla/net/FTPChannelParent.h"
|
||||
#include "mozilla/net/PHttpChannelParent.h"
|
||||
#include "mozilla/net/PFTPChannelParent.h"
|
||||
#include "ADivertableParentChannel.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
ChannelDiverterParent::ChannelDiverterParent()
|
||||
{
|
||||
}
|
||||
|
||||
ChannelDiverterParent::~ChannelDiverterParent()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ChannelDiverterParent::Init(const ChannelDiverterArgs& aChannel)
|
||||
{
|
||||
switch (aChannel.type()) {
|
||||
case ChannelDiverterArgs::TPHttpChannelParent:
|
||||
{
|
||||
mDivertableChannelParent = static_cast<ADivertableParentChannel*>(
|
||||
static_cast<HttpChannelParent*>(aChannel.get_PHttpChannelParent()));
|
||||
break;
|
||||
}
|
||||
case ChannelDiverterArgs::TPFTPChannelParent:
|
||||
{
|
||||
mDivertableChannelParent = static_cast<ADivertableParentChannel*>(
|
||||
static_cast<FTPChannelParent*>(aChannel.get_PFTPChannelParent()));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_NOTREACHED("unknown ChannelDiverterArgs type");
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(mDivertableChannelParent);
|
||||
|
||||
nsresult rv = mDivertableChannelParent->SuspendForDiversion();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ChannelDiverterParent::DivertTo(nsIStreamListener* newListener)
|
||||
{
|
||||
MOZ_ASSERT(newListener);
|
||||
MOZ_ASSERT(mDivertableChannelParent);
|
||||
|
||||
mDivertableChannelParent->DivertTo(newListener);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
37
netwerk/base/src/ChannelDiverterParent.h
Normal file
37
netwerk/base/src/ChannelDiverterParent.h
Normal file
@ -0,0 +1,37 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 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/. */
|
||||
|
||||
#ifndef _channeldiverterparent_h_
|
||||
#define _channeldiverterparent_h_
|
||||
|
||||
#include "mozilla/net/PChannelDiverterParent.h"
|
||||
|
||||
class nsIStreamListener;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class ChannelDiverterArgs;
|
||||
class ADivertableParentChannel;
|
||||
|
||||
class ChannelDiverterParent :
|
||||
public PChannelDiverterParent
|
||||
{
|
||||
public:
|
||||
ChannelDiverterParent();
|
||||
virtual ~ChannelDiverterParent();
|
||||
|
||||
bool Init(const ChannelDiverterArgs& aChannel);
|
||||
|
||||
void DivertTo(nsIStreamListener* newListener);
|
||||
private:
|
||||
nsRefPtr<ADivertableParentChannel> mDivertableChannelParent;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* _channeldiverterparent_h_ */
|
@ -12,6 +12,8 @@ EXPORTS += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.net += [
|
||||
'ChannelDiverterChild.h',
|
||||
'ChannelDiverterParent.h',
|
||||
'Dashboard.h',
|
||||
'DashboardTypes.h',
|
||||
]
|
||||
@ -19,6 +21,8 @@ EXPORTS.mozilla.net += [
|
||||
UNIFIED_SOURCES += [
|
||||
'ArrayBufferInputStream.cpp',
|
||||
'BackgroundFileSaver.cpp',
|
||||
'ChannelDiverterChild.cpp',
|
||||
'ChannelDiverterParent.cpp',
|
||||
'Dashboard.cpp',
|
||||
'EventTokenBucket.cpp',
|
||||
'LoadContextInfo.cpp',
|
||||
@ -113,6 +117,7 @@ include('/ipc/chromium/chromium-config.mozbuild')
|
||||
FINAL_LIBRARY = 'necko'
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
'/netwerk/protocol/http'
|
||||
]
|
||||
|
||||
if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
|
||||
|
@ -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) {
|
||||
|
@ -5,6 +5,8 @@
|
||||
* 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 protocol PHttpChannel;
|
||||
include protocol PFTPChannel;
|
||||
include URIParams;
|
||||
include InputStreamParams;
|
||||
|
||||
@ -80,5 +82,11 @@ union FTPChannelCreationArgs
|
||||
FTPChannelConnectArgs; // Used for redirected-to channels
|
||||
};
|
||||
|
||||
union ChannelDiverterArgs
|
||||
{
|
||||
PHttpChannel;
|
||||
PFTPChannel;
|
||||
};
|
||||
|
||||
} // namespace ipc
|
||||
} // namespace mozilla
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "mozilla/net/WebSocketChannelChild.h"
|
||||
#include "mozilla/net/DNSRequestChild.h"
|
||||
#include "mozilla/net/RemoteOpenFileChild.h"
|
||||
#include "mozilla/net/ChannelDiverterChild.h"
|
||||
#include "mozilla/dom/network/TCPSocketChild.h"
|
||||
#include "mozilla/dom/network/TCPServerSocketChild.h"
|
||||
#include "mozilla/dom/network/UDPSocketChild.h"
|
||||
@ -266,5 +267,18 @@ NeckoChild::DeallocPRemoteOpenFileChild(PRemoteOpenFileChild* aChild)
|
||||
return true;
|
||||
}
|
||||
|
||||
PChannelDiverterChild*
|
||||
NeckoChild::AllocPChannelDiverterChild(const ChannelDiverterArgs& channel)
|
||||
{
|
||||
return new ChannelDiverterChild();;
|
||||
}
|
||||
|
||||
bool
|
||||
NeckoChild::DeallocPChannelDiverterChild(PChannelDiverterChild* child)
|
||||
{
|
||||
delete static_cast<ChannelDiverterChild*>(child);
|
||||
return true;
|
||||
}
|
||||
|
||||
}} // mozilla::net
|
||||
|
||||
|
@ -62,6 +62,10 @@ protected:
|
||||
virtual bool DeallocPRemoteOpenFileChild(PRemoteOpenFileChild*) MOZ_OVERRIDE;
|
||||
virtual PRtspControllerChild* AllocPRtspControllerChild() MOZ_OVERRIDE;
|
||||
virtual bool DeallocPRtspControllerChild(PRtspControllerChild*) MOZ_OVERRIDE;
|
||||
virtual PChannelDiverterChild*
|
||||
AllocPChannelDiverterChild(const ChannelDiverterArgs& channel) MOZ_OVERRIDE;
|
||||
virtual bool
|
||||
DeallocPChannelDiverterChild(PChannelDiverterChild* actor) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -18,6 +18,7 @@
|
||||
#endif
|
||||
#include "mozilla/net/DNSRequestParent.h"
|
||||
#include "mozilla/net/RemoteOpenFileParent.h"
|
||||
#include "mozilla/net/ChannelDiverterParent.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/dom/network/TCPSocketParent.h"
|
||||
@ -608,6 +609,28 @@ NeckoParent::RecvCancelHTMLDNSPrefetch(const nsString& hostname,
|
||||
return true;
|
||||
}
|
||||
|
||||
PChannelDiverterParent*
|
||||
NeckoParent::AllocPChannelDiverterParent(const ChannelDiverterArgs& channel)
|
||||
{
|
||||
return new ChannelDiverterParent();
|
||||
}
|
||||
|
||||
bool
|
||||
NeckoParent::RecvPChannelDiverterConstructor(PChannelDiverterParent* actor,
|
||||
const ChannelDiverterArgs& channel)
|
||||
{
|
||||
auto parent = static_cast<ChannelDiverterParent*>(actor);
|
||||
parent->Init(channel);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NeckoParent::DeallocPChannelDiverterParent(PChannelDiverterParent* parent)
|
||||
{
|
||||
delete static_cast<ChannelDiverterParent*>(parent);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
NeckoParent::CloneManagees(ProtocolBase* aSource,
|
||||
mozilla::ipc::ProtocolCloneContext* aCtx)
|
||||
|
@ -144,6 +144,14 @@ protected:
|
||||
virtual PRtspControllerParent* AllocPRtspControllerParent() MOZ_OVERRIDE;
|
||||
virtual bool DeallocPRtspControllerParent(PRtspControllerParent*) MOZ_OVERRIDE;
|
||||
|
||||
virtual PChannelDiverterParent*
|
||||
AllocPChannelDiverterParent(const ChannelDiverterArgs& channel) MOZ_OVERRIDE;
|
||||
virtual bool
|
||||
RecvPChannelDiverterConstructor(PChannelDiverterParent* actor,
|
||||
const ChannelDiverterArgs& channel) MOZ_OVERRIDE;
|
||||
virtual bool DeallocPChannelDiverterParent(PChannelDiverterParent* actor)
|
||||
MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsCString mCoreAppsBasePath;
|
||||
nsCString mWebAppsBasePath;
|
||||
|
25
netwerk/ipc/PChannelDiverter.ipdl
Normal file
25
netwerk/ipc/PChannelDiverter.ipdl
Normal file
@ -0,0 +1,25 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80 ft=cpp: */
|
||||
/* 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 protocol PFTPChannel;
|
||||
include protocol PHttpChannel;
|
||||
include protocol PNecko;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
// Used when diverting necko channels from child back to the parent.
|
||||
// See nsIDivertableChannel.
|
||||
async protocol PChannelDiverter
|
||||
{
|
||||
manager PNecko;
|
||||
|
||||
child:
|
||||
__delete__();
|
||||
};
|
||||
|
||||
}// namespace net
|
||||
}// namespace mozilla
|
@ -17,6 +17,7 @@ include protocol PTCPServerSocket;
|
||||
include protocol PUDPSocket;
|
||||
include protocol PRemoteOpenFile;
|
||||
include protocol PDNSRequest;
|
||||
include protocol PChannelDiverter;
|
||||
include protocol PBlob; //FIXME: bug #792908
|
||||
|
||||
include protocol PRtspController;
|
||||
@ -45,6 +46,7 @@ sync protocol PNecko
|
||||
manages PDNSRequest;
|
||||
manages PRemoteOpenFile;
|
||||
manages PRtspController;
|
||||
manages PChannelDiverter;
|
||||
|
||||
parent:
|
||||
__delete__();
|
||||
@ -68,6 +70,7 @@ parent:
|
||||
HTMLDNSPrefetch(nsString hostname, uint16_t flags);
|
||||
CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
|
||||
PRtspController();
|
||||
PChannelDiverter(ChannelDiverterArgs channel);
|
||||
|
||||
both:
|
||||
PTCPSocket();
|
||||
|
@ -31,6 +31,7 @@ UNIFIED_SOURCES += [
|
||||
|
||||
IPDL_SOURCES = [
|
||||
'NeckoChannelParams.ipdlh',
|
||||
'PChannelDiverter.ipdl',
|
||||
'PNecko.ipdl',
|
||||
'PRemoteOpenFile.ipdl',
|
||||
'PRtspController.ipdl',
|
||||
|
@ -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,6 +482,63 @@ 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()
|
||||
{
|
||||
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
|
||||
{
|
||||
public:
|
||||
@ -475,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();
|
||||
|
||||
@ -488,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();
|
||||
@ -552,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,
|
||||
@ -79,6 +84,8 @@ protected:
|
||||
const uint32_t& count) MOZ_OVERRIDE;
|
||||
bool RecvOnStopRequest(const nsresult& statusCode) MOZ_OVERRIDE;
|
||||
bool RecvFailedAsyncOpen(const nsresult& statusCode) MOZ_OVERRIDE;
|
||||
bool RecvFlushedForDiversion() MOZ_OVERRIDE;
|
||||
bool RecvDivertMessages() MOZ_OVERRIDE;
|
||||
bool RecvDeleteSelf() MOZ_OVERRIDE;
|
||||
|
||||
void DoOnStartRequest(const int64_t& aContentLength,
|
||||
@ -112,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);
|
||||
@ -179,6 +184,87 @@ FTPChannelParent::RecvResume()
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -188,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);
|
||||
@ -235,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;
|
||||
}
|
||||
@ -254,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))
|
||||
@ -296,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.
|
||||
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);
|
||||
@ -47,6 +68,11 @@ protected:
|
||||
virtual bool RecvCancel(const nsresult& status) MOZ_OVERRIDE;
|
||||
virtual bool RecvSuspend() MOZ_OVERRIDE;
|
||||
virtual bool RecvResume() MOZ_OVERRIDE;
|
||||
virtual bool RecvDivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count) MOZ_OVERRIDE;
|
||||
virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) MOZ_OVERRIDE;
|
||||
virtual bool RecvDivertComplete() MOZ_OVERRIDE;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||
|
||||
@ -57,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
|
||||
|
@ -32,12 +32,31 @@ parent:
|
||||
Suspend();
|
||||
Resume();
|
||||
|
||||
// Divert OnDataAvailable to the parent.
|
||||
DivertOnDataAvailable(nsCString data,
|
||||
uint64_t offset,
|
||||
uint32_t count);
|
||||
|
||||
// Divert OnStopRequest to the parent.
|
||||
DivertOnStopRequest(nsresult statusCode);
|
||||
|
||||
// Child has no more events/messages to divert to the parent.
|
||||
DivertComplete();
|
||||
|
||||
child:
|
||||
OnStartRequest(int64_t aContentLength, nsCString aContentType,
|
||||
PRTime aLastModified, nsCString aEntityID, URIParams aURI);
|
||||
OnDataAvailable(nsCString data, uint64_t offset, uint32_t count);
|
||||
OnStopRequest(nsresult statusCode);
|
||||
FailedAsyncOpen(nsresult statusCode);
|
||||
|
||||
// Parent has been suspended for diversion; no more events to be enqueued.
|
||||
FlushedForDiversion();
|
||||
|
||||
// Child should resume processing the ChannelEventQueue, i.e. diverting any
|
||||
// OnDataAvailable and OnStopRequest messages in the queue back to the parent.
|
||||
DivertMessages();
|
||||
|
||||
DeleteSelf();
|
||||
};
|
||||
|
||||
|
@ -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___ */
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ipc/InputStreamUtils.h"
|
||||
#include "mozilla/ipc/URIUtils.h"
|
||||
#include "mozilla/net/ChannelDiverterChild.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "SerializedLoadContext.h"
|
||||
|
||||
@ -41,6 +42,9 @@ HttpChannelChild::HttpChannelChild()
|
||||
, mSendResumeAt(false)
|
||||
, mIPCOpen(false)
|
||||
, mKeptAlive(false)
|
||||
, mDivertingToParent(false)
|
||||
, mFlushedForDiversion(false)
|
||||
, mSuspendSent(false)
|
||||
{
|
||||
LOG(("Creating HttpChannelChild @%x\n", this));
|
||||
|
||||
@ -102,6 +106,7 @@ NS_INTERFACE_MAP_BEGIN(HttpChannelChild)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIChildChannel)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIHttpChannelChild)
|
||||
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAssociatedContentSecurity, GetAssociatedContentSecurity())
|
||||
NS_INTERFACE_MAP_ENTRY(nsIDivertableChannel)
|
||||
NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -226,6 +231,13 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead,
|
||||
const NetAddr& selfAddr,
|
||||
const NetAddr& peerAddr)
|
||||
{
|
||||
// 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 StartRequestEvent(this, responseHead, useResponseHead,
|
||||
requestHeaders, isFromCache,
|
||||
@ -255,6 +267,13 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead,
|
||||
{
|
||||
LOG(("HttpChannelChild::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!");
|
||||
|
||||
if (useResponseHead && !mCanceled)
|
||||
mResponseHead = new nsHttpResponseHead(responseHead);
|
||||
|
||||
@ -286,6 +305,14 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead,
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDivertingToParent) {
|
||||
mListener = nullptr;
|
||||
mListenerContext = nullptr;
|
||||
if (mLoadGroup) {
|
||||
mLoadGroup->RemoveRequest(this, nullptr, mStatus);
|
||||
}
|
||||
}
|
||||
|
||||
if (mResponseHead)
|
||||
SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie));
|
||||
|
||||
@ -335,11 +362,17 @@ HttpChannelChild::RecvOnTransportAndData(const nsresult& status,
|
||||
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 TransportAndDataEvent(this, status, progress,
|
||||
progressMax, data, offset,
|
||||
count));
|
||||
} else {
|
||||
MOZ_RELEASE_ASSERT(!mDivertingToParent,
|
||||
"ShouldEnqueue when diverting to parent!");
|
||||
|
||||
OnTransportAndData(status, progress, progressMax, data, offset, count);
|
||||
}
|
||||
return true;
|
||||
@ -355,6 +388,15 @@ HttpChannelChild::OnTransportAndData(const nsresult& status,
|
||||
{
|
||||
LOG(("HttpChannelChild::OnTransportAndData [this=%p]\n", this));
|
||||
|
||||
// For diversion to parent, just SendDivertOnDataAvailable.
|
||||
if (mDivertingToParent) {
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be processing any more callbacks from parent!");
|
||||
|
||||
SendDivertOnDataAvailable(data, offset, count);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
@ -431,9 +473,14 @@ class StopRequestEvent : public ChannelEvent
|
||||
bool
|
||||
HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be receiving any more callbacks from parent!");
|
||||
|
||||
if (mEventQ->ShouldEnqueue()) {
|
||||
mEventQ->Enqueue(new StopRequestEvent(this, statusCode));
|
||||
} else {
|
||||
MOZ_ASSERT(!mDivertingToParent, "ShouldEnqueue when diverting to parent!");
|
||||
|
||||
OnStopRequest(statusCode);
|
||||
}
|
||||
return true;
|
||||
@ -445,6 +492,14 @@ HttpChannelChild::OnStopRequest(const nsresult& statusCode)
|
||||
LOG(("HttpChannelChild::OnStopRequest [this=%p status=%x]\n",
|
||||
this, statusCode));
|
||||
|
||||
if (mDivertingToParent) {
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
"Should not be processing any more callbacks from parent!");
|
||||
|
||||
SendDivertOnStopRequest(statusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
mIsPending = false;
|
||||
|
||||
if (!mCanceled && NS_SUCCEEDED(mStatus))
|
||||
@ -777,6 +832,60 @@ HttpChannelChild::RecvRedirect3Complete()
|
||||
return true;
|
||||
}
|
||||
|
||||
class HttpFlushedForDiversionEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
HttpFlushedForDiversionEvent(HttpChannelChild* aChild)
|
||||
: mChild(aChild)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(aChild);
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->FlushedForDiversion();
|
||||
}
|
||||
private:
|
||||
HttpChannelChild* mChild;
|
||||
};
|
||||
|
||||
bool
|
||||
HttpChannelChild::RecvFlushedForDiversion()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mDivertingToParent);
|
||||
MOZ_RELEASE_ASSERT(mEventQ->ShouldEnqueue());
|
||||
|
||||
mEventQ->Enqueue(new HttpFlushedForDiversionEvent(this));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::FlushedForDiversion()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mDivertingToParent);
|
||||
|
||||
// Once this is set, it should not be unset before HttpChannelChild 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
|
||||
HttpChannelChild::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.
|
||||
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(Resume()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::Redirect3Complete()
|
||||
{
|
||||
@ -943,8 +1052,13 @@ NS_IMETHODIMP
|
||||
HttpChannelChild::Suspend()
|
||||
{
|
||||
NS_ENSURE_TRUE(RemoteChannelExists(), 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();
|
||||
|
||||
@ -959,7 +1073,11 @@ HttpChannelChild::Resume()
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
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();
|
||||
if (mCallOnResume) {
|
||||
AsyncCall(mCallOnResume);
|
||||
@ -1441,6 +1559,37 @@ NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(RequestHeaderTuples *
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelChild::nsIDivertableChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::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) && !RemoteChannelExists()) {
|
||||
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;
|
||||
}
|
||||
|
||||
}} // mozilla::net
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "nsIAssociatedContentSecurity.h"
|
||||
#include "nsIChildChannel.h"
|
||||
#include "nsIHttpChannelChild.h"
|
||||
#include "nsIDivertableChannel.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -42,6 +43,7 @@ class HttpChannelChild : public PHttpChannelChild
|
||||
, public nsIAssociatedContentSecurity
|
||||
, public nsIChildChannel
|
||||
, public nsIHttpChannelChild
|
||||
, public nsIDivertableChannel
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
@ -53,6 +55,7 @@ public:
|
||||
NS_DECL_NSIASSOCIATEDCONTENTSECURITY
|
||||
NS_DECL_NSICHILDCHANNEL
|
||||
NS_DECL_NSIHTTPCHANNELCHILD
|
||||
NS_DECL_NSIDIVERTABLECHANNEL
|
||||
|
||||
HttpChannelChild();
|
||||
virtual ~HttpChannelChild();
|
||||
@ -90,6 +93,8 @@ public:
|
||||
|
||||
bool IsSuspended();
|
||||
|
||||
void FlushedForDiversion();
|
||||
|
||||
protected:
|
||||
bool RecvOnStartRequest(const nsHttpResponseHead& responseHead,
|
||||
const bool& useResponseHead,
|
||||
@ -118,6 +123,8 @@ protected:
|
||||
bool RecvRedirect3Complete() MOZ_OVERRIDE;
|
||||
bool RecvAssociateApplicationCache(const nsCString& groupID,
|
||||
const nsCString& clientID) MOZ_OVERRIDE;
|
||||
bool RecvFlushedForDiversion() MOZ_OVERRIDE;
|
||||
bool RecvDivertMessages() MOZ_OVERRIDE;
|
||||
bool RecvDeleteSelf() MOZ_OVERRIDE;
|
||||
|
||||
bool GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nullptr);
|
||||
@ -140,6 +147,15 @@ private:
|
||||
bool mKeptAlive; // IPC kept open, but only for security info
|
||||
nsRefPtr<ChannelEventQueue> mEventQ;
|
||||
|
||||
// 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;
|
||||
|
||||
// true after successful AsyncOpen until OnStopRequest completes.
|
||||
bool RemoteChannelExists() { return mIPCOpen && !mKeptAlive; }
|
||||
|
||||
|
@ -43,6 +43,10 @@ HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding,
|
||||
, mReceivedRedirect2Verify(false)
|
||||
, mPBOverride(aOverrideStatus)
|
||||
, mLoadContext(aLoadContext)
|
||||
, mStatus(NS_OK)
|
||||
, mDivertingFromChild(false)
|
||||
, mDivertedOnStartRequest(false)
|
||||
, mSuspendedForDiversion(false)
|
||||
{
|
||||
// Ensure gHttpHandler is initialized: we need the atom table up and running.
|
||||
nsCOMPtr<nsIHttpProtocolHandler> dummyInitializer =
|
||||
@ -179,57 +183,57 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
|
||||
if (NS_FAILED(rv))
|
||||
return SendFailedAsyncOpen(rv);
|
||||
|
||||
rv = NS_NewChannel(getter_AddRefs(mChannel), uri, ios, nullptr, nullptr, loadFlags);
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel), uri, ios, nullptr, nullptr, loadFlags);
|
||||
if (NS_FAILED(rv))
|
||||
return SendFailedAsyncOpen(rv);
|
||||
|
||||
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
mChannel = static_cast<nsHttpChannel *>(channel.get());
|
||||
if (mPBOverride != kPBOverride_Unset) {
|
||||
httpChan->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
|
||||
mChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
|
||||
}
|
||||
|
||||
if (doResumeAt)
|
||||
httpChan->ResumeAt(startPos, entityID);
|
||||
mChannel->ResumeAt(startPos, entityID);
|
||||
|
||||
if (originalUri)
|
||||
httpChan->SetOriginalURI(originalUri);
|
||||
mChannel->SetOriginalURI(originalUri);
|
||||
if (docUri)
|
||||
httpChan->SetDocumentURI(docUri);
|
||||
mChannel->SetDocumentURI(docUri);
|
||||
if (referrerUri)
|
||||
httpChan->SetReferrerInternal(referrerUri);
|
||||
mChannel->SetReferrerInternal(referrerUri);
|
||||
if (apiRedirectToUri)
|
||||
httpChan->RedirectTo(apiRedirectToUri);
|
||||
mChannel->RedirectTo(apiRedirectToUri);
|
||||
if (loadFlags != nsIRequest::LOAD_NORMAL)
|
||||
httpChan->SetLoadFlags(loadFlags);
|
||||
mChannel->SetLoadFlags(loadFlags);
|
||||
|
||||
for (uint32_t i = 0; i < requestHeaders.Length(); i++) {
|
||||
httpChan->SetRequestHeader(requestHeaders[i].mHeader,
|
||||
mChannel->SetRequestHeader(requestHeaders[i].mHeader,
|
||||
requestHeaders[i].mValue,
|
||||
requestHeaders[i].mMerge);
|
||||
}
|
||||
|
||||
nsRefPtr<HttpChannelParentListener> channelListener =
|
||||
new HttpChannelParentListener(this);
|
||||
mParentListener = new HttpChannelParentListener(this);
|
||||
|
||||
httpChan->SetNotificationCallbacks(channelListener);
|
||||
mChannel->SetNotificationCallbacks(mParentListener);
|
||||
|
||||
httpChan->SetRequestMethod(nsDependentCString(requestMethod.get()));
|
||||
mChannel->SetRequestMethod(nsDependentCString(requestMethod.get()));
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream);
|
||||
if (stream) {
|
||||
httpChan->InternalSetUploadStream(stream);
|
||||
httpChan->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
|
||||
mChannel->InternalSetUploadStream(stream);
|
||||
mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
|
||||
}
|
||||
|
||||
if (priority != nsISupportsPriority::PRIORITY_NORMAL)
|
||||
httpChan->SetPriority(priority);
|
||||
httpChan->SetRedirectionLimit(redirectionLimit);
|
||||
httpChan->SetAllowPipelining(allowPipelining);
|
||||
httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
|
||||
httpChan->SetAllowSpdy(allowSpdy);
|
||||
mChannel->SetPriority(priority);
|
||||
mChannel->SetRedirectionLimit(redirectionLimit);
|
||||
mChannel->SetAllowPipelining(allowPipelining);
|
||||
mChannel->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie);
|
||||
mChannel->SetAllowSpdy(allowSpdy);
|
||||
|
||||
nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
|
||||
do_QueryInterface(mChannel);
|
||||
do_QueryObject(mChannel);
|
||||
nsCOMPtr<nsIApplicationCacheService> appCacheService =
|
||||
do_GetService(NS_APPLICATIONCACHESERVICE_CONTRACTID);
|
||||
|
||||
@ -273,7 +277,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
|
||||
}
|
||||
}
|
||||
|
||||
rv = httpChan->AsyncOpen(channelListener, nullptr);
|
||||
rv = mChannel->AsyncOpen(mParentListener, nullptr);
|
||||
if (NS_FAILED(rv))
|
||||
return SendFailedAsyncOpen(rv);
|
||||
|
||||
@ -286,12 +290,14 @@ HttpChannelParent::ConnectChannel(const uint32_t& channelId)
|
||||
nsresult rv;
|
||||
|
||||
LOG(("Looking for a registered channel [this=%p, id=%d]", this, channelId));
|
||||
rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(mChannel));
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel));
|
||||
mChannel = static_cast<nsHttpChannel*>(channel.get());
|
||||
LOG((" found channel %p, rv=%08x", mChannel.get(), rv));
|
||||
|
||||
if (mPBOverride != kPBOverride_Unset) {
|
||||
// redirected-to channel may not support PB
|
||||
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(mChannel);
|
||||
nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryObject(mChannel);
|
||||
if (pbChannel) {
|
||||
pbChannel->SetPrivate(mPBOverride == kPBOverride_Private ? true : false);
|
||||
}
|
||||
@ -304,8 +310,7 @@ bool
|
||||
HttpChannelParent::RecvSetPriority(const uint16_t& priority)
|
||||
{
|
||||
if (mChannel) {
|
||||
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
httpChan->SetPriority(priority);
|
||||
mChannel->SetPriority(priority);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsPriority> priorityRedirectChannel =
|
||||
@ -339,8 +344,7 @@ HttpChannelParent::RecvCancel(const nsresult& status)
|
||||
{
|
||||
// May receive cancel before channel has been constructed!
|
||||
if (mChannel) {
|
||||
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
httpChan->Cancel(status);
|
||||
mChannel->Cancel(status);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -435,6 +439,92 @@ HttpChannelParent::RecvMarkOfflineCacheEntryAsForeign()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HttpChannelParent::RecvDivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
MOZ_ASSERT(mParentListener);
|
||||
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 = mParentListener->OnDataAvailable(mChannel, nullptr, stringStream,
|
||||
offset, count);
|
||||
stringStream->Close();
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(rv);
|
||||
}
|
||||
mStatus = rv;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HttpChannelParent::RecvDivertOnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
MOZ_ASSERT(mParentListener);
|
||||
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);
|
||||
}
|
||||
|
||||
mParentListener->OnStopRequest(mChannel, nullptr, status);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HttpChannelParent::RecvDivertComplete()
|
||||
{
|
||||
MOZ_ASSERT(mParentListener);
|
||||
mParentListener = nullptr;
|
||||
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;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParent::nsIRequestObserver
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -444,6 +534,9 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
||||
{
|
||||
LOG(("HttpChannelParent::OnStartRequest [this=%p]\n", this));
|
||||
|
||||
MOZ_RELEASE_ASSERT(!mDivertingFromChild,
|
||||
"Cannot call OnStartRequest if diverting is set!");
|
||||
|
||||
nsHttpChannel *chan = static_cast<nsHttpChannel *>(aRequest);
|
||||
nsHttpResponseHead *responseHead = chan->GetResponseHead();
|
||||
nsHttpRequestHead *requestHead = chan->GetRequestHead();
|
||||
@ -492,7 +585,6 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
||||
NS_SerializeToString(secInfoSer, secInfoSerialization);
|
||||
}
|
||||
|
||||
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
if (mIPCClosed ||
|
||||
!SendOnStartRequest(responseHead ? *responseHead : nsHttpResponseHead(),
|
||||
!!responseHead,
|
||||
@ -500,7 +592,7 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
||||
isFromCache,
|
||||
mCacheEntry ? true : false,
|
||||
expirationTime, cachedCharset, secInfoSerialization,
|
||||
httpChan->GetSelfAddr(), httpChan->GetPeerAddr()))
|
||||
mChannel->GetSelfAddr(), mChannel->GetPeerAddr()))
|
||||
{
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
@ -515,6 +607,9 @@ HttpChannelParent::OnStopRequest(nsIRequest *aRequest,
|
||||
LOG(("HttpChannelParent::OnStopRequest: [this=%p status=%x]\n",
|
||||
this, aStatusCode));
|
||||
|
||||
MOZ_RELEASE_ASSERT(!mDivertingFromChild,
|
||||
"Cannot call OnStopRequest if diverting is set!");
|
||||
|
||||
if (mIPCClosed || !SendOnStopRequest(aStatusCode))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
return NS_OK;
|
||||
@ -533,6 +628,9 @@ HttpChannelParent::OnDataAvailable(nsIRequest *aRequest,
|
||||
{
|
||||
LOG(("HttpChannelParent::OnDataAvailable [this=%p]\n", this));
|
||||
|
||||
MOZ_RELEASE_ASSERT(!mDivertingFromChild,
|
||||
"Cannot call OnDataAvailable if diverting is set!");
|
||||
|
||||
nsCString data;
|
||||
nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
|
||||
if (NS_FAILED(rv))
|
||||
@ -629,8 +727,7 @@ HttpChannelParent::StartRedirect(uint32_t newChannelId,
|
||||
URIParams uriParams;
|
||||
SerializeURI(newURI, uriParams);
|
||||
|
||||
nsHttpChannel *httpChan = static_cast<nsHttpChannel *>(mChannel.get());
|
||||
nsHttpResponseHead *responseHead = httpChan->GetResponseHead();
|
||||
nsHttpResponseHead *responseHead = mChannel->GetResponseHead();
|
||||
bool result = SendRedirect1Begin(newChannelId, uriParams, redirectFlags,
|
||||
responseHead ? *responseHead
|
||||
: nsHttpResponseHead());
|
||||
@ -662,4 +759,196 @@ HttpChannelParent::CompleteRedirect(bool succeeded)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// HttpChannelParent::ADivertableParentChannel
|
||||
//-----------------------------------------------------------------------------
|
||||
nsresult
|
||||
HttpChannelParent::SuspendForDiversion()
|
||||
{
|
||||
MOZ_ASSERT(mChannel);
|
||||
MOZ_ASSERT(mParentListener);
|
||||
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 = mChannel->Suspend();
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv) || rv == NS_ERROR_NOT_AVAILABLE);
|
||||
mSuspendedForDiversion = NS_SUCCEEDED(rv);
|
||||
|
||||
rv = mParentListener->SuspendForDiversion();
|
||||
MOZ_ASSERT(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
|
||||
HttpChannelParent::ResumeForDiversion()
|
||||
{
|
||||
MOZ_ASSERT(mChannel);
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot ResumeForDiversion if not diverting!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
if (mSuspendedForDiversion) {
|
||||
// The nsHttpChannel will deliver remaining OnData/OnStop for the transfer.
|
||||
nsresult rv = mChannel->Resume();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED, true);
|
||||
return rv;
|
||||
}
|
||||
mSuspendedForDiversion = false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(mIPCClosed || !SendDeleteSelf())) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelParent::DivertTo(nsIStreamListener *aListener)
|
||||
{
|
||||
MOZ_ASSERT(mParentListener);
|
||||
if (NS_WARN_IF(!mDivertingFromChild)) {
|
||||
MOZ_ASSERT(mDivertingFromChild,
|
||||
"Cannot DivertTo new listener if diverting is not set!");
|
||||
return;
|
||||
}
|
||||
|
||||
DebugOnly<nsresult> rv = mParentListener->DivertTo(aListener);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
if (NS_WARN_IF(mIPCClosed || !SendFlushedForDiversion())) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call OnStartRequest and SendDivertMessages asynchronously to avoid
|
||||
// reentering client context.
|
||||
NS_DispatchToCurrentThread(
|
||||
NS_NewRunnableMethod(this, &HttpChannelParent::StartDiversion));
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelParent::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 = mParentListener->OnStartRequest(mChannel, nullptr);
|
||||
if (NS_FAILED(rv)) {
|
||||
if (mChannel) {
|
||||
mChannel->Cancel(rv);
|
||||
}
|
||||
mStatus = rv;
|
||||
}
|
||||
mDivertedOnStartRequest = true;
|
||||
|
||||
// After OnStartRequest has been called, tell HttpChannelChild to divert the
|
||||
// OnDataAvailables and OnStopRequest to this HttpChannelParent.
|
||||
if (NS_WARN_IF(mIPCClosed || !SendDivertMessages())) {
|
||||
FailDiversion(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
class HTTPFailDiversionEvent : public nsRunnable
|
||||
{
|
||||
public:
|
||||
HTTPFailDiversionEvent(HttpChannelParent *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<HttpChannelParent> mChannelParent;
|
||||
nsresult mErrorCode;
|
||||
bool mSkipResume;
|
||||
};
|
||||
|
||||
void
|
||||
HttpChannelParent::FailDiversion(nsresult aErrorCode,
|
||||
bool aSkipResume)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
|
||||
MOZ_RELEASE_ASSERT(mDivertingFromChild);
|
||||
MOZ_RELEASE_ASSERT(mParentListener);
|
||||
MOZ_RELEASE_ASSERT(mChannel);
|
||||
|
||||
NS_DispatchToCurrentThread(
|
||||
new HTTPFailDiversionEvent(this, aErrorCode, aSkipResume));
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelParent::NotifyDiversionFailed(nsresult aErrorCode,
|
||||
bool aSkipResume)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(NS_FAILED(aErrorCode));
|
||||
MOZ_RELEASE_ASSERT(mDivertingFromChild);
|
||||
MOZ_RELEASE_ASSERT(mParentListener);
|
||||
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 if 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);
|
||||
mParentListener->OnStartRequest(mChannel, nullptr);
|
||||
mChannel->ForcePending(false);
|
||||
}
|
||||
// If the channel is pending, it will call OnStopRequest itself; otherwise, do
|
||||
// it here.
|
||||
if (!isPending) {
|
||||
mParentListener->OnStopRequest(mChannel, nullptr, aErrorCode);
|
||||
}
|
||||
mParentListener = nullptr;
|
||||
mChannel = nullptr;
|
||||
|
||||
if (!mIPCClosed) {
|
||||
unused << SendDeleteSelf();
|
||||
}
|
||||
}
|
||||
|
||||
}} // mozilla::net
|
||||
|
@ -8,6 +8,7 @@
|
||||
#ifndef mozilla_net_HttpChannelParent_h
|
||||
#define mozilla_net_HttpChannelParent_h
|
||||
|
||||
#include "ADivertableParentChannel.h"
|
||||
#include "nsHttp.h"
|
||||
#include "mozilla/dom/PBrowserParent.h"
|
||||
#include "mozilla/net/PHttpChannelParent.h"
|
||||
@ -34,6 +35,7 @@ class HttpChannelParent : public PHttpChannelParent
|
||||
, public nsIParentRedirectingChannel
|
||||
, public nsIProgressEventSink
|
||||
, public nsIInterfaceRequestor
|
||||
, public ADivertableParentChannel
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -51,6 +53,19 @@ public:
|
||||
|
||||
bool Init(const HttpChannelCreationArgs& 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:
|
||||
// used to connect redirected-to channel in parent with just created
|
||||
// ChildChannel. Used during redirects.
|
||||
@ -89,15 +104,24 @@ protected:
|
||||
const int32_t& no) MOZ_OVERRIDE;
|
||||
virtual bool RecvDocumentChannelCleanup() MOZ_OVERRIDE;
|
||||
virtual bool RecvMarkOfflineCacheEntryAsForeign() MOZ_OVERRIDE;
|
||||
|
||||
virtual bool RecvDivertOnDataAvailable(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count) MOZ_OVERRIDE;
|
||||
virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) MOZ_OVERRIDE;
|
||||
virtual bool RecvDivertComplete() MOZ_OVERRIDE;
|
||||
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
// Supporting function for ADivertableParentChannel.
|
||||
nsresult ResumeForDiversion();
|
||||
|
||||
// Asynchronously calls NotifyDiversionFailed.
|
||||
void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);
|
||||
|
||||
friend class HttpChannelParentListener;
|
||||
nsRefPtr<mozilla::dom::TabParent> mTabParent;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsRefPtr<nsHttpChannel> mChannel;
|
||||
nsCOMPtr<nsICacheEntry> mCacheEntry;
|
||||
nsCOMPtr<nsIAssociatedContentSecurity> mAssociatedContentSecurity;
|
||||
bool mIPCClosed; // PHttpChannel actor has been Closed()
|
||||
@ -121,6 +145,19 @@ private:
|
||||
|
||||
nsCOMPtr<nsILoadContext> mLoadContext;
|
||||
nsRefPtr<nsHttpHandler> mHttpHandler;
|
||||
|
||||
nsRefPtr<HttpChannelParentListener> mParentListener;
|
||||
// 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;
|
||||
|
||||
bool mSuspendedForDiversion;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
@ -19,8 +19,9 @@ namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitialChannel)
|
||||
: mActiveChannel(aInitialChannel)
|
||||
: mNextListener(aInitialChannel)
|
||||
, mRedirectChannelId(0)
|
||||
, mSuspendedForDiversion(false)
|
||||
{
|
||||
}
|
||||
|
||||
@ -46,11 +47,14 @@ NS_IMPL_ISUPPORTS5(HttpChannelParentListener,
|
||||
NS_IMETHODIMP
|
||||
HttpChannelParentListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
|
||||
{
|
||||
if (!mActiveChannel)
|
||||
MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
|
||||
"Cannot call OnStartRequest if suspended for diversion!");
|
||||
|
||||
if (!mNextListener)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
LOG(("HttpChannelParentListener::OnStartRequest [this=%p]\n", this));
|
||||
return mActiveChannel->OnStartRequest(aRequest, aContext);
|
||||
return mNextListener->OnStartRequest(aRequest, aContext);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -58,14 +62,17 @@ HttpChannelParentListener::OnStopRequest(nsIRequest *aRequest,
|
||||
nsISupports *aContext,
|
||||
nsresult aStatusCode)
|
||||
{
|
||||
if (!mActiveChannel)
|
||||
MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
|
||||
"Cannot call OnStopRequest if suspended for diversion!");
|
||||
|
||||
if (!mNextListener)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
LOG(("HttpChannelParentListener::OnStopRequest: [this=%p status=%ul]\n",
|
||||
this, aStatusCode));
|
||||
nsresult rv = mActiveChannel->OnStopRequest(aRequest, aContext, aStatusCode);
|
||||
nsresult rv = mNextListener->OnStopRequest(aRequest, aContext, aStatusCode);
|
||||
|
||||
mActiveChannel = nullptr;
|
||||
mNextListener = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
@ -80,11 +87,14 @@ HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest,
|
||||
uint64_t aOffset,
|
||||
uint32_t aCount)
|
||||
{
|
||||
if (!mActiveChannel)
|
||||
MOZ_RELEASE_ASSERT(!mSuspendedForDiversion,
|
||||
"Cannot call OnDataAvailable if suspended for diversion!");
|
||||
|
||||
if (!mNextListener)
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
LOG(("HttpChannelParentListener::OnDataAvailable [this=%p]\n", this));
|
||||
return mActiveChannel->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
|
||||
return mNextListener->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -102,8 +112,8 @@ HttpChannelParentListener::GetInterface(const nsIID& aIID, void **result)
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> ir;
|
||||
if (mActiveChannel &&
|
||||
NS_SUCCEEDED(CallQueryInterface(mActiveChannel.get(),
|
||||
if (mNextListener &&
|
||||
NS_SUCCEEDED(CallQueryInterface(mNextListener.get(),
|
||||
getter_AddRefs(ir))))
|
||||
{
|
||||
return ir->GetInterface(aIID, result);
|
||||
@ -136,7 +146,7 @@ HttpChannelParentListener::AsyncOnChannelRedirect(
|
||||
LOG(("Registered %p channel under id=%d", newChannel, mRedirectChannelId));
|
||||
|
||||
nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
|
||||
do_QueryInterface(mActiveChannel);
|
||||
do_QueryInterface(mNextListener);
|
||||
if (!activeRedirectingChannel) {
|
||||
NS_RUNTIMEABORT("Channel got a redirect response, but doesn't implement "
|
||||
"nsIParentRedirectingChannel to handle it.");
|
||||
@ -190,7 +200,7 @@ HttpChannelParentListener::OnRedirectResult(bool succeeded)
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIParentRedirectingChannel> activeRedirectingChannel =
|
||||
do_QueryInterface(mActiveChannel);
|
||||
do_QueryInterface(mNextListener);
|
||||
MOZ_ASSERT(activeRedirectingChannel,
|
||||
"Channel finished a redirect response, but doesn't implement "
|
||||
"nsIParentRedirectingChannel to complete it.");
|
||||
@ -203,8 +213,12 @@ HttpChannelParentListener::OnRedirectResult(bool succeeded)
|
||||
|
||||
if (succeeded) {
|
||||
// Switch to redirect channel and delete the old one.
|
||||
mActiveChannel->Delete();
|
||||
mActiveChannel = redirectChannel;
|
||||
nsCOMPtr<nsIParentChannel> parent;
|
||||
parent = do_QueryInterface(mNextListener);
|
||||
MOZ_ASSERT(parent);
|
||||
parent->Delete();
|
||||
mNextListener = do_QueryInterface(redirectChannel);
|
||||
MOZ_ASSERT(mNextListener);
|
||||
} else if (redirectChannel) {
|
||||
// Delete the redirect target channel: continue using old channel
|
||||
redirectChannel->Delete();
|
||||
@ -213,4 +227,43 @@ HttpChannelParentListener::OnRedirectResult(bool succeeded)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
nsresult
|
||||
HttpChannelParentListener::SuspendForDiversion()
|
||||
{
|
||||
if (NS_WARN_IF(mSuspendedForDiversion)) {
|
||||
MOZ_ASSERT(!mSuspendedForDiversion, "Cannot SuspendForDiversion twice!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// While this is set, no OnStart/OnData/OnStop callbacks should be forwarded
|
||||
// to mNextListener.
|
||||
mSuspendedForDiversion = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HttpChannelParentListener::ResumeForDiversion()
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!");
|
||||
|
||||
// Allow OnStart/OnData/OnStop callbacks to be forwarded to mNextListener.
|
||||
mSuspendedForDiversion = false;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HttpChannelParentListener::DivertTo(nsIStreamListener* aListener)
|
||||
{
|
||||
MOZ_ASSERT(aListener);
|
||||
MOZ_RELEASE_ASSERT(mSuspendedForDiversion, "Must already be suspended!");
|
||||
|
||||
mNextListener = aListener;
|
||||
|
||||
return ResumeForDiversion();
|
||||
}
|
||||
|
||||
}} // mozilla::net
|
||||
|
@ -35,9 +35,22 @@ public:
|
||||
HttpChannelParentListener(HttpChannelParent* aInitialChannel);
|
||||
virtual ~HttpChannelParentListener();
|
||||
|
||||
// For channel diversion from child to parent.
|
||||
nsresult DivertTo(nsIStreamListener *aListener);
|
||||
nsresult SuspendForDiversion();
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIParentChannel> mActiveChannel;
|
||||
// Private partner function to SuspendForDiversion.
|
||||
nsresult ResumeForDiversion();
|
||||
|
||||
// Can be the original HttpChannelParent that created this object (normal
|
||||
// case), a different {HTTP|FTP}ChannelParent that we've been redirected to,
|
||||
// or some other listener that we have been diverted to via
|
||||
// nsIDivertableChannel.
|
||||
nsCOMPtr<nsIStreamListener> mNextListener;
|
||||
uint32_t mRedirectChannelId;
|
||||
// When set, no OnStart/OnData/OnStop calls should be received.
|
||||
bool mSuspendedForDiversion;
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
|
@ -66,6 +66,17 @@ parent:
|
||||
// again, indefinitely.
|
||||
MarkOfflineCacheEntryAsForeign();
|
||||
|
||||
// Divert OnDataAvailable to the parent.
|
||||
DivertOnDataAvailable(nsCString data,
|
||||
uint64_t offset,
|
||||
uint32_t count);
|
||||
|
||||
// Divert OnStopRequest to the parent.
|
||||
DivertOnStopRequest(nsresult statusCode);
|
||||
|
||||
// Child has no more events/messages to divert to the parent.
|
||||
DivertComplete();
|
||||
|
||||
__delete__();
|
||||
|
||||
child:
|
||||
@ -113,6 +124,13 @@ child:
|
||||
AssociateApplicationCache(nsCString groupID,
|
||||
nsCString clientID);
|
||||
|
||||
// Parent has been suspended for diversion; no more events to be enqueued.
|
||||
FlushedForDiversion();
|
||||
|
||||
// Child should resume processing the ChannelEventQueue, i.e. diverting any
|
||||
// OnDataAvailable and OnStopRequest messages in the queue back to the parent.
|
||||
DivertMessages();
|
||||
|
||||
// Tell child to delete channel (all IPDL deletes must be done from child to
|
||||
// avoid races: see bug 591708).
|
||||
DeleteSelf();
|
||||
|
@ -220,6 +220,7 @@ nsHttpChannel::nsHttpChannel()
|
||||
, mIsPartialRequest(0)
|
||||
, mHasAutoRedirectVetoNotifier(0)
|
||||
, mDidReval(false)
|
||||
, mForcePending(false)
|
||||
{
|
||||
LOG(("Creating nsHttpChannel [this=%p]\n", this));
|
||||
mChannelCreationTime = PR_Now();
|
||||
@ -6200,4 +6201,22 @@ nsHttpChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
|
||||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
nsHttpChannel::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
|
||||
nsHttpChannel::IsPending(bool *aIsPending)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aIsPending);
|
||||
*aIsPending = mIsPending || mForcePending;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} } // namespace mozilla::net
|
||||
|
@ -105,6 +105,7 @@ public:
|
||||
NS_IMETHOD Cancel(nsresult status);
|
||||
NS_IMETHOD Suspend();
|
||||
NS_IMETHOD Resume();
|
||||
NS_IMETHOD IsPending(bool *aIsPending);
|
||||
// nsIChannel
|
||||
NS_IMETHOD GetSecurityInfo(nsISupports **aSecurityInfo);
|
||||
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext);
|
||||
@ -182,6 +183,8 @@ public: /* internal necko use only */
|
||||
uint32_t mKeep : 2;
|
||||
};
|
||||
|
||||
void ForcePending(bool aForcePending);
|
||||
|
||||
private:
|
||||
typedef nsresult (nsHttpChannel::*nsContinueRedirectionFunc)(nsresult result);
|
||||
|
||||
@ -424,6 +427,7 @@ private: // cache telemetry
|
||||
private:
|
||||
nsIPrincipal *GetPrincipal();
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
bool mForcePending;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::net
|
||||
|
Loading…
x
Reference in New Issue
Block a user