mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-12 02:31:41 +00:00
Bug 1197679 - If nsUnknownDecoder is involved in e10s DivertToParent can break. r=jduell
This commit is contained in:
parent
17a52110f3
commit
48d945f4e6
@ -22,7 +22,7 @@ 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)]
|
||||
[uuid(7a9bf52d-f828-4b31-b8df-b40fdd37d007)]
|
||||
interface nsIDivertableChannel : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -57,4 +57,22 @@ interface nsIDivertableChannel : nsISupports
|
||||
* canceled channel.
|
||||
*/
|
||||
ChannelDiverterChild divertToParent();
|
||||
|
||||
/**
|
||||
* nsUnknownDecoder delays calling OnStartRequest until it gets enough data
|
||||
* to decide about the content type (until OnDataAvaiable is called). In a
|
||||
* OnStartRequest DivertToParent can be called but some OnDataAvailables are
|
||||
* already called and therefore can not be diverted to parent.
|
||||
*
|
||||
* nsUnknownDecoder will call UnknownDecoderInvolvedKeepData in its
|
||||
* OnStartRequest function and when it calls OnStartRequest of the next
|
||||
* listener it will call UnknownDecoderInvolvedOnStartRequestCalled. In this
|
||||
* function Child process will decide to discarge data if it is not diverting
|
||||
* to parent or keep them if it is diverting to parent.
|
||||
*/
|
||||
void unknownDecoderInvolvedKeepData();
|
||||
|
||||
void unknownDecoderInvolvedOnStartRequestCalled();
|
||||
|
||||
readonly attribute bool divertingToParent;
|
||||
};
|
||||
|
@ -51,6 +51,7 @@ class ChannelEventQueue final
|
||||
// Puts IPDL-generated channel event into queue, to be run later
|
||||
// automatically when EndForcedQueueing and/or Resume is called.
|
||||
inline void Enqueue(ChannelEvent* callback);
|
||||
inline nsresult PrependEvents(nsTArray<nsAutoPtr<ChannelEvent> >& aEvents);
|
||||
|
||||
// After StartForcedQueueing is called, ShouldEnqueue() will return true and
|
||||
// no events will be run/flushed until EndForcedQueueing is called.
|
||||
@ -127,6 +128,19 @@ ChannelEventQueue::EndForcedQueueing()
|
||||
MaybeFlushQueue();
|
||||
}
|
||||
|
||||
inline nsresult
|
||||
ChannelEventQueue::PrependEvents(nsTArray<nsAutoPtr<ChannelEvent> >& aEvents)
|
||||
{
|
||||
if (!mEventQueue.InsertElementsAt(0, aEvents.Length())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aEvents.Length(); i++) {
|
||||
mEventQueue.ReplaceElementAt(i, aEvents[i].forget());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline void
|
||||
ChannelEventQueue::Suspend()
|
||||
{
|
||||
|
@ -29,6 +29,7 @@ namespace net {
|
||||
|
||||
FTPChannelChild::FTPChannelChild(nsIURI* uri)
|
||||
: mIPCOpen(false)
|
||||
, mUnknownDecoderInvolved(false)
|
||||
, mCanceled(false)
|
||||
, mSuspendCount(0)
|
||||
, mIsPending(false)
|
||||
@ -390,6 +391,40 @@ FTPChannelChild::RecvOnDataAvailable(const nsresult& channelStatus,
|
||||
return true;
|
||||
}
|
||||
|
||||
class MaybeDivertOnDataFTPEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
MaybeDivertOnDataFTPEvent(FTPChannelChild* child,
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
: mChild(child)
|
||||
, mData(data)
|
||||
, mOffset(offset)
|
||||
, mCount(count) {}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->MaybeDivertOnData(mData, mOffset, mCount);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
nsCString mData;
|
||||
uint64_t mOffset;
|
||||
uint32_t mCount;
|
||||
};
|
||||
|
||||
void
|
||||
FTPChannelChild::MaybeDivertOnData(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
if (mDivertingToParent) {
|
||||
SendDivertOnDataAvailable(data, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::DoOnDataAvailable(const nsresult& channelStatus,
|
||||
const nsCString& data,
|
||||
@ -413,6 +448,11 @@ FTPChannelChild::DoOnDataAvailable(const nsresult& channelStatus,
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
if (mUnknownDecoderInvolved) {
|
||||
mUnknownDecoderEventQ.AppendElement(
|
||||
new MaybeDivertOnDataFTPEvent(this, data, offset, count));
|
||||
}
|
||||
|
||||
// NOTE: the OnDataAvailable contract requires the client to read all the data
|
||||
// in the inputstream. This code relies on that ('data' will go away after
|
||||
// this function). Apparently the previous, non-e10s behavior was to actually
|
||||
@ -472,6 +512,32 @@ FTPChannelChild::RecvOnStopRequest(const nsresult& aChannelStatus)
|
||||
return true;
|
||||
}
|
||||
|
||||
class MaybeDivertOnStopFTPEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
MaybeDivertOnStopFTPEvent(FTPChannelChild* child,
|
||||
const nsresult& aChannelStatus)
|
||||
: mChild(child)
|
||||
, mChannelStatus(aChannelStatus) {}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->MaybeDivertOnStop(mChannelStatus);
|
||||
}
|
||||
|
||||
private:
|
||||
FTPChannelChild* mChild;
|
||||
nsresult mChannelStatus;
|
||||
};
|
||||
|
||||
void
|
||||
FTPChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus)
|
||||
{
|
||||
if (mDivertingToParent) {
|
||||
SendDivertOnStopRequest(aChannelStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus)
|
||||
{
|
||||
@ -489,6 +555,11 @@ FTPChannelChild::DoOnStopRequest(const nsresult& aChannelStatus)
|
||||
if (!mCanceled)
|
||||
mStatus = aChannelStatus;
|
||||
|
||||
if (mUnknownDecoderInvolved) {
|
||||
mUnknownDecoderEventQ.AppendElement(
|
||||
new MaybeDivertOnStopFTPEvent(this, aChannelStatus));
|
||||
}
|
||||
|
||||
{ // Ensure that all queued ipdl events are dispatched before
|
||||
// we initiate protocol deletion below.
|
||||
mIsPending = false;
|
||||
@ -788,6 +859,36 @@ FTPChannelChild::DivertToParent(ChannelDiverterChild **aChild)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::UnknownDecoderInvolvedKeepData()
|
||||
{
|
||||
mUnknownDecoderInvolved = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::UnknownDecoderInvolvedOnStartRequestCalled()
|
||||
{
|
||||
mUnknownDecoderInvolved = false;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (mDivertingToParent) {
|
||||
rv = mEventQ->PrependEvents(mUnknownDecoderEventQ);
|
||||
}
|
||||
mUnknownDecoderEventQ.Clear();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FTPChannelChild::GetDivertingToParent(bool* aDiverting)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDiverting);
|
||||
*aDiverting = mDivertingToParent;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -101,13 +101,19 @@ protected:
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void MaybeDivertOnData(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void MaybeDivertOnStop(const nsresult& statusCode);
|
||||
void DoOnStopRequest(const nsresult& statusCode);
|
||||
void DoFailedAsyncOpen(const nsresult& statusCode);
|
||||
void DoDeleteSelf();
|
||||
|
||||
friend class FTPStartRequestEvent;
|
||||
friend class FTPDataAvailableEvent;
|
||||
friend class MaybeDivertOnDataFTPEvent;
|
||||
friend class FTPStopRequestEvent;
|
||||
friend class MaybeDivertOnStopFTPEvent;
|
||||
friend class FTPFailedAsyncOpenEvent;
|
||||
friend class FTPDeleteSelfEvent;
|
||||
|
||||
@ -116,6 +122,13 @@ private:
|
||||
|
||||
bool mIPCOpen;
|
||||
nsRefPtr<ChannelEventQueue> mEventQ;
|
||||
|
||||
// If nsUnknownDecoder is involved we queue onDataAvailable (and possibly
|
||||
// OnStopRequest) so that we can divert them if needed when the listener's
|
||||
// OnStartRequest is finally called
|
||||
nsTArray<nsAutoPtr<ChannelEvent>> mUnknownDecoderEventQ;
|
||||
bool mUnknownDecoderInvolved;
|
||||
|
||||
bool mCanceled;
|
||||
uint32_t mSuspendCount;
|
||||
bool mIsPending;
|
||||
|
@ -179,6 +179,7 @@ HttpChannelChild::HttpChannelChild()
|
||||
, mSendResumeAt(false)
|
||||
, mIPCOpen(false)
|
||||
, mKeptAlive(false)
|
||||
, mUnknownDecoderInvolved(false)
|
||||
, mDivertingToParent(false)
|
||||
, mFlushedForDiversion(false)
|
||||
, mSuspendSent(false)
|
||||
@ -580,6 +581,42 @@ HttpChannelChild::RecvOnTransportAndData(const nsresult& channelStatus,
|
||||
return true;
|
||||
}
|
||||
|
||||
class MaybeDivertOnDataHttpEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
MaybeDivertOnDataHttpEvent(HttpChannelChild* child,
|
||||
const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
: mChild(child)
|
||||
, mData(data)
|
||||
, mOffset(offset)
|
||||
, mCount(count) {}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->MaybeDivertOnData(mData, mOffset, mCount);
|
||||
}
|
||||
|
||||
private:
|
||||
HttpChannelChild* mChild;
|
||||
nsCString mData;
|
||||
uint64_t mOffset;
|
||||
uint32_t mCount;
|
||||
};
|
||||
|
||||
void
|
||||
HttpChannelChild::MaybeDivertOnData(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count)
|
||||
{
|
||||
LOG(("HttpChannelChild::MaybeDivertOnData [this=%p]", this));
|
||||
|
||||
if (mDivertingToParent) {
|
||||
SendDivertOnDataAvailable(data, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::OnTransportAndData(const nsresult& channelStatus,
|
||||
const nsresult& transportStatus,
|
||||
@ -607,6 +644,13 @@ HttpChannelChild::OnTransportAndData(const nsresult& channelStatus,
|
||||
if (mCanceled)
|
||||
return;
|
||||
|
||||
if (mUnknownDecoderInvolved) {
|
||||
LOG(("UnknownDecoder is involved queue OnDataAvailable call. [this=%p]",
|
||||
this));
|
||||
mUnknownDecoderEventQ.AppendElement(
|
||||
new MaybeDivertOnDataHttpEvent(this, data, offset, count));
|
||||
}
|
||||
|
||||
// Hold queue lock throughout all three calls, else we might process a later
|
||||
// necko msg in between them.
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
@ -742,12 +786,42 @@ HttpChannelChild::RecvOnStopRequest(const nsresult& channelStatus,
|
||||
return true;
|
||||
}
|
||||
|
||||
class MaybeDivertOnStopHttpEvent : public ChannelEvent
|
||||
{
|
||||
public:
|
||||
MaybeDivertOnStopHttpEvent(HttpChannelChild* child,
|
||||
const nsresult& channelStatus)
|
||||
: mChild(child)
|
||||
, mChannelStatus(channelStatus)
|
||||
{}
|
||||
|
||||
void Run()
|
||||
{
|
||||
mChild->MaybeDivertOnStop(mChannelStatus);
|
||||
}
|
||||
|
||||
private:
|
||||
HttpChannelChild* mChild;
|
||||
nsresult mChannelStatus;
|
||||
};
|
||||
|
||||
void
|
||||
HttpChannelChild::MaybeDivertOnStop(const nsresult& aChannelStatus)
|
||||
{
|
||||
LOG(("HttpChannelChild::MaybeDivertOnStop [this=%p, "
|
||||
"mDivertingToParent=%d status=%x]", this, mDivertingToParent,
|
||||
aChannelStatus));
|
||||
if (mDivertingToParent) {
|
||||
SendDivertOnStopRequest(aChannelStatus);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HttpChannelChild::OnStopRequest(const nsresult& channelStatus,
|
||||
const ResourceTimingStruct& timing)
|
||||
{
|
||||
LOG(("HttpChannelChild::OnStopRequest [this=%p status=%x]\n",
|
||||
this, channelStatus));
|
||||
this, channelStatus));
|
||||
|
||||
if (mDivertingToParent) {
|
||||
MOZ_RELEASE_ASSERT(!mFlushedForDiversion,
|
||||
@ -757,6 +831,13 @@ HttpChannelChild::OnStopRequest(const nsresult& channelStatus,
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUnknownDecoderInvolved) {
|
||||
LOG(("UnknownDecoder is involved queue OnStopRequest call. [this=%p]",
|
||||
this));
|
||||
mUnknownDecoderEventQ.AppendElement(
|
||||
new MaybeDivertOnStopHttpEvent(this, channelStatus));
|
||||
}
|
||||
|
||||
mTransactionTimings.domainLookupStart = timing.domainLookupStart;
|
||||
mTransactionTimings.domainLookupEnd = timing.domainLookupEnd;
|
||||
mTransactionTimings.connectStart = timing.connectStart;
|
||||
@ -2239,6 +2320,41 @@ HttpChannelChild::DivertToParent(ChannelDiverterChild **aChild)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::UnknownDecoderInvolvedKeepData()
|
||||
{
|
||||
LOG(("HttpChannelChild::UnknownDecoderInvolvedKeepData [this=%p]",
|
||||
this));
|
||||
mUnknownDecoderInvolved = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::UnknownDecoderInvolvedOnStartRequestCalled()
|
||||
{
|
||||
LOG(("HttpChannelChild::UnknownDecoderInvolvedOnStartRequestCalled "
|
||||
"[this=%p, mDivertingToParent=%d]", this, mDivertingToParent));
|
||||
mUnknownDecoderInvolved = false;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (mDivertingToParent) {
|
||||
rv = mEventQ->PrependEvents(mUnknownDecoderEventQ);
|
||||
}
|
||||
mUnknownDecoderEventQ.Clear();
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HttpChannelChild::GetDivertingToParent(bool* aDiverting)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aDiverting);
|
||||
*aDiverting = mDivertingToParent;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
HttpChannelChild::ResetInterception()
|
||||
{
|
||||
|
@ -195,6 +195,12 @@ private:
|
||||
bool mKeptAlive; // IPC kept open, but only for security info
|
||||
nsRefPtr<ChannelEventQueue> mEventQ;
|
||||
|
||||
// If nsUnknownDecoder is involved OnStartRequest call will be delayed and
|
||||
// this queue keeps OnDataAvailable data until OnStartRequest is finally
|
||||
// called.
|
||||
nsTArray<nsAutoPtr<ChannelEvent>> mUnknownDecoderEventQ;
|
||||
bool mUnknownDecoderInvolved;
|
||||
|
||||
// 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
|
||||
@ -229,6 +235,9 @@ private:
|
||||
const NetAddr& selfAddr,
|
||||
const NetAddr& peerAddr,
|
||||
const uint32_t& cacheKey);
|
||||
void MaybeDivertOnData(const nsCString& data,
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void OnTransportAndData(const nsresult& channelStatus,
|
||||
const nsresult& status,
|
||||
const uint64_t progress,
|
||||
@ -237,6 +246,7 @@ private:
|
||||
const uint64_t& offset,
|
||||
const uint32_t& count);
|
||||
void OnStopRequest(const nsresult& channelStatus, const ResourceTimingStruct& timing);
|
||||
void MaybeDivertOnStop(const nsresult& aChannelStatus);
|
||||
void OnProgress(const int64_t& progress, const int64_t& progressMax);
|
||||
void OnStatus(const nsresult& status);
|
||||
void FailedAsyncOpen(const nsresult& status);
|
||||
@ -253,6 +263,8 @@ private:
|
||||
friend class StartRequestEvent;
|
||||
friend class StopRequestEvent;
|
||||
friend class TransportAndDataEvent;
|
||||
friend class MaybeDivertOnDataHttpEvent;
|
||||
friend class MaybeDivertOnStopHttpEvent;
|
||||
friend class ProgressEvent;
|
||||
friend class StatusEvent;
|
||||
friend class FailedAsyncOpenEvent;
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "nsIMIMEService.h"
|
||||
|
||||
#include "nsIDivertableChannel.h"
|
||||
#include "nsIViewSourceChannel.h"
|
||||
#include "nsIHttpChannel.h"
|
||||
#include "nsIForcePendingChannel.h"
|
||||
@ -213,6 +214,15 @@ nsUnknownDecoder::OnDataAvailable(nsIRequest* request,
|
||||
NS_ASSERTION(!mContentType.IsEmpty(),
|
||||
"Content type should be known by now.");
|
||||
|
||||
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
|
||||
if (divertable) {
|
||||
bool diverting;
|
||||
divertable->GetDivertingToParent(&diverting);
|
||||
if (diverting) {
|
||||
// The channel is diverted to the parent do not send any more data here.
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
rv = mNextListener->OnDataAvailable(request, aCtxt, aStream,
|
||||
aSourceOffset, aCount);
|
||||
}
|
||||
@ -242,6 +252,11 @@ nsUnknownDecoder::OnStartRequest(nsIRequest* request, nsISupports *aCtxt)
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
|
||||
if (divertable) {
|
||||
divertable->UnknownDecoderInvolvedKeepData();
|
||||
}
|
||||
|
||||
// Do not pass the OnStartRequest on to the next listener (yet)...
|
||||
return rv;
|
||||
}
|
||||
@ -647,6 +662,12 @@ nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
|
||||
// mNextListener looks at it.
|
||||
request->Cancel(rv);
|
||||
mNextListener->OnStartRequest(request, aCtxt);
|
||||
|
||||
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
|
||||
if (divertable) {
|
||||
rv = divertable->UnknownDecoderInvolvedOnStartRequestCalled();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
@ -654,6 +675,17 @@ nsresult nsUnknownDecoder::FireListenerNotifications(nsIRequest* request,
|
||||
// Fire the OnStartRequest(...)
|
||||
rv = mNextListener->OnStartRequest(request, aCtxt);
|
||||
|
||||
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
|
||||
if (divertable) {
|
||||
rv = divertable->UnknownDecoderInvolvedOnStartRequestCalled();
|
||||
bool diverting;
|
||||
divertable->GetDivertingToParent(&diverting);
|
||||
if (diverting) {
|
||||
// The channel is diverted to the parent do not send any more data here.
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
// install stream converter if required
|
||||
nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(request);
|
||||
|
Loading…
x
Reference in New Issue
Block a user