mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 660774 - e10s necko: refactor channelEventQueue to allow async resume/flush. r=jdm
This commit is contained in:
parent
c7f6b1cb89
commit
aeeb58e404
80
netwerk/ipc/ChannelEventQueue.cpp
Normal file
80
netwerk/ipc/ChannelEventQueue.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set sw=2 ts=8 et tw=80 :
|
||||
*/
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at:
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* The Mozilla Foundation
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Josh Matthews <josh@joshmatthews.net> (initial developer)
|
||||
* Jason Duell <jduell.mcbugs@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIChannel.h"
|
||||
#include "mozilla/net/ChannelEventQueue.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
void
|
||||
ChannelEventQueue::FlushQueue()
|
||||
{
|
||||
// Events flushed could include destruction of channel (and our own
|
||||
// destructor) unless we make sure its refcount doesn't drop to 0 while this
|
||||
// method is running.
|
||||
nsCOMPtr<nsIChannel> kungFuDeathGrip(mOwner);
|
||||
|
||||
// Prevent flushed events from flushing the queue recursively
|
||||
mFlushing = true;
|
||||
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mEventQueue.Length(); i++) {
|
||||
mEventQueue[i]->Run();
|
||||
if (mSuspended)
|
||||
break;
|
||||
}
|
||||
|
||||
// We will always want to remove at least one finished callback.
|
||||
if (i < mEventQueue.Length())
|
||||
i++;
|
||||
|
||||
// It is possible for new callbacks to be enqueued as we are
|
||||
// flushing the queue, so the queue must not be cleared until
|
||||
// all callbacks have run.
|
||||
mEventQueue.RemoveElementsAt(0, i);
|
||||
|
||||
mFlushing = false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: sw=2 ts=8 et :
|
||||
* vim: set sw=2 ts=8 et tw=80 :
|
||||
*/
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
@ -23,6 +23,7 @@
|
||||
*
|
||||
* Contributor(s):
|
||||
* Josh Matthews <josh@joshmatthews.net> (initial developer)
|
||||
* Jason Duell <jduell.mcbugs@gmail.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
@ -41,6 +42,11 @@
|
||||
#ifndef mozilla_net_ChannelEventQueue_h
|
||||
#define mozilla_net_ChannelEventQueue_h
|
||||
|
||||
#include <nsTArray.h>
|
||||
#include <nsAutoPtr.h>
|
||||
|
||||
class nsIChannel;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
@ -56,125 +62,137 @@ class ChannelEvent
|
||||
// queue if still dispatching previous one(s) to listeners/observers.
|
||||
// Otherwise synchronous XMLHttpRequests and/or other code that spins the
|
||||
// event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for
|
||||
// instance) to be called before mListener->OnStartRequest has completed.
|
||||
// instance) to be dispatched and called before mListener->OnStartRequest has
|
||||
// completed.
|
||||
|
||||
template<class T> class AutoEventEnqueuerBase;
|
||||
class AutoEventEnqueuerBase;
|
||||
|
||||
template<class T>
|
||||
class ChannelEventQueue
|
||||
{
|
||||
public:
|
||||
ChannelEventQueue(T* self) : mQueuePhase(PHASE_UNQUEUED)
|
||||
, mSelf(self) {}
|
||||
ChannelEventQueue(nsIChannel *owner)
|
||||
: mForced(false)
|
||||
, mSuspended(false)
|
||||
, mFlushing(false)
|
||||
, mOwner(owner) {}
|
||||
|
||||
~ChannelEventQueue() {}
|
||||
|
||||
protected:
|
||||
void BeginEventQueueing();
|
||||
void EndEventQueueing();
|
||||
void EnqueueEvent(ChannelEvent* callback);
|
||||
bool ShouldEnqueue();
|
||||
void FlushEventQueue();
|
||||
|
||||
nsTArray<nsAutoPtr<ChannelEvent> > mEventQueue;
|
||||
enum {
|
||||
PHASE_UNQUEUED,
|
||||
PHASE_QUEUEING,
|
||||
PHASE_FINISHED_QUEUEING,
|
||||
PHASE_FLUSHING
|
||||
} mQueuePhase;
|
||||
// Checks to determine if an IPDL-generated channel event can be processed
|
||||
// immediately, or needs to be queued using Enqueue().
|
||||
inline bool ShouldEnqueue();
|
||||
|
||||
typedef AutoEventEnqueuerBase<T> AutoEventEnqueuer;
|
||||
// Puts IPDL-generated channel event into queue, to be run later
|
||||
// automatically when EndForcedQueueing and/or Resume is called.
|
||||
inline void Enqueue(ChannelEvent* callback);
|
||||
|
||||
// After StartForcedQueueing is called, ShouldEnqueue() will return true and
|
||||
// no events will be run/flushed until EndForcedQueueing is called.
|
||||
// - Note: queueing may still be required after EndForcedQueueing() (if the
|
||||
// queue is suspended, etc): always call ShouldEnqueue() to determine
|
||||
// whether queueing is needed.
|
||||
inline void StartForcedQueueing();
|
||||
inline void EndForcedQueueing();
|
||||
|
||||
// Suspend/resume event queue. ShouldEnqueue() will return true and no events
|
||||
// will be run/flushed until resume is called. These should be called when
|
||||
// the channel owning the event queue is suspended/resumed.
|
||||
// - Note: these suspend/resume functions are NOT meant to be called
|
||||
// recursively: call them only at initial suspend, and actual resume).
|
||||
// - Note: Resume flushes the queue and invokes any pending callbacks
|
||||
// immediately--caller must arrange any needed asynchronicity vis a vis
|
||||
// the channel's own Resume() method.
|
||||
inline void Suspend();
|
||||
inline void Resume();
|
||||
|
||||
private:
|
||||
T* mSelf;
|
||||
inline void MaybeFlushQueue();
|
||||
void FlushQueue();
|
||||
|
||||
friend class AutoEventEnqueuerBase<T>;
|
||||
nsTArray<nsAutoPtr<ChannelEvent> > mEventQueue;
|
||||
|
||||
bool mForced;
|
||||
bool mSuspended;
|
||||
bool mFlushing;
|
||||
|
||||
// Keep ptr to avoid refcount cycle: only grab ref during flushing.
|
||||
nsIChannel *mOwner;
|
||||
|
||||
friend class AutoEventEnqueuer;
|
||||
};
|
||||
|
||||
template<class T> inline void
|
||||
ChannelEventQueue<T>::BeginEventQueueing()
|
||||
inline bool
|
||||
ChannelEventQueue::ShouldEnqueue()
|
||||
{
|
||||
if (mQueuePhase != PHASE_UNQUEUED)
|
||||
return;
|
||||
// Store incoming IPDL messages for later.
|
||||
mQueuePhase = PHASE_QUEUEING;
|
||||
bool answer = mForced || mSuspended || mFlushing;
|
||||
|
||||
NS_ABORT_IF_FALSE(answer == true || mEventQueue.IsEmpty(),
|
||||
"Should always enqueue if ChannelEventQueue not empty");
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
template<class T> inline void
|
||||
ChannelEventQueue<T>::EndEventQueueing()
|
||||
{
|
||||
if (mQueuePhase != PHASE_QUEUEING)
|
||||
return;
|
||||
|
||||
mQueuePhase = PHASE_FINISHED_QUEUEING;
|
||||
}
|
||||
|
||||
template<class T> inline bool
|
||||
ChannelEventQueue<T>::ShouldEnqueue()
|
||||
{
|
||||
return mQueuePhase != PHASE_UNQUEUED || mSelf->IsSuspended();
|
||||
}
|
||||
|
||||
template<class T> inline void
|
||||
ChannelEventQueue<T>::EnqueueEvent(ChannelEvent* callback)
|
||||
inline void
|
||||
ChannelEventQueue::Enqueue(ChannelEvent* callback)
|
||||
{
|
||||
mEventQueue.AppendElement(callback);
|
||||
}
|
||||
|
||||
template<class T> void
|
||||
ChannelEventQueue<T>::FlushEventQueue()
|
||||
inline void
|
||||
ChannelEventQueue::StartForcedQueueing()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mQueuePhase != PHASE_UNQUEUED,
|
||||
"Queue flushing should not occur if PHASE_UNQUEUED");
|
||||
|
||||
// Queue already being flushed
|
||||
if (mQueuePhase != PHASE_FINISHED_QUEUEING || mSelf->IsSuspended())
|
||||
return;
|
||||
|
||||
nsRefPtr<T> kungFuDeathGrip(mSelf);
|
||||
if (mEventQueue.Length() > 0) {
|
||||
// It is possible for new callbacks to be enqueued as we are
|
||||
// flushing the queue, so the queue must not be cleared until
|
||||
// all callbacks have run.
|
||||
mQueuePhase = PHASE_FLUSHING;
|
||||
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mEventQueue.Length(); i++) {
|
||||
mEventQueue[i]->Run();
|
||||
if (mSelf->IsSuspended())
|
||||
break;
|
||||
}
|
||||
|
||||
// We will always want to remove at least one finished callback.
|
||||
if (i < mEventQueue.Length())
|
||||
i++;
|
||||
|
||||
mEventQueue.RemoveElementsAt(0, i);
|
||||
}
|
||||
|
||||
if (mSelf->IsSuspended())
|
||||
mQueuePhase = PHASE_QUEUEING;
|
||||
else
|
||||
mQueuePhase = PHASE_UNQUEUED;
|
||||
mForced = true;
|
||||
}
|
||||
|
||||
// Ensures any incoming IPDL msgs are queued during its lifetime, and flushes
|
||||
// the queue when it goes out of scope.
|
||||
template<class T>
|
||||
class AutoEventEnqueuerBase
|
||||
inline void
|
||||
ChannelEventQueue::EndForcedQueueing()
|
||||
{
|
||||
mForced = false;
|
||||
MaybeFlushQueue();
|
||||
}
|
||||
|
||||
inline void
|
||||
ChannelEventQueue::Suspend()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(!mSuspended,
|
||||
"ChannelEventQueue::Suspend called recursively");
|
||||
|
||||
mSuspended = true;
|
||||
}
|
||||
|
||||
inline void
|
||||
ChannelEventQueue::Resume()
|
||||
{
|
||||
NS_ABORT_IF_FALSE(mSuspended,
|
||||
"ChannelEventQueue::Resume called when not suspended!");
|
||||
|
||||
mSuspended = false;
|
||||
MaybeFlushQueue();
|
||||
}
|
||||
|
||||
inline void
|
||||
ChannelEventQueue::MaybeFlushQueue()
|
||||
{
|
||||
// Don't flush if forced queuing on, we're already being flushed, or
|
||||
// suspended, or there's nothing to flush
|
||||
if (!mForced && !mFlushing && !mSuspended && !mEventQueue.IsEmpty())
|
||||
FlushQueue();
|
||||
}
|
||||
|
||||
// Ensures that ShouldEnqueue() will be true during its lifetime (letting
|
||||
// caller know incoming IPDL msgs should be queued). Flushes the queue when it
|
||||
// goes out of scope.
|
||||
class AutoEventEnqueuer
|
||||
{
|
||||
public:
|
||||
AutoEventEnqueuerBase(ChannelEventQueue<T>* queue) : mEventQueue(queue)
|
||||
{
|
||||
mEventQueue->BeginEventQueueing();
|
||||
AutoEventEnqueuer(ChannelEventQueue &queue) : mEventQueue(queue) {
|
||||
mEventQueue.StartForcedQueueing();
|
||||
}
|
||||
~AutoEventEnqueuerBase()
|
||||
{
|
||||
mEventQueue->EndEventQueueing();
|
||||
mEventQueue->FlushEventQueue();
|
||||
~AutoEventEnqueuer() {
|
||||
mEventQueue.EndForcedQueueing();
|
||||
}
|
||||
private:
|
||||
ChannelEventQueue<T> *mEventQueue;
|
||||
ChannelEventQueue &mEventQueue;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ EXPORTS_mozilla/net = \
|
||||
CPPSRCS = \
|
||||
NeckoChild.cpp \
|
||||
NeckoParent.cpp \
|
||||
ChannelEventQueue.cpp \
|
||||
$(NULL)
|
||||
|
||||
LOCAL_INCLUDES += \
|
||||
|
@ -56,8 +56,8 @@ namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
FTPChannelChild::FTPChannelChild(nsIURI* uri)
|
||||
: ChannelEventQueue<FTPChannelChild>(this)
|
||||
, mIPCOpen(false)
|
||||
: mIPCOpen(false)
|
||||
, mEventQ(this)
|
||||
, mCanceled(false)
|
||||
, mSuspendCount(0)
|
||||
, mIsPending(PR_FALSE)
|
||||
@ -251,9 +251,9 @@ FTPChannelChild::RecvOnStartRequest(const PRInt32& aContentLength,
|
||||
const nsCString& aEntityID,
|
||||
const IPC::URI& aURI)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new FTPStartRequestEvent(this, aContentLength, aContentType,
|
||||
aLastModified, aEntityID, aURI));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new FTPStartRequestEvent(this, aContentLength, aContentType,
|
||||
aLastModified, aEntityID, aURI));
|
||||
} else {
|
||||
DoOnStartRequest(aContentLength, aContentType, aLastModified,
|
||||
aEntityID, aURI);
|
||||
@ -280,7 +280,7 @@ FTPChannelChild::DoOnStartRequest(const PRInt32& aContentLength,
|
||||
uri->GetSpec(spec);
|
||||
nsBaseChannel::URI()->SetSpec(spec);
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
nsresult rv = mListener->OnStartRequest(this, mListenerContext);
|
||||
if (NS_FAILED(rv))
|
||||
Cancel(rv);
|
||||
@ -304,8 +304,8 @@ FTPChannelChild::RecvOnDataAvailable(const nsCString& data,
|
||||
const PRUint32& offset,
|
||||
const PRUint32& count)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new FTPDataAvailableEvent(this, data, offset, count));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new FTPDataAvailableEvent(this, data, offset, count));
|
||||
} else {
|
||||
DoOnDataAvailable(data, offset, count);
|
||||
}
|
||||
@ -337,7 +337,7 @@ FTPChannelChild::DoOnDataAvailable(const nsCString& data,
|
||||
return;
|
||||
}
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
rv = mListener->OnDataAvailable(this, mListenerContext,
|
||||
stringStream, offset, count);
|
||||
if (NS_FAILED(rv))
|
||||
@ -359,8 +359,8 @@ class FTPStopRequestEvent : public ChannelEvent
|
||||
bool
|
||||
FTPChannelChild::RecvOnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new FTPStopRequestEvent(this, statusCode));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new FTPStopRequestEvent(this, statusCode));
|
||||
} else {
|
||||
DoOnStopRequest(statusCode);
|
||||
}
|
||||
@ -379,7 +379,7 @@ FTPChannelChild::DoOnStopRequest(const nsresult& statusCode)
|
||||
{ // Ensure that all queued ipdl events are dispatched before
|
||||
// we initiate protocol deletion below.
|
||||
mIsPending = PR_FALSE;
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
(void)mListener->OnStopRequest(this, mListenerContext, statusCode);
|
||||
mListener = nsnull;
|
||||
mListenerContext = nsnull;
|
||||
@ -407,8 +407,8 @@ class FTPCancelEarlyEvent : public ChannelEvent
|
||||
bool
|
||||
FTPChannelChild::RecvCancelEarly(const nsresult& statusCode)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new FTPCancelEarlyEvent(this, statusCode));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new FTPCancelEarlyEvent(this, statusCode));
|
||||
} else {
|
||||
DoCancelEarly(statusCode);
|
||||
}
|
||||
@ -453,8 +453,8 @@ class FTPDeleteSelfEvent : public ChannelEvent
|
||||
bool
|
||||
FTPChannelChild::RecvDeleteSelf()
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new FTPDeleteSelfEvent(this));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new FTPDeleteSelfEvent(this));
|
||||
} else {
|
||||
DoDeleteSelf();
|
||||
}
|
||||
@ -485,8 +485,10 @@ NS_IMETHODIMP
|
||||
FTPChannelChild::Suspend()
|
||||
{
|
||||
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
|
||||
mSuspendCount++;
|
||||
SendSuspend();
|
||||
if (!mSuspendCount++) {
|
||||
SendSuspend();
|
||||
mEventQ.Suspend();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -494,12 +496,10 @@ NS_IMETHODIMP
|
||||
FTPChannelChild::Resume()
|
||||
{
|
||||
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
|
||||
SendResume();
|
||||
--mSuspendCount;
|
||||
if (!mSuspendCount) {
|
||||
if (mQueuePhase == PHASE_UNQUEUED)
|
||||
mQueuePhase = PHASE_FINISHED_QUEUEING;
|
||||
FlushEventQueue();
|
||||
|
||||
if (!--mSuspendCount) {
|
||||
SendResume();
|
||||
mEventQ.Resume(); // TODO: make this async: see HttpChannelChild::Resume
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -68,7 +68,6 @@ class FTPChannelChild : public PFTPChannelChild
|
||||
, public nsIResumableChannel
|
||||
, public nsIProxiedChannel
|
||||
, public nsIChildChannel
|
||||
, public ChannelEventQueue<FTPChannelChild>
|
||||
{
|
||||
public:
|
||||
typedef ::nsIStreamListener nsIStreamListener;
|
||||
@ -137,6 +136,7 @@ private:
|
||||
nsCOMPtr<nsIInputStream> mUploadStream;
|
||||
|
||||
bool mIPCOpen;
|
||||
ChannelEventQueue mEventQ;
|
||||
bool mCanceled;
|
||||
PRUint32 mSuspendCount;
|
||||
PRPackedBool mIsPending;
|
||||
|
@ -60,13 +60,13 @@ namespace net {
|
||||
|
||||
HttpChannelChild::HttpChannelChild()
|
||||
: HttpAsyncAborter<HttpChannelChild>(this)
|
||||
, ChannelEventQueue<HttpChannelChild>(this)
|
||||
, mIsFromCache(PR_FALSE)
|
||||
, mCacheEntryAvailable(PR_FALSE)
|
||||
, mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME)
|
||||
, mSendResumeAt(false)
|
||||
, mIPCOpen(false)
|
||||
, mKeptAlive(false)
|
||||
, mEventQ(this)
|
||||
{
|
||||
LOG(("Creating HttpChannelChild @%x\n", this));
|
||||
}
|
||||
@ -219,13 +219,13 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead,
|
||||
const PRNetAddr& selfAddr,
|
||||
const PRNetAddr& peerAddr)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new StartRequestEvent(this, responseHead, useResponseHead,
|
||||
requestHeaders,
|
||||
isFromCache, cacheEntryAvailable,
|
||||
cacheExpirationTime, cachedCharset,
|
||||
securityInfoSerialization, selfAddr,
|
||||
peerAddr));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new StartRequestEvent(this, responseHead, useResponseHead,
|
||||
requestHeaders, isFromCache,
|
||||
cacheEntryAvailable,
|
||||
cacheExpirationTime, cachedCharset,
|
||||
securityInfoSerialization, selfAddr,
|
||||
peerAddr));
|
||||
} else {
|
||||
OnStartRequest(responseHead, useResponseHead, requestHeaders, isFromCache,
|
||||
cacheEntryAvailable, cacheExpirationTime, cachedCharset,
|
||||
@ -255,13 +255,13 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead,
|
||||
NS_DeserializeObject(securityInfoSerialization,
|
||||
getter_AddRefs(mSecurityInfo));
|
||||
}
|
||||
|
||||
|
||||
mIsFromCache = isFromCache;
|
||||
mCacheEntryAvailable = cacheEntryAvailable;
|
||||
mCacheExpirationTime = cacheExpirationTime;
|
||||
mCachedCharset = cachedCharset;
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
// replace our request headers with what actually got sent in the parent
|
||||
mRequestHead.ClearHeaders();
|
||||
@ -329,9 +329,10 @@ HttpChannelChild::RecvOnTransportAndData(const nsresult& status,
|
||||
const PRUint32& offset,
|
||||
const PRUint32& count)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new TransportAndDataEvent(this, status, progress, progressMax,
|
||||
data, offset, count));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new TransportAndDataEvent(this, status, progress,
|
||||
progressMax, data, offset,
|
||||
count));
|
||||
} else {
|
||||
OnTransportAndData(status, progress, progressMax, data, offset, count);
|
||||
}
|
||||
@ -357,7 +358,7 @@ HttpChannelChild::OnTransportAndData(const nsresult& status,
|
||||
|
||||
// Hold queue lock throughout all three calls, else we might process a later
|
||||
// necko msg in between them.
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
// block status/progress after Cancel or OnStopRequest has been called,
|
||||
// or if channel has LOAD_BACKGROUND set.
|
||||
@ -425,8 +426,8 @@ class StopRequestEvent : public ChannelEvent
|
||||
bool
|
||||
HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new StopRequestEvent(this, statusCode));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new StopRequestEvent(this, statusCode));
|
||||
} else {
|
||||
OnStopRequest(statusCode);
|
||||
}
|
||||
@ -447,7 +448,7 @@ HttpChannelChild::OnStopRequest(const nsresult& statusCode)
|
||||
{ // We must flush the queue before we Send__delete__
|
||||
// (although we really shouldn't receive any msgs after OnStop),
|
||||
// so make sure this goes out of scope before then.
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
mListener->OnStopRequest(this, mListenerContext, mStatus);
|
||||
|
||||
@ -490,8 +491,8 @@ bool
|
||||
HttpChannelChild::RecvOnProgress(const PRUint64& progress,
|
||||
const PRUint64& progressMax)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new ProgressEvent(this, progress, progressMax));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new ProgressEvent(this, progress, progressMax));
|
||||
} else {
|
||||
OnProgress(progress, progressMax);
|
||||
}
|
||||
@ -512,7 +513,7 @@ HttpChannelChild::OnProgress(const PRUint64& progress,
|
||||
if (!mProgressSink)
|
||||
GetCallback(mProgressSink);
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
// block socket status event after Cancel or OnStopRequest has been called,
|
||||
// or if channel has LOAD_BACKGROUND set
|
||||
@ -543,8 +544,8 @@ class StatusEvent : public ChannelEvent
|
||||
bool
|
||||
HttpChannelChild::RecvOnStatus(const nsresult& status)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new StatusEvent(this, status));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new StatusEvent(this, status));
|
||||
} else {
|
||||
OnStatus(status);
|
||||
}
|
||||
@ -563,7 +564,7 @@ HttpChannelChild::OnStatus(const nsresult& status)
|
||||
if (!mProgressSink)
|
||||
GetCallback(mProgressSink);
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
// block socket status event after Cancel or OnStopRequest has been called,
|
||||
// or if channel has LOAD_BACKGROUND set
|
||||
@ -593,8 +594,8 @@ class FailedAsyncOpenEvent : public ChannelEvent
|
||||
bool
|
||||
HttpChannelChild::RecvFailedAsyncOpen(const nsresult& status)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new FailedAsyncOpenEvent(this, status));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new FailedAsyncOpenEvent(this, status));
|
||||
} else {
|
||||
FailedAsyncOpen(status);
|
||||
}
|
||||
@ -640,8 +641,8 @@ class DeleteSelfEvent : public ChannelEvent
|
||||
bool
|
||||
HttpChannelChild::RecvDeleteSelf()
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new DeleteSelfEvent(this));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new DeleteSelfEvent(this));
|
||||
} else {
|
||||
DeleteSelf();
|
||||
}
|
||||
@ -687,9 +688,9 @@ HttpChannelChild::RecvRedirect1Begin(const PRUint32& newChannelId,
|
||||
const PRUint32& redirectFlags,
|
||||
const nsHttpResponseHead& responseHead)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new Redirect1Event(this, newChannelId, newUri, redirectFlags,
|
||||
responseHead));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new Redirect1Event(this, newChannelId, newUri,
|
||||
redirectFlags, responseHead));
|
||||
} else {
|
||||
Redirect1Begin(newChannelId, newUri, redirectFlags, responseHead);
|
||||
}
|
||||
@ -760,8 +761,8 @@ class Redirect3Event : public ChannelEvent
|
||||
bool
|
||||
HttpChannelChild::RecvRedirect3Complete()
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new Redirect3Event(this));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new Redirect3Event(this));
|
||||
} else {
|
||||
Redirect3Complete();
|
||||
}
|
||||
@ -905,8 +906,10 @@ NS_IMETHODIMP
|
||||
HttpChannelChild::Suspend()
|
||||
{
|
||||
NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
|
||||
SendSuspend();
|
||||
mSuspendCount++;
|
||||
if (!mSuspendCount++) {
|
||||
SendSuspend();
|
||||
mEventQ.Suspend();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -918,7 +921,9 @@ HttpChannelChild::CompleteResume()
|
||||
mCallOnResume = 0;
|
||||
}
|
||||
|
||||
FlushEventQueue();
|
||||
// Don't resume event queue until now, else queued events could get
|
||||
// flushed/called before mCallOnResume, which needs to run first.
|
||||
mEventQ.Resume();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -929,15 +934,8 @@ HttpChannelChild::Resume()
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
SendResume();
|
||||
mSuspendCount--;
|
||||
if (!mSuspendCount) {
|
||||
// If we were suspended outside of an event handler (bug 595972) we'll
|
||||
// consider ourselves unqueued. This is a legal state of affairs but
|
||||
// FlushEventQueue() can't easily ensure this fact, so we'll do some
|
||||
// fudging to set the invariants correctly.
|
||||
if (mQueuePhase == PHASE_UNQUEUED)
|
||||
mQueuePhase = PHASE_FINISHED_QUEUEING;
|
||||
if (!--mSuspendCount) {
|
||||
SendResume();
|
||||
rv = AsyncCall(&HttpChannelChild::CompleteResume);
|
||||
}
|
||||
return rv;
|
||||
|
@ -76,7 +76,6 @@ class HttpChannelChild : public PHttpChannelChild
|
||||
, public nsIAssociatedContentSecurity
|
||||
, public nsIChildChannel
|
||||
, public nsIHttpChannelChild
|
||||
, public ChannelEventQueue<HttpChannelChild>
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
@ -173,6 +172,7 @@ private:
|
||||
|
||||
bool mIPCOpen;
|
||||
bool mKeptAlive;
|
||||
ChannelEventQueue mEventQ;
|
||||
|
||||
void OnStartRequest(const nsHttpResponseHead& responseHead,
|
||||
const PRBool& useResponseHead,
|
||||
|
@ -57,8 +57,7 @@ NS_IMPL_ISUPPORTS3(WyciwygChannelChild,
|
||||
|
||||
|
||||
WyciwygChannelChild::WyciwygChannelChild()
|
||||
: ChannelEventQueue<WyciwygChannelChild>(this)
|
||||
, mStatus(NS_OK)
|
||||
: mStatus(NS_OK)
|
||||
, mIsPending(PR_FALSE)
|
||||
, mCanceled(false)
|
||||
, mLoadFlags(LOAD_NORMAL)
|
||||
@ -66,6 +65,7 @@ WyciwygChannelChild::WyciwygChannelChild()
|
||||
, mCharsetSource(kCharsetUninitialized)
|
||||
, mState(WCC_NEW)
|
||||
, mIPCOpen(false)
|
||||
, mEventQ(this)
|
||||
{
|
||||
LOG(("Creating WyciwygChannelChild @%x\n", this));
|
||||
}
|
||||
@ -138,9 +138,10 @@ WyciwygChannelChild::RecvOnStartRequest(const nsresult& statusCode,
|
||||
const nsCString& charset,
|
||||
const nsCString& securityInfo)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new WyciwygStartRequestEvent(this, statusCode, contentLength,
|
||||
source, charset, securityInfo));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new WyciwygStartRequestEvent(this, statusCode,
|
||||
contentLength, source,
|
||||
charset, securityInfo));
|
||||
} else {
|
||||
OnStartRequest(statusCode, contentLength, source, charset, securityInfo);
|
||||
}
|
||||
@ -167,7 +168,7 @@ WyciwygChannelChild::OnStartRequest(const nsresult& statusCode,
|
||||
NS_DeserializeObject(securityInfo, getter_AddRefs(mSecurityInfo));
|
||||
}
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
nsresult rv = mListener->OnStartRequest(this, mListenerContext);
|
||||
if (NS_FAILED(rv))
|
||||
@ -192,8 +193,8 @@ bool
|
||||
WyciwygChannelChild::RecvOnDataAvailable(const nsCString& data,
|
||||
const PRUint32& offset)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new WyciwygDataAvailableEvent(this, data, offset));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new WyciwygDataAvailableEvent(this, data, offset));
|
||||
} else {
|
||||
OnDataAvailable(data, offset);
|
||||
}
|
||||
@ -226,7 +227,7 @@ WyciwygChannelChild::OnDataAvailable(const nsCString& data,
|
||||
return;
|
||||
}
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
rv = mListener->OnDataAvailable(this, mListenerContext,
|
||||
stringStream, offset, data.Length());
|
||||
@ -253,8 +254,8 @@ private:
|
||||
bool
|
||||
WyciwygChannelChild::RecvOnStopRequest(const nsresult& statusCode)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new WyciwygStopRequestEvent(this, statusCode));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new WyciwygStopRequestEvent(this, statusCode));
|
||||
} else {
|
||||
OnStopRequest(statusCode);
|
||||
}
|
||||
@ -269,8 +270,8 @@ WyciwygChannelChild::OnStopRequest(const nsresult& statusCode)
|
||||
|
||||
{ // We need to ensure that all IPDL message dispatching occurs
|
||||
// before we delete the protocol below
|
||||
AutoEventEnqueuer ensureSerialDispatch(this);
|
||||
|
||||
AutoEventEnqueuer ensureSerialDispatch(mEventQ);
|
||||
|
||||
mState = WCC_ONSTOP;
|
||||
|
||||
mIsPending = PR_FALSE;
|
||||
@ -310,8 +311,8 @@ class WyciwygCancelEvent : public ChannelEvent
|
||||
bool
|
||||
WyciwygChannelChild::RecvCancelEarly(const nsresult& statusCode)
|
||||
{
|
||||
if (ShouldEnqueue()) {
|
||||
EnqueueEvent(new WyciwygCancelEvent(this, statusCode));
|
||||
if (mEventQ.ShouldEnqueue()) {
|
||||
mEventQ.Enqueue(new WyciwygCancelEvent(this, statusCode));
|
||||
} else {
|
||||
CancelEarly(statusCode);
|
||||
}
|
||||
|
@ -67,7 +67,6 @@ enum WyciwygChannelChildState {
|
||||
// Header file contents
|
||||
class WyciwygChannelChild : public PWyciwygChannelChild
|
||||
, public nsIWyciwygChannel
|
||||
, public ChannelEventQueue<WyciwygChannelChild>
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -128,6 +127,7 @@ private:
|
||||
enum WyciwygChannelChildState mState;
|
||||
|
||||
bool mIPCOpen;
|
||||
ChannelEventQueue mEventQ;
|
||||
|
||||
friend class WyciwygStartRequestEvent;
|
||||
friend class WyciwygDataAvailableEvent;
|
||||
|
@ -25,7 +25,9 @@ var listener = {
|
||||
// Insert a delay between this and the next callback to ensure message buffering
|
||||
// works correctly
|
||||
request.suspend();
|
||||
request.suspend();
|
||||
do_timeout(RESUME_DELAY, function() request.resume());
|
||||
do_timeout(RESUME_DELAY + 1000, function() request.resume());
|
||||
},
|
||||
|
||||
onDataAvailable: function(request, context, stream, offset, count) {
|
||||
@ -34,6 +36,8 @@ var listener = {
|
||||
|
||||
// Ensure that suspending and resuming inside a callback works correctly
|
||||
request.suspend();
|
||||
request.suspend();
|
||||
request.resume();
|
||||
request.resume();
|
||||
|
||||
this._gotData = true;
|
||||
|
Loading…
Reference in New Issue
Block a user