From 15b52ddb80bd631ff0da09121ce58f2059ce8cac Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Mon, 20 Oct 2008 00:26:37 +0300 Subject: [PATCH] Bug 444546 - Don't dispatch progress event more often than every 350msec, r+sr=sicking --- content/base/src/nsXMLHttpRequest.cpp | 102 +++++++++++++++++++++++++- content/base/src/nsXMLHttpRequest.h | 17 ++++- 2 files changed, 114 insertions(+), 5 deletions(-) diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index 5ef831b1e045..cf48d2d473f7 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -143,6 +143,8 @@ #define NS_BADCERTHANDLER_CONTRACTID \ "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1" +#define NS_PROGRESS_EVENT_INTERVAL 350 + NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventListenerWrapper) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMEventListenerWrapper) @@ -1042,7 +1044,11 @@ nsAccessControlLRUCache* nsXMLHttpRequest::sAccessControlCache = nsnull; nsXMLHttpRequest::nsXMLHttpRequest() : mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNINITIALIZED), mUploadTransferred(0), mUploadTotal(0), mUploadComplete(PR_TRUE), - mErrorLoad(PR_FALSE), mFirstStartRequestSeen(PR_FALSE) + mUploadProgress(0), mUploadProgressMax(0), + mErrorLoad(PR_FALSE), mTimerIsActive(PR_FALSE), + mProgressEventWasDelayed(PR_FALSE), + mLoadLengthComputable(PR_FALSE), mLoadTotal(0), + mFirstStartRequestSeen(PR_FALSE) { nsLayoutStatics::AddRef(); } @@ -1215,6 +1221,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLHttpRequest) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer) + NS_INTERFACE_MAP_ENTRY(nsITimerCallback) NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XMLHttpRequest) NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget) @@ -1487,6 +1494,7 @@ nsXMLHttpRequest::Abort() mACGetChannel->Cancel(NS_BINDING_ABORTED); } mResponseXML = nsnull; + PRUint32 responseLength = mResponseBody.Length(); mResponseBody.Truncate(); mState |= XML_HTTP_REQUEST_ABORTED; @@ -1498,7 +1506,8 @@ nsXMLHttpRequest::Abort() if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) { NS_NAMED_LITERAL_STRING(abortStr, ABORT_STR); - DispatchProgressEvent(this, abortStr, PR_FALSE, mResponseBody.Length(), 0); + DispatchProgressEvent(this, abortStr, mLoadLengthComputable, responseLength, + mLoadTotal); if (mUpload && !mUploadComplete) { mUploadComplete = PR_TRUE; DispatchProgressEvent(mUpload, abortStr, PR_TRUE, mUploadTransferred, @@ -2037,7 +2046,7 @@ IsSameOrBaseChannel(nsIRequest* aPossibleBase, nsIChannel* aChannel) NS_IMETHODIMP nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) { - nsresult rv; + nsresult rv = NS_OK; if (!mFirstStartRequestSeen && mRequestObserver) { mFirstStartRequestSeen = PR_TRUE; mRequestObserver->OnStartRequest(request, ctxt); @@ -2181,6 +2190,14 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt) NS_ENSURE_SUCCESS(rv, rv); } + // We won't get any progress events anyway if we didn't have progress + // events when starting the request - so maybe no need to start timer here. + if (NS_SUCCEEDED(rv) && + (mState & XML_HTTP_REQUEST_ASYNC) && + HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR))) { + StartProgressEventTimer(); + } + return NS_OK; } @@ -2414,6 +2431,10 @@ nsXMLHttpRequest::Send(nsIVariant *aBody) // By default we don't have any upload, so mark upload complete. mUploadComplete = PR_TRUE; mErrorLoad = PR_FALSE; + mLoadLengthComputable = PR_FALSE; + mLoadTotal = 0; + mUploadProgress = 0; + mUploadProgressMax = 0; if (aBody && httpChannel && !method.EqualsLiteral("GET")) { nsXPIDLString serial; nsCOMPtr postDataStream; @@ -2741,6 +2762,11 @@ nsXMLHttpRequest::Send(nsIVariant *aBody) } } } else { + if (!mUploadComplete && + HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) || + (mUpload && mUpload->HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR)))) { + StartProgressEventTimer(); + } DispatchProgressEvent(this, NS_LITERAL_STRING(LOADSTART_STR), PR_FALSE, 0, 0); if (mUpload && !mUploadComplete) { @@ -3047,6 +3073,12 @@ nsXMLHttpRequest::ChangeState(PRUint32 aState, PRBool aBroadcast) mState |= aState; nsresult rv = NS_OK; + if (mProgressNotifier && + !(aState & (XML_HTTP_REQUEST_LOADED | XML_HTTP_REQUEST_INTERACTIVE))) { + mTimerIsActive = PR_FALSE; + mProgressNotifier->Cancel(); + } + if ((mState & XML_HTTP_REQUEST_ASYNC) && (aState & XML_HTTP_REQUEST_LOADSTATES) && // Broadcast load states only aBroadcast) { @@ -3125,16 +3157,28 @@ nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, PRUint total -= headerSize; } mUploadTransferred = loaded; + mUploadProgress = aProgress; + mUploadProgressMax = aProgressMax; + } else { + mLoadLengthComputable = lengthComputable; + mLoadTotal = mLoadLengthComputable ? total : 0; + } + + if (mTimerIsActive) { + // The progress event will be dispatched when the notifier calls Notify(). + mProgressEventWasDelayed = PR_TRUE; + return NS_OK; } if (!mErrorLoad) { + StartProgressEventTimer(); NS_NAMED_LITERAL_STRING(progress, PROGRESS_STR); NS_NAMED_LITERAL_STRING(uploadprogress, UPLOADPROGRESS_STR); DispatchProgressEvent(this, upload ? uploadprogress : progress, PR_TRUE, lengthComputable, loaded, lengthComputable ? total : 0, aProgress, aProgressMax); - if (upload && mUpload) { + if (upload && mUpload && !mUploadComplete) { NS_WARN_IF_FALSE(mUploadTotal == PRUint32(total), "Wrong upload total?"); DispatchProgressEvent(mUpload, progress, PR_TRUE, lengthComputable, loaded, lengthComputable ? total : 0, aProgress, aProgressMax); @@ -3248,6 +3292,56 @@ nsXMLHttpRequest::GetUpload(nsIXMLHttpRequestUpload** aUpload) return NS_OK; } +NS_IMETHODIMP +nsXMLHttpRequest::Notify(nsITimer* aTimer) +{ + mTimerIsActive = PR_FALSE; + if (NS_SUCCEEDED(CheckInnerWindowCorrectness()) && !mErrorLoad) { + if (mProgressEventWasDelayed) { + mProgressEventWasDelayed = PR_FALSE; + if (!(XML_HTTP_REQUEST_MPART_HEADERS & mState)) { + StartProgressEventTimer(); + // We're uploading if our state is XML_HTTP_REQUEST_OPENED or + // XML_HTTP_REQUEST_SENT + if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) { + DispatchProgressEvent(this, NS_LITERAL_STRING(UPLOADPROGRESS_STR), + PR_TRUE, PR_TRUE, mUploadTransferred, + mUploadTotal, mUploadProgress, + mUploadProgressMax); + if (mUpload && !mUploadComplete) { + DispatchProgressEvent(mUpload, NS_LITERAL_STRING(PROGRESS_STR), + PR_TRUE, PR_TRUE, mUploadTransferred, + mUploadTotal, mUploadProgress, + mUploadProgressMax); + } + } else { + DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR), + mLoadLengthComputable, mResponseBody.Length(), + mLoadTotal); + } + } + } + } else if (mProgressNotifier) { + mProgressNotifier->Cancel(); + } + return NS_OK; +} + +void +nsXMLHttpRequest::StartProgressEventTimer() +{ + if (!mProgressNotifier) { + mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID); + } + if (mProgressNotifier) { + mProgressEventWasDelayed = PR_FALSE; + mTimerIsActive = PR_TRUE; + mProgressNotifier->Cancel(); + mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL, + nsITimer::TYPE_ONE_SHOT); + } +} + NS_IMPL_ISUPPORTS1(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor) NS_IMETHODIMP nsXMLHttpRequest:: diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h index bffb33e1a323..8dc29455e818 100644 --- a/content/base/src/nsXMLHttpRequest.h +++ b/content/base/src/nsXMLHttpRequest.h @@ -70,6 +70,7 @@ #include "prtime.h" #include "nsIEventListenerManager.h" #include "nsIDOMNSEvent.h" +#include "nsITimer.h" #include "nsIPrivateDOMEvent.h" #include "nsDOMProgressEvent.h" @@ -251,7 +252,8 @@ class nsXMLHttpRequest : public nsXHREventTarget, public nsIProgressEventSink, public nsIInterfaceRequestor, public nsSupportsWeakReference, - public nsIJSNativeInitializer + public nsIJSNativeInitializer, + public nsITimerCallback { public: nsXMLHttpRequest(); @@ -293,6 +295,9 @@ public: // nsIInterfaceRequestor NS_DECL_NSIINTERFACEREQUESTOR + // nsITimerCallback + NS_DECL_NSITIMERCALLBACK + // nsIJSNativeInitializer NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj, PRUint32 argc, jsval* argv); @@ -394,6 +399,8 @@ protected: */ nsresult CheckChannelForCrossSiteRequest(nsIChannel* aChannel); + void StartProgressEventTimer(); + nsCOMPtr mContext; nsCOMPtr mPrincipal; nsCOMPtr mChannel; @@ -445,9 +452,17 @@ protected: PRUint32 mUploadTransferred; PRUint32 mUploadTotal; PRPackedBool mUploadComplete; + PRUint64 mUploadProgress; // For legacy + PRUint64 mUploadProgressMax; // For legacy PRPackedBool mErrorLoad; + PRPackedBool mTimerIsActive; + PRPackedBool mProgressEventWasDelayed; + PRPackedBool mLoadLengthComputable; + PRUint32 mLoadTotal; // 0 if not known. + nsCOMPtr mProgressNotifier; + PRPackedBool mFirstStartRequestSeen; };