Make XMLHttpRequest dispatch an error event when it fails. Bug 218236, patch

by Wladimir Palant <trev@gtchat.de>, r=jst, sr=bzbarsky
This commit is contained in:
bzbarsky%mit.edu 2004-03-06 19:05:08 +00:00
Родитель a8f8c3eb40
Коммит 3e7be428ed
2 изменённых файлов: 113 добавлений и 119 удалений

Просмотреть файл

@ -538,8 +538,12 @@ nsXMLHttpRequest::Abort()
mDocument = nsnull;
mState |= XML_HTTP_REQUEST_ABORTED;
ChangeState(XML_HTTP_REQUEST_COMPLETED);
ClearEventListeners();
ChangeState(XML_HTTP_REQUEST_UNINITIALIZED, PR_FALSE); // IE seems to do it
return NS_OK;
}
@ -627,6 +631,85 @@ nsXMLHttpRequest::GetBaseURI(nsIURI **aBaseURI)
return NS_OK;
}
nsresult
nsXMLHttpRequest::CreateEvent(PRUint32 aMsg, nsIDOMEvent** aDOMEvent)
{
nsresult rv;
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(mDocument));
if (!receiver) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIEventListenerManager> manager;
rv = receiver->GetListenerManager(getter_AddRefs(manager));
if (!manager) {
return NS_ERROR_FAILURE;
}
nsEvent event(aMsg);
rv = manager->CreateEvent(nsnull, &event,
NS_LITERAL_STRING("HTMLEvents"),
aDOMEvent);
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIPrivateDOMEvent> privevent(do_QueryInterface(*aDOMEvent));
if (!privevent) {
NS_IF_RELEASE(*aDOMEvent);
return NS_ERROR_FAILURE;
}
privevent->SetTarget(this);
privevent->SetCurrentTarget(this);
privevent->SetOriginalTarget(this);
return NS_OK;
}
void
nsXMLHttpRequest::NotifyEventListeners(nsIDOMEventListener* aHandler, nsISupportsArray* aListeners, nsIDOMEvent* aEvent)
{
if (!aEvent)
return;
nsCOMPtr<nsIJSContextStack> stack;
JSContext *cx = nsnull;
if (mScriptContext) {
stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
if (stack) {
cx = (JSContext *)mScriptContext->GetNativeContext();
if (cx) {
stack->Push(cx);
}
}
}
if (aHandler) {
aHandler->HandleEvent(aEvent);
}
if (aListeners) {
PRUint32 index, count;
aListeners->Count(&count);
for (index = 0; index < count; index++) {
nsCOMPtr<nsIDOMEventListener> listener = do_QueryElementAt(aListeners, index);
if (listener) {
listener->HandleEvent(aEvent);
}
}
}
if (cx) {
stack->Pop(&cx);
}
}
void
nsXMLHttpRequest::ClearEventListeners()
{
@ -942,6 +1025,10 @@ nsXMLHttpRequest::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInp
NS_IMETHODIMP
nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
{
// Don't do anything if we have been aborted
if (mState & XML_HTTP_REQUEST_UNINITIALIZED)
return NS_OK;
mReadRequest = request;
mContext = ctxt;
mState |= XML_HTTP_REQUEST_PARSEBODY;
@ -999,6 +1086,10 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
NS_IMETHODIMP
nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
{
// Don't do anything if we have been aborted
if (mState & XML_HTTP_REQUEST_UNINITIALIZED)
return NS_OK;
nsresult rv = NS_OK;
nsCOMPtr<nsIParser> parser;
@ -1016,17 +1107,14 @@ nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult
mChannel->SetNotificationCallbacks(nsnull);
if (NS_FAILED(status)) {
// This can happen if the user leaves the page while the request was still
// active. This might also happen if request was active during the regular
// page load and the user was able to hit STOP button. If XMLHttpRequest is
// the only active network connection on the page the throbber nor the STOP
// button are active.
Abort();
// This can happen if the server is unreachable. Other possible reasons
// are that the user leaves the page or hits the ESC key.
Error(nsnull);
// By nulling out channel here we make it so that Send() can test for that
// and throw. Also calling the various status methods/members will not throw.
// This matches what IE does.
mChannel = nsnull;
ChangeState(XML_HTTP_REQUEST_COMPLETED, PR_FALSE); // IE also seems to set this
} else if (!parser || parser->IsParserEnabled()) {
// If we don't have a parser, we never attempted to parse the
// incoming data, and we can proceed to call RequestCompleted().
@ -1063,32 +1151,9 @@ nsXMLHttpRequest::RequestCompleted()
return NS_OK;
}
// We need to create the event before nulling out mDocument
nsCOMPtr<nsIDOMEvent> domevent;
nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(mDocument));
if (!receiver) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIEventListenerManager> manager;
rv = receiver->GetListenerManager(getter_AddRefs(manager));
if (!manager) {
return NS_ERROR_FAILURE;
}
nsEvent event(NS_PAGE_LOAD);
rv = manager->CreateEvent(nsnull, &event,
NS_LITERAL_STRING("HTMLEvents"),
getter_AddRefs(domevent));
if (NS_FAILED(rv)) {
return rv;
}
nsCOMPtr<nsIPrivateDOMEvent> privevent(do_QueryInterface(domevent));
if (!privevent) {
return NS_ERROR_FAILURE;
}
privevent->SetTarget(this);
privevent->SetCurrentTarget(this);
rv = CreateEvent(NS_PAGE_LOAD, getter_AddRefs(domevent));
// We might have been sent non-XML data. If that was the case,
// we should null out the document member. The idea in this
@ -1104,48 +1169,9 @@ nsXMLHttpRequest::RequestCompleted()
ChangeState(XML_HTTP_REQUEST_COMPLETED);
nsCOMPtr<nsIJSContextStack> stack;
JSContext *cx = nsnull;
if (mScriptContext) {
stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
if (stack) {
cx = (JSContext *)mScriptContext->GetNativeContext();
if (cx) {
stack->Push(cx);
}
}
}
if (mOnLoadListener) {
mOnLoadListener->HandleEvent(domevent);
}
if (mLoadEventListeners) {
PRUint32 index, count;
mLoadEventListeners->Count(&count);
for (index = 0; index < count; index++) {
nsCOMPtr<nsIDOMEventListener> listener;
mLoadEventListeners->QueryElementAt(index,
NS_GET_IID(nsIDOMEventListener),
getter_AddRefs(listener));
if (listener) {
listener->HandleEvent(domevent);
}
}
}
NotifyEventListeners(mOnLoadListener, mLoadEventListeners, domevent);
ClearEventListeners();
if (cx) {
stack->Pop(&cx);
}
return rv;
}
@ -1154,7 +1180,7 @@ NS_IMETHODIMP
nsXMLHttpRequest::Send(nsIVariant *aBody)
{
nsresult rv;
// Return error if we're already processing a request
if (XML_HTTP_REQUEST_SENT & mState) {
return NS_ERROR_FAILURE;
@ -1362,7 +1388,7 @@ nsXMLHttpRequest::SetRequestHeader(const char *header, const char *value)
nsDependentCString(value),
PR_FALSE);
}
return NS_OK;
}
@ -1398,6 +1424,7 @@ nsXMLHttpRequest::OverrideMimeType(const char* aMimeType)
return NS_OK;
}
// nsIDOMEventListener
nsresult
nsXMLHttpRequest::HandleEvent(nsIDOMEvent* aEvent)
@ -1446,8 +1473,6 @@ nsXMLHttpRequest::Abort(nsIDOMEvent* aEvent)
{
Abort();
ChangeState(XML_HTTP_REQUEST_UNINITIALIZED);
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
return NS_OK;
@ -1456,53 +1481,21 @@ nsXMLHttpRequest::Abort(nsIDOMEvent* aEvent)
nsresult
nsXMLHttpRequest::Error(nsIDOMEvent* aEvent)
{
// We need to create the event before nulling out mDocument
nsCOMPtr<nsIDOMEvent> event(do_QueryInterface(aEvent));
if (!event) {
// There is no NS_PAGE_ERROR event but NS_SCRIPT_ERROR should be ok.
CreateEvent(NS_SCRIPT_ERROR, getter_AddRefs(event));
}
mDocument = nsnull;
ChangeState(XML_HTTP_REQUEST_UNINITIALIZED);
ChangeState(XML_HTTP_REQUEST_COMPLETED);
mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
nsCOMPtr<nsIJSContextStack> stack;
JSContext *cx = nsnull;
if (mScriptContext) {
stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
if (stack) {
cx = (JSContext *)mScriptContext->GetNativeContext();
if (cx) {
stack->Push(cx);
}
}
}
if (mOnErrorListener) {
mOnErrorListener->HandleEvent(aEvent);
}
if (mErrorEventListeners) {
PRUint32 index, count;
mErrorEventListeners->Count(&count);
for (index = 0; index < count; index++) {
nsCOMPtr<nsIDOMEventListener> listener;
mErrorEventListeners->QueryElementAt(index,
NS_GET_IID(nsIDOMEventListener),
getter_AddRefs(listener));
if (listener) {
listener->HandleEvent(aEvent);
}
}
}
NotifyEventListeners(mOnErrorListener, mErrorEventListeners, event);
ClearEventListeners();
if (cx) {
stack->Pop(&cx);
}
return NS_OK;
}
@ -1630,4 +1623,3 @@ nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value)
mHeaders.Append('\n');
return NS_OK;
}

Просмотреть файл

@ -123,6 +123,8 @@ protected:
nsresult RequestCompleted();
nsresult GetLoadGroup(nsILoadGroup **aLoadGroup);
nsresult GetBaseURI(nsIURI **aBaseURI);
nsresult CreateEvent(PRUint32 msg, nsIDOMEvent** domevent);
void NotifyEventListeners(nsIDOMEventListener* aHandler, nsISupportsArray* aListeners, nsIDOMEvent* aEvent);
void ClearEventListeners();
nsCOMPtr<nsISupports> mContext;