From bbaa044539ffd80cdaea678512c09dff5deadde8 Mon Sep 17 00:00:00 2001 From: "scott%scott-macgregor.org" Date: Thu, 15 Jul 2004 16:47:10 +0000 Subject: [PATCH] Bug #251418 --> Expose progress notification events through nsIXMLHttpRequest. Allow consumers of nsIXMLHttpRequest to set a progress event listener. Listen for the http channel's nsIProgressEventSink notification and fire our new progress event to the consumer. r/sr=jst --- .../base/public/nsIXMLHttpRequest.idl | 14 ++ .../xmlextras/base/src/nsXMLHttpRequest.cpp | 154 +++++++++++++++++- .../xmlextras/base/src/nsXMLHttpRequest.h | 28 ++++ .../xmlextras/build/src/nsXMLExtrasModule.cpp | 14 ++ 4 files changed, 207 insertions(+), 3 deletions(-) diff --git a/extensions/xmlextras/base/public/nsIXMLHttpRequest.idl b/extensions/xmlextras/base/public/nsIXMLHttpRequest.idl index 0e1f42fc80f..ef7ccca2e3f 100644 --- a/extensions/xmlextras/base/public/nsIXMLHttpRequest.idl +++ b/extensions/xmlextras/base/public/nsIXMLHttpRequest.idl @@ -315,6 +315,20 @@ interface nsIJSXMLHttpRequest : nsISupports { */ attribute nsIDOMEventListener onerror; + /** + * Meant to be a script-only mechanism for setting a progress event listener. + * The attribute is expected to be JavaScript function object. When + * the error event occurs, the function is invoked. + * This attribute should not be used from native code!! + * This event listener may be called multiple times during the open request. + * + * After the initial response, all event listeners will be cleared. + * Call open() before setting new event listeners. + * + * Mozilla only. + */ + attribute nsIDOMEventListener onprogress; + /** * Meant to be a script-only mechanism for setting a callback function. * The attribute is expected to be JavaScript function object. When the diff --git a/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp b/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp index feba86c52b5..e3124fa37e6 100644 --- a/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp +++ b/extensions/xmlextras/base/src/nsXMLHttpRequest.cpp @@ -285,6 +285,7 @@ NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequest) NS_INTERFACE_MAP_ENTRY(nsIRequestObserver) NS_INTERFACE_MAP_ENTRY(nsIStreamListener) NS_INTERFACE_MAP_ENTRY(nsIHttpEventSink) + NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink) NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) NS_INTERFACE_MAP_ENTRY_EXTERNAL_DOM_CLASSINFO(XMLHttpRequest) @@ -429,6 +430,28 @@ nsXMLHttpRequest::SetOnerror(nsIDOMEventListener * aOnerror) return NS_OK; } +/* attribute nsIDOMEventListener onprogress; */ +NS_IMETHODIMP +nsXMLHttpRequest::GetOnprogress(nsIDOMEventListener * *aOnprogress) +{ + NS_ENSURE_ARG_POINTER(aOnprogress); + + *aOnprogress = mOnProgressListener; + NS_IF_ADDREF(*aOnprogress); + + return NS_OK; +} + +NS_IMETHODIMP +nsXMLHttpRequest::SetOnprogress(nsIDOMEventListener * aOnprogress) +{ + mOnProgressListener = aOnprogress; + + mScriptContext = GetCurrentContext(); + + return NS_OK; +} + /* readonly attribute nsIChannel channel; */ NS_IMETHODIMP nsXMLHttpRequest::GetChannel(nsIChannel **aChannel) @@ -806,6 +829,8 @@ nsXMLHttpRequest::ClearEventListeners() mOnLoadListener = nsnull; mOnErrorListener = nsnull; mOnReadystatechangeListener = nsnull; + mOnProgressListener = nsnull; + } already_AddRefed @@ -891,9 +916,11 @@ nsXMLHttpRequest::OpenRequest(const nsACString& method, GetLoadGroup(getter_AddRefs(loadGroup)); // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, - // which in turn keeps STOP button from becoming active + // which in turn keeps STOP button from becoming active. + // If the consumer passed in a progress event handler we must load with nsIRequest::LOAD_NORMAL + // or necko won't generate any progress notifications rv = NS_NewChannel(getter_AddRefs(mChannel), uri, nsnull, loadGroup, - nsnull, nsIRequest::LOAD_BACKGROUND); + nsnull, mOnProgressListener ? nsIRequest::LOAD_NORMAL : nsIRequest::LOAD_BACKGROUND); if (NS_FAILED(rv)) return rv; //mChannel->SetAuthTriedWithPrehost(authp); @@ -1778,6 +1805,38 @@ nsXMLHttpRequest::OnRedirect(nsIHttpChannel *aHttpChannel, return NS_OK; } +///////////////////////////////////////////////////// +// nsIProgressEventSink methods: +// + +NS_IMETHODIMP +nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, PRUint32 aProgress, PRUint32 aProgressMax) +{ + if (mOnProgressListener) + { + nsCOMPtr event; + nsEvent evt(NS_EVENT_NULL ); // what name do we make up here? + nsresult rv = CreateEvent(&evt, getter_AddRefs(event)); + NS_ENSURE_SUCCESS(rv, rv); + + nsXMLHttpProgressEvent * progressEvent = new nsXMLHttpProgressEvent(event, aProgress, aProgressMax); + if (!progressEvent) + return NS_ERROR_OUT_OF_MEMORY; + + event = do_QueryInterface(progressEvent); + NotifyEventListeners(mOnProgressListener, nsnull, event); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatus, const PRUnichar *aStatusArg) +{ + // nothing to do here... + return NS_OK; +} + ///////////////////////////////////////////////////// // nsIInterfaceRequestor methods: // @@ -1807,7 +1866,6 @@ nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult) return QueryInterface(aIID, aResult); } - NS_IMPL_ISUPPORTS1(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor) NS_IMETHODIMP nsXMLHttpRequest:: @@ -1819,3 +1877,93 @@ nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value) mHeaders.Append('\n'); return NS_OK; } + +// DOM event class to handle progress notifications +nsXMLHttpProgressEvent::nsXMLHttpProgressEvent(nsIDOMEvent * aInner, PRUint32 aCurrentProgress, PRUint32 aMaxProgress) +{ + mInner = aInner; + mCurProgress = aCurrentProgress; + mMaxProgress = aMaxProgress; +} + +nsXMLHttpProgressEvent::~nsXMLHttpProgressEvent() +{} + +// QueryInterface implementation for nsXMLHttpRequest +NS_INTERFACE_MAP_BEGIN(nsXMLHttpProgressEvent) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLSProgressEvent) + NS_INTERFACE_MAP_ENTRY(nsIDOMLSProgressEvent) + NS_INTERFACE_MAP_ENTRY(nsIDOMEvent) + NS_INTERFACE_MAP_ENTRY_EXTERNAL_DOM_CLASSINFO(XMLHttpProgressEvent) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(nsXMLHttpProgressEvent) +NS_IMPL_RELEASE(nsXMLHttpProgressEvent) + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetInput(nsIDOMLSInput * *aInput) +{ + *aInput = nsnull; + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetPosition(PRUint32 *aPosition) +{ + *aPosition = mCurProgress; + return NS_OK; +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetTotalSize(PRUint32 *aTotalSize) +{ + *aTotalSize = mMaxProgress; + return NS_OK; +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetType(nsAString & aType) +{ + return mInner->GetType(aType); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetTarget(nsIDOMEventTarget * *aTarget) +{ + return mInner->GetTarget(aTarget); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetCurrentTarget(nsIDOMEventTarget * *aCurrentTarget) +{ + return mInner->GetCurrentTarget(aCurrentTarget); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetEventPhase(PRUint16 *aEventPhase) +{ + return mInner->GetEventPhase(aEventPhase); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetBubbles(PRBool *aBubbles) +{ + return mInner->GetBubbles(aBubbles); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetCancelable(PRBool *aCancelable) +{ + return mInner->GetCancelable(aCancelable); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::GetTimeStamp(DOMTimeStamp *aTimeStamp) +{ + return mInner->GetTimeStamp(aTimeStamp); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::StopPropagation() +{ + return mInner->StopPropagation(); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::PreventDefault() +{ + return mInner->PreventDefault(); +} + +NS_IMETHODIMP nsXMLHttpProgressEvent::InitEvent(const nsAString & eventTypeArg, PRBool canBubbleArg, PRBool cancelableArg) +{ + return mInner->InitEvent(eventTypeArg, canBubbleArg, cancelableArg); +} diff --git a/extensions/xmlextras/base/src/nsXMLHttpRequest.h b/extensions/xmlextras/base/src/nsXMLHttpRequest.h index 8fc34219098..c3d328a619b 100644 --- a/extensions/xmlextras/base/src/nsXMLHttpRequest.h +++ b/extensions/xmlextras/base/src/nsXMLHttpRequest.h @@ -57,6 +57,10 @@ #include "nsIHttpEventSink.h" #include "nsIInterfaceRequestor.h" #include "nsIHttpHeaderVisitor.h" +#include "nsIProgressEventSink.h" + +#include "nsIDOMLSProgressEvent.h" + class nsILoadGroup; class nsXMLHttpRequest : public nsIXMLHttpRequest, @@ -66,6 +70,7 @@ class nsXMLHttpRequest : public nsIXMLHttpRequest, public nsIStreamListener, public nsIHttpEventSink, public nsIInterfaceRequestor, + public nsIProgressEventSink, public nsSupportsWeakReference { public: @@ -102,6 +107,9 @@ public: // nsIHttpEventSink NS_DECL_NSIHTTPEVENTSINK + // nsIProgressEventSink + NS_DECL_NSIPROGRESSEVENTSINK + // nsIInterfaceRequestor NS_DECL_NSIINTERFACEREQUESTOR protected: @@ -139,6 +147,7 @@ protected: nsCOMPtr mOnLoadListener; nsCOMPtr mOnErrorListener; + nsCOMPtr mOnProgressListener; nsCOMPtr mOnReadystatechangeListener; @@ -164,4 +173,23 @@ protected: PRUint32 mState; }; + +// helper class to expose a progress DOM Event + +class nsXMLHttpProgressEvent : public nsIDOMLSProgressEvent +{ +public: + nsXMLHttpProgressEvent(nsIDOMEvent * aInner, PRUint32 aCurrentProgress, PRUint32 aMaxProgress); + virtual ~nsXMLHttpProgressEvent(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMLSPROGRESSEVENT + NS_DECL_NSIDOMEVENT + +protected: + nsCOMPtr mInner; + PRUint32 mCurProgress; + PRUint32 mMaxProgress; +}; + #endif diff --git a/extensions/xmlextras/build/src/nsXMLExtrasModule.cpp b/extensions/xmlextras/build/src/nsXMLExtrasModule.cpp index fdf49638051..0c90f72f036 100644 --- a/extensions/xmlextras/build/src/nsXMLExtrasModule.cpp +++ b/extensions/xmlextras/build/src/nsXMLExtrasModule.cpp @@ -67,6 +67,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsXPointerResult) NS_DECL_DOM_CLASSINFO(XMLSerializer) NS_DECL_DOM_CLASSINFO(XMLHttpRequest) +NS_DECL_DOM_CLASSINFO(XMLHttpProgressEvent) NS_DECL_DOM_CLASSINFO(DOMParser) NS_DECL_DOM_CLASSINFO(XPointerResult) @@ -90,6 +91,11 @@ NS_DOMCI_EXTENSION(XMLExtras) NS_DOMCI_EXTENSION_ENTRY_INTERFACE(nsIDOMEventTarget) NS_DOMCI_EXTENSION_ENTRY_END_NO_PRIMARY_IF(XMLHttpRequest, PR_TRUE, &kXMLHttpRequestCID) + NS_DOMCI_EXTENSION_ENTRY_BEGIN(XMLHttpProgressEvent) + NS_DOMCI_EXTENSION_ENTRY_INTERFACE(nsIDOMEvent) + NS_DOMCI_EXTENSION_ENTRY_INTERFACE(nsIDOMLSProgressEvent) + NS_DOMCI_EXTENSION_ENTRY_END_NO_PRIMARY_IF(XMLHttpProgressEvent, PR_TRUE, nsnull) + static NS_DEFINE_CID(kDOMParserCID, NS_DOMPARSER_CID); NS_DOMCI_EXTENSION_ENTRY_BEGIN(DOMParser) NS_DOMCI_EXTENSION_ENTRY_INTERFACE(nsIDOMParser) @@ -161,6 +167,13 @@ RegisterXMLExtras(nsIComponentManager *aCompMgr, PR_TRUE, PR_TRUE, getter_Copies(previous)); NS_ENSURE_SUCCESS(rv, rv); + + rv = catman->AddCategoryEntry(JAVASCRIPT_DOM_CLASS, + "XMLHttpProgressEvent", + XMLEXTRAS_DOMCI_EXTENSION_CONTRACTID, + PR_TRUE, PR_TRUE, getter_Copies(previous)); + NS_ENSURE_SUCCESS(rv, rv); + char* iidString = NS_GET_IID(nsIXMLHttpRequest).ToString(); if (!iidString) return NS_ERROR_OUT_OF_MEMORY; @@ -215,6 +228,7 @@ XMLExtrasModuleDestructor(nsIModule* self) { NS_IF_RELEASE(NS_CLASSINFO_NAME(XMLSerializer)); NS_IF_RELEASE(NS_CLASSINFO_NAME(XMLHttpRequest)); + NS_IF_RELEASE(NS_CLASSINFO_NAME(XMLHttpProgressEvent)); NS_IF_RELEASE(NS_CLASSINFO_NAME(DOMParser)); NS_IF_RELEASE(NS_CLASSINFO_NAME(XPointerResult)); }