Back to index

lightning-sunbird  0.9+nobinonly
nsXMLHttpRequest.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsXMLHttpRequest.h"
00039 #include "nsISimpleEnumerator.h"
00040 #include "nsIXPConnect.h"
00041 #include "nsIUnicodeEncoder.h"
00042 #include "nsIServiceManager.h"
00043 #include "nsICharsetConverterManager.h"
00044 #include "nsLayoutCID.h"
00045 #include "nsIDOMDOMImplementation.h"
00046 #include "nsIPrivateDOMImplementation.h"
00047 #include "nsXPIDLString.h"
00048 #include "nsReadableUtils.h"
00049 #include "nsCRT.h"
00050 #include "nsIURI.h"
00051 #include "nsILoadGroup.h"
00052 #include "nsNetUtil.h"
00053 #include "nsIUploadChannel.h"
00054 #include "nsIDOMSerializer.h"
00055 #include "nsXPCOM.h"
00056 #include "nsISupportsPrimitives.h"
00057 #include "nsIDOMEventReceiver.h"
00058 #include "nsIEventListenerManager.h"
00059 #include "nsGUIEvent.h"
00060 #include "nsIPrivateDOMEvent.h"
00061 #include "prprf.h"
00062 #include "nsIDOMEventListener.h"
00063 #include "nsIJSContextStack.h"
00064 #include "nsIScriptSecurityManager.h"
00065 #include "nsWeakPtr.h"
00066 #include "nsICharsetAlias.h"
00067 #include "nsIScriptGlobalObject.h"
00068 #include "nsIDOMClassInfo.h"
00069 #include "nsIDOMElement.h"
00070 #include "nsIDOMWindow.h"
00071 #include "nsIVariant.h"
00072 #include "nsIParser.h"
00073 #include "nsLoadListenerProxy.h"
00074 #include "nsIWindowWatcher.h"
00075 #include "nsIAuthPrompt.h"
00076 #include "nsIStringStream.h"
00077 #include "nsIStreamConverterService.h"
00078 #include "nsICachingChannel.h"
00079 #include "nsContentUtils.h"
00080 #include "nsCOMArray.h"
00081 #include "nsDOMClassInfo.h"
00082 #include "nsIContentPolicy.h"
00083 #include "nsContentPolicyUtils.h"
00084 #include "nsContentErrors.h"
00085 #include "nsLayoutStatics.h"
00086 #include "nsIScriptObjectPrincipal.h"
00087 
00088 static const char* kLoadAsData = "loadAsData";
00089 #define LOADSTR NS_LITERAL_STRING("load")
00090 #define ERRORSTR NS_LITERAL_STRING("error")
00091 
00092 // CIDs
00093 static NS_DEFINE_CID(kCharsetAliasCID, NS_CHARSETALIAS_CID);
00094 static NS_DEFINE_CID(kIDOMDOMImplementationCID, NS_DOM_IMPLEMENTATION_CID);
00095 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
00096 static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
00097 
00098 // State
00099 #define XML_HTTP_REQUEST_UNINITIALIZED  (1 << 0)  // 0
00100 #define XML_HTTP_REQUEST_OPENED         (1 << 1)  // 1 aka LOADING
00101 #define XML_HTTP_REQUEST_LOADED         (1 << 2)  // 2
00102 #define XML_HTTP_REQUEST_INTERACTIVE    (1 << 3)  // 3
00103 #define XML_HTTP_REQUEST_COMPLETED      (1 << 4)  // 4
00104 #define XML_HTTP_REQUEST_SENT           (1 << 5)  // Internal, LOADING in IE and external view
00105 #define XML_HTTP_REQUEST_STOPPED        (1 << 6)  // Internal, INTERACTIVE in IE and external view
00106 // The above states are mutually exclusive, change with ChangeState() only.
00107 // The states below can be combined.
00108 #define XML_HTTP_REQUEST_ABORTED        (1 << 7)  // Internal
00109 #define XML_HTTP_REQUEST_ASYNC          (1 << 8)  // Internal
00110 #define XML_HTTP_REQUEST_PARSEBODY      (1 << 9)  // Internal
00111 #define XML_HTTP_REQUEST_XSITEENABLED   (1 << 10) // Internal
00112 #define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 11) // Internal
00113 #define XML_HTTP_REQUEST_MULTIPART      (1 << 12) // Internal
00114 #define XML_HTTP_REQUEST_ROOTED         (1 << 13) // Internal
00115 
00116 #define XML_HTTP_REQUEST_LOADSTATES         \
00117   (XML_HTTP_REQUEST_UNINITIALIZED |         \
00118    XML_HTTP_REQUEST_OPENED |                \
00119    XML_HTTP_REQUEST_LOADED |                \
00120    XML_HTTP_REQUEST_INTERACTIVE |           \
00121    XML_HTTP_REQUEST_COMPLETED |             \
00122    XML_HTTP_REQUEST_SENT |                  \
00123    XML_HTTP_REQUEST_STOPPED)
00124 
00125 // This helper function adds the given load flags to the request's existing
00126 // load flags.
00127 static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
00128 {
00129   nsLoadFlags flags;
00130   request->GetLoadFlags(&flags);
00131   flags |= newFlags;
00132   request->SetLoadFlags(flags);
00133 }
00134 
00135 // Helper proxy class to be used when expecting an
00136 // multipart/x-mixed-replace stream of XML documents.
00137 
00138 class nsMultipartProxyListener : public nsIStreamListener
00139 {
00140 public:
00141   nsMultipartProxyListener(nsIStreamListener *dest);
00142   virtual ~nsMultipartProxyListener();
00143 
00144   /* additional members */
00145   NS_DECL_ISUPPORTS
00146   NS_DECL_NSISTREAMLISTENER
00147   NS_DECL_NSIREQUESTOBSERVER
00148 
00149 private:
00150   nsCOMPtr<nsIStreamListener> mDestListener;
00151 };
00152 
00153 
00154 nsMultipartProxyListener::nsMultipartProxyListener(nsIStreamListener *dest)
00155   : mDestListener(dest)
00156 {
00157 }
00158 
00159 nsMultipartProxyListener::~nsMultipartProxyListener()
00160 {
00161 }
00162 
00163 NS_IMPL_ISUPPORTS2(nsMultipartProxyListener, nsIStreamListener,
00164                    nsIRequestObserver)
00165 
00166 
00168 NS_IMETHODIMP
00169 nsMultipartProxyListener::OnStartRequest(nsIRequest *aRequest,
00170                                          nsISupports *ctxt)
00171 {
00172   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
00173   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
00174 
00175   nsCAutoString contentType;
00176   nsresult rv = channel->GetContentType(contentType);
00177 
00178   if (!contentType.EqualsLiteral("multipart/x-mixed-replace")) {
00179     return NS_ERROR_INVALID_ARG;
00180   }
00181 
00182   // If multipart/x-mixed-replace content, we'll insert a MIME
00183   // decoder in the pipeline to handle the content and pass it along
00184   // to our original listener.
00185 
00186   nsCOMPtr<nsIStreamConverterService> convServ =
00187     do_GetService("@mozilla.org/streamConverters;1", &rv);
00188   if (NS_SUCCEEDED(rv)) {
00189     nsCOMPtr<nsIStreamListener> toListener(mDestListener);
00190     nsCOMPtr<nsIStreamListener> fromListener;
00191 
00192     rv = convServ->AsyncConvertData("multipart/x-mixed-replace",
00193                                     "*/*",
00194                                     toListener,
00195                                     nsnull,
00196                                     getter_AddRefs(fromListener));
00197     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && fromListener, NS_ERROR_UNEXPECTED);
00198 
00199     mDestListener = fromListener;
00200   }
00201 
00202   return mDestListener->OnStartRequest(aRequest, ctxt);
00203 }
00204 
00205 NS_IMETHODIMP
00206 nsMultipartProxyListener::OnStopRequest(nsIRequest *aRequest,
00207                                         nsISupports *ctxt,
00208                                         nsresult status)
00209 {
00210   return mDestListener->OnStopRequest(aRequest, ctxt, status);
00211 }
00212 
00215 NS_IMETHODIMP
00216 nsMultipartProxyListener::OnDataAvailable(nsIRequest *aRequest,
00217                                           nsISupports *ctxt,
00218                                           nsIInputStream *inStr,
00219                                           PRUint32 sourceOffset,
00220                                           PRUint32 count)
00221 {
00222   return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset,
00223                                         count);
00224 }
00225 
00226 nsresult
00227 nsXMLHttpRequest::Init()
00228 {
00229   // Set the original mScriptContext and mPrincipal, if available.
00230   // Get JSContext from stack.
00231   nsCOMPtr<nsIJSContextStack> stack =
00232     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
00233 
00234   if (!stack) {
00235     return NS_OK;
00236   }
00237 
00238   JSContext *cx;
00239 
00240   if (NS_FAILED(stack->Peek(&cx)) || !cx) {
00241     return NS_OK;
00242   }
00243 
00244   nsIScriptContext* context = GetScriptContextFromJSContext(cx);
00245   if (!context) {
00246     return NS_OK;
00247   }
00248   nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
00249   nsCOMPtr<nsIPrincipal> subjectPrincipal;
00250   if (secMan) {
00251     secMan->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
00252   }
00253   NS_ENSURE_STATE(subjectPrincipal);
00254 
00255   mScriptContext = context;
00256   mPrincipal = subjectPrincipal;
00257   nsCOMPtr<nsPIDOMWindow> window =
00258     do_QueryInterface(context->GetGlobalObject());
00259   if (window) {
00260     mOwner = do_GetWeakReference(window->GetCurrentInnerWindow());
00261   }
00262 
00263   return NS_OK;
00264 }
00265 
00266 NS_IMETHODIMP
00267 nsXMLHttpRequest::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
00268                              PRUint32 argc, jsval *argv)
00269 {
00270   mOwner = do_GetWeakReference(aOwner);
00271   if (!mOwner) {
00272     NS_WARNING("Unexpected nsIJSNativeInitializer owner");
00273     return NS_OK;
00274   }
00275 
00276   // This XHR object is bound to a |window|,
00277   // so re-set principal and script context.
00278   nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = do_QueryInterface(aOwner);
00279   NS_ENSURE_STATE(scriptPrincipal);
00280   mPrincipal = scriptPrincipal->GetPrincipal();
00281   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aOwner);
00282   NS_ENSURE_STATE(sgo);
00283   mScriptContext = sgo->GetContext();
00284   NS_ENSURE_STATE(mScriptContext);
00285   return NS_OK; 
00286 }
00287 
00295 static already_AddRefed<nsIDocument>
00296 GetDocumentFromScriptContext(nsIScriptContext *aScriptContext)
00297 {
00298   if (!aScriptContext)
00299     return nsnull;
00300 
00301   nsCOMPtr<nsIDOMWindow> window =
00302     do_QueryInterface(aScriptContext->GetGlobalObject());
00303   nsIDocument *doc = nsnull;
00304   if (window) {
00305     nsCOMPtr<nsIDOMDocument> domdoc;
00306     window->GetDocument(getter_AddRefs(domdoc));
00307     if (domdoc) {
00308       CallQueryInterface(domdoc, &doc);
00309     }
00310   }
00311   return doc;
00312 }
00313 
00315 //
00316 //
00318 
00319 nsXMLHttpRequest::nsXMLHttpRequest()
00320   : mState(XML_HTTP_REQUEST_UNINITIALIZED)
00321 {
00322   nsLayoutStatics::AddRef();
00323 }
00324 
00325 nsXMLHttpRequest::~nsXMLHttpRequest()
00326 {
00327   if (mState & (XML_HTTP_REQUEST_STOPPED |
00328                 XML_HTTP_REQUEST_SENT |
00329                 XML_HTTP_REQUEST_INTERACTIVE)) {
00330     Abort();
00331   }
00332 
00333   NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
00334   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
00335 
00336   // Needed to free the two arrays.
00337   ClearEventListeners();
00338   nsLayoutStatics::Release();
00339 }
00340 
00341 
00342 // QueryInterface implementation for nsXMLHttpRequest
00343 NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequest)
00344   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXMLHttpRequest)
00345   NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
00346   NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
00347   NS_INTERFACE_MAP_ENTRY(nsIDOMLoadListener)
00348   NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
00349   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
00350   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
00351   NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
00352   NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
00353   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
00354   NS_INTERFACE_MAP_ENTRY(nsIDOMGCParticipant)
00355   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00356   NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer_MOZILLA_1_8_BRANCH)
00357   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XMLHttpRequest)
00358 NS_INTERFACE_MAP_END
00359 
00360 
00361 NS_IMPL_ADDREF(nsXMLHttpRequest)
00362 NS_IMPL_RELEASE(nsXMLHttpRequest)
00363 
00364 
00365 /* void addEventListener (in string type, in nsIDOMEventListener
00366    listener); */
00367 NS_IMETHODIMP
00368 nsXMLHttpRequest::AddEventListener(const nsAString& type,
00369                                    nsIDOMEventListener *listener,
00370                                    PRBool useCapture)
00371 {
00372   NS_ENSURE_ARG(listener);
00373 
00374   nsTArray<ListenerHolder*> *array;
00375   if (type.Equals(LOADSTR)) {
00376     array = &mLoadEventListeners;
00377   }
00378   else if (type.Equals(ERRORSTR)) {
00379     array = &mErrorEventListeners;
00380   }
00381   else {
00382     return NS_ERROR_INVALID_ARG;
00383   }
00384 
00385   ListenerHolder *holder = new ListenerHolder;
00386   NS_ENSURE_TRUE(holder, NS_ERROR_OUT_OF_MEMORY);
00387   holder->Set(listener, this);
00388   array->AppendElement(holder);
00389 
00390   return NS_OK;
00391 }
00392 
00393 /* void removeEventListener (in string type, in nsIDOMEventListener
00394    listener); */
00395 NS_IMETHODIMP
00396 nsXMLHttpRequest::RemoveEventListener(const nsAString & type,
00397                                       nsIDOMEventListener *listener,
00398                                       PRBool useCapture)
00399 {
00400   NS_ENSURE_ARG(listener);
00401 
00402   nsTArray<ListenerHolder*> *array;
00403   if (type.Equals(LOADSTR)) {
00404     array = &mLoadEventListeners;
00405   }
00406   else if (type.Equals(ERRORSTR)) {
00407     array = &mLoadEventListeners;
00408   }
00409   else {
00410     return NS_ERROR_INVALID_ARG;
00411   }
00412 
00413   // Allow a caller to remove O(N^2) behavior by removing end-to-start.
00414   for (PRUint32 i = array->Length() - 1; i != PRUint32(-1); --i) {
00415     ListenerHolder *holder = array->ElementAt(i);
00416     if (nsCOMPtr<nsIDOMEventListener>(holder->Get()) == listener) {
00417       array->RemoveElementAt(i);
00418       delete holder;
00419       break;
00420     }
00421   }
00422 
00423   return NS_OK;
00424 }
00425 
00426 /* boolean dispatchEvent (in nsIDOMEvent evt); */
00427 NS_IMETHODIMP
00428 nsXMLHttpRequest::DispatchEvent(nsIDOMEvent *evt, PRBool *_retval)
00429 {
00430   // Ignored
00431 
00432   return NS_OK;
00433 }
00434 
00435 /* attribute nsIOnReadyStateChangeHandler onreadystatechange; */
00436 NS_IMETHODIMP
00437 nsXMLHttpRequest::GetOnreadystatechange(nsIOnReadyStateChangeHandler * *aOnreadystatechange)
00438 {
00439   NS_ENSURE_ARG_POINTER(aOnreadystatechange);
00440 
00441   mOnReadystatechangeListener.Get(aOnreadystatechange);
00442 
00443   return NS_OK;
00444 }
00445 
00446 NS_IMETHODIMP
00447 nsXMLHttpRequest::SetOnreadystatechange(nsIOnReadyStateChangeHandler * aOnreadystatechange)
00448 {
00449   mOnReadystatechangeListener.Set(aOnreadystatechange, this);
00450 
00451   return NS_OK;
00452 }
00453 
00454 
00455 /* attribute nsIDOMEventListener onload; */
00456 NS_IMETHODIMP
00457 nsXMLHttpRequest::GetOnload(nsIDOMEventListener * *aOnLoad)
00458 {
00459   NS_ENSURE_ARG_POINTER(aOnLoad);
00460 
00461   mOnLoadListener.Get(aOnLoad);
00462 
00463   return NS_OK;
00464 }
00465 
00466 NS_IMETHODIMP
00467 nsXMLHttpRequest::SetOnload(nsIDOMEventListener * aOnLoad)
00468 {
00469   mOnLoadListener.Set(aOnLoad, this);
00470 
00471   return NS_OK;
00472 }
00473 
00474 /* attribute nsIDOMEventListener onerror; */
00475 NS_IMETHODIMP
00476 nsXMLHttpRequest::GetOnerror(nsIDOMEventListener * *aOnerror)
00477 {
00478   NS_ENSURE_ARG_POINTER(aOnerror);
00479 
00480   mOnErrorListener.Get(aOnerror);
00481 
00482   return NS_OK;
00483 }
00484 
00485 NS_IMETHODIMP
00486 nsXMLHttpRequest::SetOnerror(nsIDOMEventListener * aOnerror)
00487 {
00488   mOnErrorListener.Set(aOnerror, this);
00489 
00490   return NS_OK;
00491 }
00492 
00493 /* attribute nsIDOMEventListener onprogress; */
00494 NS_IMETHODIMP
00495 nsXMLHttpRequest::GetOnprogress(nsIDOMEventListener * *aOnprogress)
00496 {
00497   NS_ENSURE_ARG_POINTER(aOnprogress);
00498 
00499   mOnProgressListener.Get(aOnprogress);
00500 
00501   return NS_OK;
00502 }
00503 
00504 NS_IMETHODIMP
00505 nsXMLHttpRequest::SetOnprogress(nsIDOMEventListener * aOnprogress)
00506 {
00507   mOnProgressListener.Set(aOnprogress, this);
00508 
00509   return NS_OK;
00510 }
00511 
00512 /* readonly attribute nsIChannel channel; */
00513 NS_IMETHODIMP
00514 nsXMLHttpRequest::GetChannel(nsIChannel **aChannel)
00515 {
00516   NS_ENSURE_ARG_POINTER(aChannel);
00517   *aChannel = mChannel;
00518   NS_IF_ADDREF(*aChannel);
00519 
00520   return NS_OK;
00521 }
00522 
00523 /* readonly attribute nsIDOMDocument responseXML; */
00524 NS_IMETHODIMP
00525 nsXMLHttpRequest::GetResponseXML(nsIDOMDocument **aResponseXML)
00526 {
00527   NS_ENSURE_ARG_POINTER(aResponseXML);
00528   *aResponseXML = nsnull;
00529   if ((XML_HTTP_REQUEST_COMPLETED & mState) && mDocument) {
00530     *aResponseXML = mDocument;
00531     NS_ADDREF(*aResponseXML);
00532   }
00533 
00534   return NS_OK;
00535 }
00536 
00537 /*
00538  * This piece copied from nsXMLDocument, we try to get the charset
00539  * from HTTP headers.
00540  */
00541 nsresult
00542 nsXMLHttpRequest::DetectCharset(nsACString& aCharset)
00543 {
00544   aCharset.Truncate();
00545   nsresult rv;
00546   nsCAutoString charsetVal;
00547   nsCOMPtr<nsIChannel> channel(do_QueryInterface(mReadRequest));
00548   if (!channel) {
00549     channel = mChannel;
00550     if (!channel) {
00551       // There will be no mChannel when we got a necko error in
00552       // OnStopRequest or if we were never sent.
00553       return NS_ERROR_NOT_AVAILABLE;
00554     }
00555   }
00556 
00557   rv = channel->GetContentCharset(charsetVal);
00558   if (NS_SUCCEEDED(rv)) {
00559     nsCOMPtr<nsICharsetAlias> calias(do_GetService(kCharsetAliasCID,&rv));
00560     if(NS_SUCCEEDED(rv) && calias) {
00561       rv = calias->GetPreferred(charsetVal, aCharset);
00562     }
00563   }
00564   return rv;
00565 }
00566 
00567 nsresult
00568 nsXMLHttpRequest::ConvertBodyToText(nsAString& aOutBuffer)
00569 {
00570   // This code here is basically a copy of a similar thing in
00571   // nsScanner::Append(const char* aBuffer, PRUint32 aLen).
00572   // If we get illegal characters in the input we replace
00573   // them and don't just fail.
00574 
00575   PRInt32 dataLen = mResponseBody.Length();
00576   if (!dataLen)
00577     return NS_OK;
00578 
00579   nsresult rv = NS_OK;
00580 
00581   nsCAutoString dataCharset;
00582   nsCOMPtr<nsIDocument> document(do_QueryInterface(mDocument));
00583   if (document) {
00584     dataCharset = document->GetDocumentCharacterSet();
00585   } else {
00586     if (NS_FAILED(DetectCharset(dataCharset)) || dataCharset.IsEmpty()) {
00587       // MS documentation states UTF-8 is default for responseText
00588       dataCharset.AssignLiteral("UTF-8");
00589     }
00590   }
00591 
00592   if (dataCharset.EqualsLiteral("ASCII")) {
00593     CopyASCIItoUTF16(mResponseBody, aOutBuffer);
00594 
00595     return NS_OK;
00596   }
00597 
00598   nsCOMPtr<nsICharsetConverterManager> ccm =
00599     do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
00600   if (NS_FAILED(rv))
00601     return rv;
00602 
00603   nsCOMPtr<nsIUnicodeDecoder> decoder;
00604   rv = ccm->GetUnicodeDecoderRaw(dataCharset.get(),
00605                                  getter_AddRefs(decoder));
00606   if (NS_FAILED(rv))
00607     return rv;
00608 
00609   const char * inBuffer = mResponseBody.get();
00610   PRInt32 outBufferLength;
00611   rv = decoder->GetMaxLength(inBuffer, dataLen, &outBufferLength);
00612   if (NS_FAILED(rv))
00613     return rv;
00614 
00615   PRUnichar * outBuffer =
00616     NS_STATIC_CAST(PRUnichar*, nsMemory::Alloc((outBufferLength + 1) *
00617                                                sizeof(PRUnichar)));
00618   if (!outBuffer) {
00619     return NS_ERROR_OUT_OF_MEMORY;
00620   }
00621 
00622   PRInt32 totalChars = 0,
00623           outBufferIndex = 0,
00624           outLen = outBufferLength;
00625 
00626   do {
00627     PRInt32 inBufferLength = dataLen;
00628     rv = decoder->Convert(inBuffer,
00629                           &inBufferLength,
00630                           &outBuffer[outBufferIndex],
00631                           &outLen);
00632     totalChars += outLen;
00633     if (NS_FAILED(rv)) {
00634       // We consume one byte, replace it with U+FFFD
00635       // and try the conversion again.
00636       outBuffer[outBufferIndex + outLen++] = (PRUnichar)0xFFFD;
00637       outBufferIndex += outLen;
00638       outLen = outBufferLength - (++totalChars);
00639 
00640       decoder->Reset();
00641 
00642       if((inBufferLength + 1) > dataLen) {
00643         inBufferLength = dataLen;
00644       } else {
00645         inBufferLength++;
00646       }
00647 
00648       inBuffer = &inBuffer[inBufferLength];
00649       dataLen -= inBufferLength;
00650     }
00651   } while ( NS_FAILED(rv) && (dataLen > 0) );
00652 
00653   aOutBuffer.Assign(outBuffer, totalChars);
00654   nsMemory::Free(outBuffer);
00655 
00656   return NS_OK;
00657 }
00658 
00659 /* readonly attribute AString responseText; */
00660 NS_IMETHODIMP nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
00661 {
00662   nsresult rv = NS_OK;
00663 
00664   aResponseText.Truncate();
00665 
00666   if (mState & (XML_HTTP_REQUEST_COMPLETED |
00667                 XML_HTTP_REQUEST_INTERACTIVE)) {
00668     rv = ConvertBodyToText(aResponseText);
00669   }
00670 
00671   return rv;
00672 }
00673 
00674 /* readonly attribute unsigned long status; */
00675 NS_IMETHODIMP
00676 nsXMLHttpRequest::GetStatus(PRUint32 *aStatus)
00677 {
00678   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
00679 
00680   if (httpChannel) {
00681     return httpChannel->GetResponseStatus(aStatus);
00682   }
00683   *aStatus = 0;
00684 
00685   return NS_OK;
00686 }
00687 
00688 /* readonly attribute AUTF8String statusText; */
00689 NS_IMETHODIMP
00690 nsXMLHttpRequest::GetStatusText(nsACString& aStatusText)
00691 {
00692   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
00693 
00694   aStatusText.Truncate();
00695 
00696   nsresult rv = NS_OK;
00697 
00698   if (httpChannel) {
00699     rv = httpChannel->GetResponseStatusText(aStatusText);
00700   }
00701 
00702   return rv;
00703 }
00704 
00705 /* void abort (); */
00706 NS_IMETHODIMP
00707 nsXMLHttpRequest::Abort()
00708 {
00709   if (mReadRequest) {
00710     mReadRequest->Cancel(NS_BINDING_ABORTED);
00711   }
00712   if (mChannel) {
00713     mChannel->Cancel(NS_BINDING_ABORTED);
00714   }
00715   mDocument = nsnull;
00716   mState |= XML_HTTP_REQUEST_ABORTED;
00717 
00718   ChangeState(XML_HTTP_REQUEST_COMPLETED, PR_TRUE, PR_TRUE);
00719 
00720   ChangeState(XML_HTTP_REQUEST_UNINITIALIZED, PR_FALSE);  // IE seems to do it
00721 
00722   return NS_OK;
00723 }
00724 
00725 /* string getAllResponseHeaders (); */
00726 NS_IMETHODIMP
00727 nsXMLHttpRequest::GetAllResponseHeaders(char **_retval)
00728 {
00729   NS_ENSURE_ARG_POINTER(_retval);
00730   *_retval = nsnull;
00731 
00732   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
00733 
00734   if (httpChannel) {
00735     nsHeaderVisitor *visitor = nsnull;
00736     NS_NEWXPCOM(visitor, nsHeaderVisitor);
00737     if (!visitor)
00738       return NS_ERROR_OUT_OF_MEMORY;
00739     NS_ADDREF(visitor);
00740 
00741     nsresult rv = httpChannel->VisitResponseHeaders(visitor);
00742     if (NS_SUCCEEDED(rv))
00743       *_retval = ToNewCString(visitor->Headers());
00744 
00745     NS_RELEASE(visitor);
00746     return rv;
00747   }
00748 
00749   return NS_OK;
00750 }
00751 
00752 /* ACString getResponseHeader (in AUTF8String header); */
00753 NS_IMETHODIMP
00754 nsXMLHttpRequest::GetResponseHeader(const nsACString& header,
00755                                     nsACString& _retval)
00756 {
00757   nsresult rv = NS_OK;
00758   _retval.Truncate();
00759 
00760   nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
00761 
00762   if (httpChannel) {
00763     rv = httpChannel->GetResponseHeader(header, _retval);
00764   }
00765 
00766   if (rv == NS_ERROR_NOT_AVAILABLE) {
00767     // Means no header
00768     _retval.SetIsVoid(PR_TRUE);
00769     rv = NS_OK;
00770   }
00771 
00772   return rv;
00773 }
00774 
00775 nsresult
00776 nsXMLHttpRequest::GetLoadGroup(nsILoadGroup **aLoadGroup)
00777 {
00778   NS_ENSURE_ARG_POINTER(aLoadGroup);
00779   *aLoadGroup = nsnull;
00780 
00781   nsCOMPtr<nsIDocument> doc = GetDocumentFromScriptContext(mScriptContext);
00782   if (doc) {
00783     *aLoadGroup = doc->GetDocumentLoadGroup().get();  // already_AddRefed
00784   }
00785 
00786   return NS_OK;
00787 }
00788 
00789 nsIURI *
00790 nsXMLHttpRequest::GetBaseURI()
00791 {
00792   if (!mScriptContext) {
00793     return nsnull;
00794   }
00795 
00796   nsCOMPtr<nsIDocument> doc = GetDocumentFromScriptContext(mScriptContext);
00797   if (!doc) {
00798     return nsnull;
00799   }
00800 
00801   return doc->GetBaseURI();
00802 }
00803 
00804 nsresult
00805 nsXMLHttpRequest::CreateEvent(nsEvent* aEvent, nsIDOMEvent** aDOMEvent)
00806 {
00807   nsresult rv;
00808 
00809   nsCOMPtr<nsIDOMEventReceiver> receiver(do_QueryInterface(mDocument));
00810   if (!receiver) {
00811     return NS_ERROR_FAILURE;
00812   }
00813 
00814   nsCOMPtr<nsIEventListenerManager> manager;
00815   rv = receiver->GetListenerManager(getter_AddRefs(manager));
00816   if (!manager) {
00817     return NS_ERROR_FAILURE;
00818   }
00819 
00820   rv = manager->CreateEvent(nsnull, aEvent,
00821                             NS_LITERAL_STRING("HTMLEvents"),
00822                             aDOMEvent);
00823   if (NS_FAILED(rv)) {
00824     return rv;
00825   }
00826 
00827   nsCOMPtr<nsIPrivateDOMEvent> privevent(do_QueryInterface(*aDOMEvent));
00828   if (!privevent) {
00829     NS_IF_RELEASE(*aDOMEvent);
00830     return NS_ERROR_FAILURE;
00831   }
00832   privevent->SetTarget(this);
00833   privevent->SetCurrentTarget(this);
00834   privevent->SetOriginalTarget(this);
00835 
00836   // We're not marking these events trusted as there is no way for us
00837   // to tell if we're called through the code that normally calls us
00838   // (i.e. necko) or if script on a webpage QI'd this object to say,
00839   // nsIProgressEventSink and called onProgress(...).
00840   //
00841   // privevent->SetTrusted(?);
00842 
00843   return NS_OK;
00844 }
00845 
00846 void
00847 nsXMLHttpRequest::NotifyEventListeners(nsIDOMEventListener* aHandler,
00848                                        const nsCOMArray<nsIDOMEventListener>* aListeners,
00849                                        nsIDOMEvent* aEvent)
00850 {
00851   if (!aEvent)
00852     return;
00853 
00854   nsCOMPtr<nsIJSContextStack> stack;
00855   JSContext *cx = nsnull;
00856 
00857   if (NS_FAILED(CheckInnerWindowCorrectness())) {
00858     return;
00859   }
00860 
00861   if (mScriptContext) {
00862     stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
00863 
00864     if (stack) {
00865       cx = (JSContext *)mScriptContext->GetNativeContext();
00866 
00867       if (cx) {
00868         stack->Push(cx);
00869       }
00870     }
00871   }
00872 
00873   if (aHandler) {
00874     aHandler->HandleEvent(aEvent);
00875   }
00876 
00877   if (aListeners) {
00878     for (PRInt32 i = 0, i_end = aListeners->Count(); i < i_end; ++i) {
00879       nsIDOMEventListener *listener = aListeners->ObjectAt(i);
00880       if (listener) {
00881         listener->HandleEvent(aEvent);
00882       }
00883     }
00884   }
00885 
00886   if (cx) {
00887     stack->Pop(&cx);
00888   }
00889 }
00890 
00891 void
00892 nsXMLHttpRequest::ClearEventListeners()
00893 {
00894   if (mState & XML_HTTP_REQUEST_ROOTED) {
00895     nsDOMClassInfo::UnsetExternallyReferenced(this);
00896     mState &= ~XML_HTTP_REQUEST_ROOTED;
00897   }
00898 
00899   // This isn't *really* needed anymore now that we use
00900   // nsMarkedJSFunctionHolder, but we may as well keep it for safety
00901   // (against leaks) and compatibility, and also for the code to clear
00902   // the first two arrays (called from the destructor).
00903   PRUint32 i, i_end;
00904   for (i = 0, i_end = mLoadEventListeners.Length(); i < i_end; ++i)
00905     delete mLoadEventListeners[i];
00906   mLoadEventListeners.Clear();
00907   for (i = 0, i_end = mErrorEventListeners.Length(); i < i_end; ++i)
00908     delete mErrorEventListeners[i];
00909   mErrorEventListeners.Clear();
00910 
00911   mOnLoadListener.Set(nsnull, this);
00912   mOnErrorListener.Set(nsnull, this);
00913   mOnReadystatechangeListener.Set(nsnull, this);
00914   mOnProgressListener.Set(nsnull, this);
00915 }
00916 
00917 already_AddRefed<nsIHttpChannel>
00918 nsXMLHttpRequest::GetCurrentHttpChannel()
00919 {
00920   nsIHttpChannel *httpChannel = nsnull;
00921 
00922   if (mReadRequest) {
00923     CallQueryInterface(mReadRequest, &httpChannel);
00924   }
00925 
00926   if (!httpChannel && mChannel) {
00927     CallQueryInterface(mChannel, &httpChannel);
00928   }
00929 
00930   return httpChannel;
00931 }
00932 
00933 inline PRBool
00934 IsSystemPrincipal(nsIPrincipal* aPrincipal)
00935 {
00936   nsCOMPtr<nsIPrincipal> systemPrincipal;
00937   nsresult rv = nsContentUtils::GetSecurityManager()->
00938                   GetSystemPrincipal(getter_AddRefs(systemPrincipal));
00939   if (NS_FAILED(rv))
00940     return PR_FALSE;
00941 
00942   return (aPrincipal == systemPrincipal);
00943 }
00944 
00945 
00946 /* noscript void openRequest (in AUTF8String method, in AUTF8String url, in boolean async, in AString user, in AString password); */
00947 NS_IMETHODIMP
00948 nsXMLHttpRequest::OpenRequest(const nsACString& method,
00949                               const nsACString& url,
00950                               PRBool async,
00951                               const nsAString& user,
00952                               const nsAString& password)
00953 {
00954   NS_ENSURE_ARG(!method.IsEmpty());
00955   NS_ENSURE_ARG(!url.IsEmpty());
00956 
00957   // Disallow HTTP/1.1 TRACE method (see bug 302489)
00958   // and MS IIS equivalent TRACK (see bug 381264)
00959   if (method.LowerCaseEqualsASCII("trace") ||
00960       method.LowerCaseEqualsASCII("track")) {
00961     return NS_ERROR_INVALID_ARG;
00962   }
00963 
00964   nsresult rv;
00965   nsCOMPtr<nsIURI> uri;
00966   PRBool authp = PR_FALSE;
00967 
00968   if (mState & XML_HTTP_REQUEST_ABORTED) {
00969     // Something caused this request to abort (e.g the current request
00970     // was caceled, channels closed etc), most likely the abort()
00971     // function was called by script. Unset our aborted state, and
00972     // proceed as normal
00973 
00974     mState &= ~XML_HTTP_REQUEST_ABORTED;
00975   } else if (mState & (XML_HTTP_REQUEST_OPENED |
00976                        XML_HTTP_REQUEST_LOADED |
00977                        XML_HTTP_REQUEST_INTERACTIVE |
00978                        XML_HTTP_REQUEST_SENT |
00979                        XML_HTTP_REQUEST_STOPPED)) {
00980     // IE aborts as well
00981     Abort();
00982 
00983     // XXX We should probably send a warning to the JS console
00984     //     that load was aborted and event listeners were cleared
00985     //     since this looks like a situation that could happen
00986     //     by accident and you could spend a lot of time wondering
00987     //     why things didn't work.
00988 
00989     return NS_OK;
00990   }
00991 
00992   if (async) {
00993     mState |= XML_HTTP_REQUEST_ASYNC;
00994   } else {
00995     mState &= ~XML_HTTP_REQUEST_ASYNC;
00996   }
00997 
00998   rv = NS_NewURI(getter_AddRefs(uri), url, nsnull, GetBaseURI());
00999   if (NS_FAILED(rv)) return rv;
01000 
01001   // mScriptContext should be initialized because of Init()/Initialize().
01002   // Still need to consider the case that doc is nsnull however.
01003   rv = CheckInnerWindowCorrectness();
01004   NS_ENSURE_SUCCESS(rv, rv);
01005   nsCOMPtr<nsIDocument> doc = GetDocumentFromScriptContext(mScriptContext);
01006   PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
01007   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
01008                                 uri,
01009                                 (doc ? doc->GetDocumentURI() : nsnull),
01010                                 doc,
01011                                 EmptyCString(), //mime guess
01012                                 nsnull,         //extra
01013                                 &shouldLoad);
01014   if (NS_FAILED(rv)) return rv;
01015   if (NS_CP_REJECTED(shouldLoad)) {
01016     // Disallowed by content policy
01017     return NS_ERROR_CONTENT_BLOCKED;
01018   }
01019 
01020   if (!user.IsEmpty()) {
01021     nsCAutoString userpass;
01022     CopyUTF16toUTF8(user, userpass);
01023     if (!password.IsEmpty()) {
01024       userpass.Append(':');
01025       AppendUTF16toUTF8(password, userpass);
01026     }
01027     uri->SetUserPass(userpass);
01028     authp = PR_TRUE;
01029   }
01030 
01031   // When we are called from JS we can find the load group for the page,
01032   // and add ourselves to it. This way any pending requests
01033   // will be automatically aborted if the user leaves the page.
01034   nsCOMPtr<nsILoadGroup> loadGroup;
01035   GetLoadGroup(getter_AddRefs(loadGroup));
01036 
01037   // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
01038   // in turn keeps STOP button from becoming active.  If the consumer passed in
01039   // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
01040   // necko won't generate any progress notifications
01041   nsLoadFlags loadFlags;
01042   if (nsCOMPtr<nsIDOMEventListener>(mOnProgressListener.Get())) {
01043     loadFlags = nsIRequest::LOAD_NORMAL;
01044   } else {
01045     loadFlags = nsIRequest::LOAD_BACKGROUND;
01046   }
01047   rv = NS_NewChannel(getter_AddRefs(mChannel), uri, nsnull, loadGroup, nsnull,
01048                      loadFlags);
01049   if (NS_FAILED(rv)) return rv;
01050 
01051   //mChannel->SetAuthTriedWithPrehost(authp);
01052 
01053   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
01054   if (httpChannel) {
01055     rv = httpChannel->SetRequestMethod(method);
01056   }
01057 
01058   ChangeState(XML_HTTP_REQUEST_OPENED);
01059 
01060   return rv;
01061 }
01062 
01063 /* void open (in AUTF8String method, in AUTF8String url); */
01064 NS_IMETHODIMP
01065 nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url)
01066 {
01067   nsresult rv;
01068   PRBool async = PR_TRUE;
01069   nsAutoString user, password;
01070 
01071   nsCOMPtr<nsIXPCNativeCallContext> cc;
01072   nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID(), &rv));
01073   if(NS_SUCCEEDED(rv)) {
01074     rv = xpc->GetCurrentNativeCallContext(getter_AddRefs(cc));
01075   }
01076 
01077   if (NS_SUCCEEDED(rv) && cc) {
01078     PRUint32 argc;
01079     rv = cc->GetArgc(&argc);
01080     if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01081 
01082     jsval* argv;
01083     rv = cc->GetArgvPtr(&argv);
01084     if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01085 
01086     JSContext* cx;
01087     rv = cc->GetJSContext(&cx);
01088     if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01089 
01090     nsCOMPtr<nsIURI> targetURI;
01091     rv = NS_NewURI(getter_AddRefs(targetURI), url, nsnull, GetBaseURI());
01092     if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01093 
01094     nsCOMPtr<nsIScriptSecurityManager> secMan =
01095              do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
01096     if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01097 
01098     rv = secMan->CheckConnect(cx, targetURI, "XMLHttpRequest","open");
01099     if (NS_FAILED(rv))
01100     {
01101       // Security check failed.
01102       return NS_OK;
01103     }
01104 
01105     // Find out if UniversalBrowserRead privileges are enabled
01106     // we will need this in case of a redirect
01107     PRBool crossSiteAccessEnabled;
01108     rv = secMan->IsCapabilityEnabled("UniversalBrowserRead",
01109                                      &crossSiteAccessEnabled);
01110     if (NS_FAILED(rv)) return rv;
01111     if (crossSiteAccessEnabled) {
01112       mState |= XML_HTTP_REQUEST_XSITEENABLED;
01113     } else {
01114       mState &= ~XML_HTTP_REQUEST_XSITEENABLED;
01115     }
01116 
01117     if (argc > 2) {
01118       JSBool asyncBool;
01119       ::JS_ValueToBoolean(cx, argv[2], &asyncBool);
01120       async = (PRBool)asyncBool;
01121 
01122       if (argc > 3 && !JSVAL_IS_NULL(argv[3]) && !JSVAL_IS_VOID(argv[3])) {
01123         JSString* userStr = ::JS_ValueToString(cx, argv[3]);
01124 
01125         if (userStr) {
01126           user.Assign(NS_REINTERPRET_CAST(PRUnichar *,
01127                                           ::JS_GetStringChars(userStr)),
01128                       ::JS_GetStringLength(userStr));
01129         }
01130 
01131         if (argc > 4 && !JSVAL_IS_NULL(argv[4]) && !JSVAL_IS_VOID(argv[4])) {
01132           JSString* passwdStr = JS_ValueToString(cx, argv[4]);
01133 
01134           if (passwdStr) {
01135             password.Assign(NS_REINTERPRET_CAST(PRUnichar *,
01136                                                 ::JS_GetStringChars(passwdStr)),
01137                             ::JS_GetStringLength(passwdStr));
01138           }
01139         }
01140       }
01141     }
01142   }
01143 
01144   return OpenRequest(method, url, async, user, password);
01145 }
01146 
01147 nsresult
01148 nsXMLHttpRequest::GetStreamForWString(const PRUnichar* aStr,
01149                                       PRInt32 aLength,
01150                                       nsIInputStream** aStream)
01151 {
01152   nsresult rv;
01153   nsCOMPtr<nsIUnicodeEncoder> encoder;
01154   char* postData;
01155 
01156   // We want to encode the string as utf-8, so get the right encoder
01157   nsCOMPtr<nsICharsetConverterManager> charsetConv =
01158            do_GetService(kCharsetConverterManagerCID, &rv);
01159   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
01160 
01161   rv = charsetConv->GetUnicodeEncoderRaw("UTF-8",
01162                                          getter_AddRefs(encoder));
01163   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
01164 
01165   // Convert to utf-8
01166   PRInt32 charLength;
01167   const PRUnichar* unicodeBuf = aStr;
01168   PRInt32 unicodeLength = aLength;
01169 
01170   rv = encoder->GetMaxLength(unicodeBuf, unicodeLength, &charLength);
01171   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01172 
01173 
01174   // Allocate extra space for the null-terminator
01175   postData = (char*)nsMemory::Alloc(charLength + 1);
01176   if (!postData) {
01177     return NS_ERROR_OUT_OF_MEMORY;
01178   }
01179   rv = encoder->Convert(unicodeBuf,
01180                         &unicodeLength, postData, &charLength);
01181   if (NS_FAILED(rv)) {
01182     nsMemory::Free(postData);
01183     return NS_ERROR_FAILURE;
01184   }
01185 
01186   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
01187   if (!httpChannel) {
01188     nsMemory::Free(postData);
01189     return NS_ERROR_FAILURE;
01190   }
01191 
01192   // Null-terminate
01193   postData[charLength] = '\0';
01194 
01195   nsCOMPtr<nsIStringInputStream> inputStream(do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
01196   if (NS_SUCCEEDED(rv)) {
01197     rv = inputStream->AdoptData(postData, charLength);
01198     if (NS_SUCCEEDED(rv)) {
01199       return CallQueryInterface(inputStream, aStream);
01200     }
01201   }
01202 
01203   // If we got here then something went wrong before the stream
01204   // adopted the buffer.
01205   nsMemory::Free(postData);
01206   return NS_ERROR_FAILURE;
01207 }
01208 
01209 
01210 /*
01211  * "Copy" from a stream.
01212  */
01213 NS_METHOD
01214 nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
01215                                    void* closure,
01216                                    const char* fromRawSegment,
01217                                    PRUint32 toOffset,
01218                                    PRUint32 count,
01219                                    PRUint32 *writeCount)
01220 {
01221   nsXMLHttpRequest* xmlHttpRequest = NS_STATIC_CAST(nsXMLHttpRequest*, closure);
01222   if (!xmlHttpRequest || !writeCount) {
01223     NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
01224     return NS_ERROR_FAILURE;
01225   }
01226 
01227   // Copy for our own use
01228   xmlHttpRequest->mResponseBody.Append(fromRawSegment,count);
01229 
01230   nsresult rv = NS_OK;
01231 
01232   if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
01233     // Give the same data to the parser.
01234 
01235     // We need to wrap the data in a new lightweight stream and pass that
01236     // to the parser, because calling ReadSegments() recursively on the same
01237     // stream is not supported.
01238     nsCOMPtr<nsIInputStream> copyStream;
01239     rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
01240 
01241     if (NS_SUCCEEDED(rv)) {
01242       NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
01243       nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
01244                                   ->OnDataAvailable(xmlHttpRequest->mReadRequest,
01245                                                     xmlHttpRequest->mContext,
01246                                                     copyStream, toOffset, count);
01247 
01248       // No use to continue parsing if we failed here, but we
01249       // should still finish reading the stream
01250       if (NS_FAILED(parsingResult)) {
01251         xmlHttpRequest->mState &= ~XML_HTTP_REQUEST_PARSEBODY;
01252       }
01253     }
01254   }
01255 
01256   xmlHttpRequest->ChangeState(XML_HTTP_REQUEST_INTERACTIVE);
01257 
01258   if (NS_SUCCEEDED(rv)) {
01259     *writeCount = count;
01260   } else {
01261     *writeCount = 0;
01262   }
01263 
01264   return rv;
01265 }
01266 
01267 /* void onDataAvailable (in nsIRequest request, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
01268 NS_IMETHODIMP
01269 nsXMLHttpRequest::OnDataAvailable(nsIRequest *request, nsISupports *ctxt, nsIInputStream *inStr, PRUint32 sourceOffset, PRUint32 count)
01270 {
01271   NS_ENSURE_ARG_POINTER(inStr);
01272 
01273   NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context");
01274 
01275   PRUint32 totalRead;
01276   return inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc, (void*)this, count, &totalRead);
01277 }
01278 
01279 /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
01280 NS_IMETHODIMP
01281 nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
01282 {
01283   // Don't do anything if we have been aborted
01284   if (mState & XML_HTTP_REQUEST_UNINITIALIZED)
01285     return NS_OK;
01286 
01287   if (mState & XML_HTTP_REQUEST_ABORTED) {
01288     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
01289 
01290     return NS_ERROR_UNEXPECTED;
01291   }
01292 
01293   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
01294   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
01295 
01296   mChannel->SetOwner(mPrincipal);
01297 
01298   mReadRequest = request;
01299   mContext = ctxt;
01300   mState |= XML_HTTP_REQUEST_PARSEBODY;
01301   ChangeState(XML_HTTP_REQUEST_LOADED);
01302 
01303   nsresult rv;
01304   nsCOMPtr<nsIDocument> contextDoc = 
01305     GetDocumentFromScriptContext(mScriptContext);
01306   // Get and initialize a DOMImplementation
01307   nsCOMPtr<nsIDOMDOMImplementation> implementation;
01308   if (contextDoc) {
01309     nsCOMPtr<nsIDOMDocument> domContext = do_QueryInterface(contextDoc);
01310     rv = domContext->GetImplementation(getter_AddRefs(implementation));
01311     NS_ENSURE_SUCCESS(rv, rv);
01312   } else {
01313     implementation =
01314       do_CreateInstance(kIDOMDOMImplementationCID, &rv);
01315     if (NS_FAILED(rv)) return rv;
01316 
01317     nsCOMPtr<nsIPrivateDOMImplementation> privImpl =
01318       do_QueryInterface(implementation);
01319     if (privImpl) {
01320       privImpl->Init(GetBaseURI());
01321     }
01322   }
01323 
01324   // Create an empty document from it (resets current document as well)
01325   const nsAString& emptyStr = EmptyString();
01326   rv = implementation->CreateDocument(emptyStr,
01327                                       emptyStr,
01328                                       nsnull,
01329                                       getter_AddRefs(mDocument));
01330   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01331 
01332   nsCOMPtr<nsIDocument> doc(do_QueryInterface(mDocument));
01333   doc->SetPrincipal(mPrincipal);
01334 
01335   // Reset responseBody
01336   mResponseBody.Truncate();
01337 
01338   // Register as a load listener on the document
01339   nsCOMPtr<nsIDOMEventReceiver> target(do_QueryInterface(mDocument));
01340   if (target) {
01341     nsWeakPtr requestWeak =
01342       do_GetWeakReference(NS_STATIC_CAST(nsIXMLHttpRequest*, this));
01343     nsCOMPtr<nsIDOMEventListener> proxy = new nsLoadListenerProxy(requestWeak);
01344     if (!proxy) return NS_ERROR_OUT_OF_MEMORY;
01345 
01346     // This will addref the proxy
01347     rv = target->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMEventListener*,
01348                                                       proxy),
01349                                        NS_GET_IID(nsIDOMLoadListener));
01350     if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
01351   }
01352 
01353   nsresult status;
01354   request->GetStatus(&status);
01355 
01356   if (NS_SUCCEEDED(status)) {
01357     if (!mOverrideMimeType.IsEmpty()) {
01358       channel->SetContentType(mOverrideMimeType);
01359     }
01360 
01361     // We can gain a huge performance win by not even trying to
01362     // parse non-XML data. This also protects us from the situation
01363     // where we have an XML document and sink, but HTML (or other)
01364     // parser, which can produce unreliable results.
01365     nsCAutoString type;
01366     channel->GetContentType(type);
01367 
01368     if (type.Find("xml") == kNotFound) {
01369       mState &= ~XML_HTTP_REQUEST_PARSEBODY;
01370     }
01371   } else {
01372     // The request failed, so we shouldn't be parsing anyway
01373     mState &= ~XML_HTTP_REQUEST_PARSEBODY;
01374   }
01375 
01376   if (mState & XML_HTTP_REQUEST_PARSEBODY) {
01377     nsCOMPtr<nsIStreamListener> listener;
01378     nsCOMPtr<nsILoadGroup> loadGroup;
01379     channel->GetLoadGroup(getter_AddRefs(loadGroup));
01380 
01381     nsCOMPtr<nsIDocument> document(do_QueryInterface(mDocument));
01382     if (!document) {
01383       return NS_ERROR_FAILURE;
01384     }
01385 
01386     rv = document->StartDocumentLoad(kLoadAsData, channel, loadGroup, nsnull,
01387                                      getter_AddRefs(listener), PR_TRUE);
01388     NS_ENSURE_SUCCESS(rv, rv);
01389 
01390     mXMLParserStreamListener = listener;
01391     return mXMLParserStreamListener->OnStartRequest(request, ctxt);
01392   }
01393 
01394   return NS_OK;
01395 }
01396 
01397 /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status, in wstring statusArg); */
01398 NS_IMETHODIMP
01399 nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
01400 {
01401   // Don't do anything if we have been aborted
01402   if (mState & XML_HTTP_REQUEST_UNINITIALIZED)
01403     return NS_OK;
01404 
01405   nsresult rv = NS_OK;
01406 
01407   nsCOMPtr<nsIParser> parser;
01408 
01409   // If we're loading a multipart stream of XML documents, we'll get
01410   // an OnStopRequest() for the last part in the stream, and then
01411   // another one for the end of the initiating
01412   // "multipart/x-mixed-replace" stream too. So we must check that we
01413   // still have an xml parser stream listener before accessing it
01414   // here.
01415 
01416   // Is this good enough here?
01417   if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
01418     parser = do_QueryInterface(mXMLParserStreamListener);
01419     NS_ABORT_IF_FALSE(parser, "stream listener was expected to be a parser");
01420     rv = mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
01421   }
01422 
01423   mXMLParserStreamListener = nsnull;
01424   mReadRequest = nsnull;
01425   mContext = nsnull;
01426 
01427   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
01428   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
01429 
01430   channel->SetNotificationCallbacks(nsnull);
01431   mNotificationCallbacks = nsnull;
01432   mChannelEventSink = nsnull;
01433   mProgressEventSink = nsnull;
01434 
01435   if (NS_FAILED(status)) {
01436     // This can happen if the server is unreachable. Other possible
01437     // reasons are that the user leaves the page or hits the ESC key.
01438     Error(nsnull);
01439 
01440     // By nulling out channel here we make it so that Send() can test
01441     // for that and throw. Also calling the various status
01442     // methods/members will not throw.
01443     // This matches what IE does.
01444     mChannel = nsnull;
01445   } else if (!parser || parser->IsParserEnabled()) {
01446     // If we don't have a parser, we never attempted to parse the
01447     // incoming data, and we can proceed to call RequestCompleted().
01448     // Alternatively, if we do have a parser, its possible that we
01449     // have given it some data and this caused it to block e.g. by a
01450     // by a xml-stylesheet PI. In this case, we will have to wait till
01451     // it gets enabled again and RequestCompleted() must be called
01452     // later, when we get the load event from the document. If the
01453     // parser is enabled, it is not blocked and we can still go ahead
01454     // and call RequestCompleted() and expect everything to get
01455     // cleaned up immediately.
01456     RequestCompleted();
01457   } else {
01458     ChangeState(XML_HTTP_REQUEST_STOPPED, PR_FALSE);
01459   }
01460 
01461   if (mScriptContext) {
01462     // Force a GC since we could be loading a lot of documents
01463     // (especially if streaming), and not doing anything that would
01464     // normally trigger a GC.
01465     mScriptContext->GC();
01466   }
01467 
01468   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
01469 
01470   return rv;
01471 }
01472 
01473 nsresult
01474 nsXMLHttpRequest::RequestCompleted()
01475 {
01476   nsresult rv = NS_OK;
01477 
01478   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
01479 
01480   // If we're uninitialized at this point, we encountered an error
01481   // earlier and listeners have already been notified. Also we do
01482   // not want to do this if we already completed.
01483   if (mState & (XML_HTTP_REQUEST_UNINITIALIZED |
01484                 XML_HTTP_REQUEST_COMPLETED)) {
01485     return NS_OK;
01486   }
01487 
01488   // We need to create the event before nulling out mDocument
01489   nsEvent evt(PR_TRUE, NS_PAGE_LOAD);
01490   nsCOMPtr<nsIDOMEvent> domevent;
01491   rv = CreateEvent(&evt, getter_AddRefs(domevent));
01492 
01493   // We might have been sent non-XML data. If that was the case,
01494   // we should null out the document member. The idea in this
01495   // check here is that if there is no document element it is not
01496   // an XML document. We might need a fancier check...
01497   if (mDocument) {
01498     nsCOMPtr<nsIDOMElement> root;
01499     mDocument->GetDocumentElement(getter_AddRefs(root));
01500     if (!root) {
01501       mDocument = nsnull;
01502     }
01503   }
01504 
01505   // Grab hold of the event listener lists we will need
01506   nsCOMPtr<nsIDOMEventListener> onLoadListener = mOnLoadListener.Get();
01507   PRUint32 count = mLoadEventListeners.Length();
01508   nsCOMArray<nsIDOMEventListener> listenersCopy(count);
01509   for (PRUint32 i = 0; i < count; ++i)
01510     listenersCopy.ReplaceObjectAt(nsCOMPtr<nsIDOMEventListener>(mLoadEventListeners[i]->Get()), i);
01511 
01512   // Clear listeners here unless we're multipart
01513   ChangeState(XML_HTTP_REQUEST_COMPLETED, PR_TRUE,
01514               !(mState & XML_HTTP_REQUEST_MULTIPART));
01515 
01516   NotifyEventListeners(onLoadListener, &listenersCopy, domevent);
01517 
01518   if (mState & XML_HTTP_REQUEST_MULTIPART) {
01519     // We're a multipart request, so we're not done. Reset to opened.
01520     ChangeState(XML_HTTP_REQUEST_OPENED);
01521   }
01522 
01523   return rv;
01524 }
01525 
01526 /* void send (in nsIVariant aBody); */
01527 NS_IMETHODIMP
01528 nsXMLHttpRequest::Send(nsIVariant *aBody)
01529 {
01530   nsresult rv = CheckInnerWindowCorrectness();
01531   NS_ENSURE_SUCCESS(rv, rv);
01532 
01533   // Return error if we're already processing a request
01534   if (XML_HTTP_REQUEST_SENT & mState) {
01535     return NS_ERROR_FAILURE;
01536   }
01537 
01538   // Make sure we've been opened
01539   if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
01540     return NS_ERROR_NOT_INITIALIZED;
01541   }
01542 
01543   // XXX We should probably send a warning to the JS console
01544   //     if there are no event listeners set and we are doing
01545   //     an asynchronous call.
01546 
01547   // Ignore argument if method is GET, there is no point in trying to
01548   // upload anything
01549   nsCAutoString method;
01550   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
01551 
01552   if (httpChannel) {
01553     httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
01554 
01555     if (mPrincipal && !IsSystemPrincipal(mPrincipal)) {
01556       nsCOMPtr<nsIURI> codebase;
01557       mPrincipal->GetURI(getter_AddRefs(codebase));
01558 
01559       httpChannel->SetReferrer(codebase);
01560     }
01561   }
01562 
01563   if (aBody && httpChannel && !method.EqualsLiteral("GET")) {
01564     nsXPIDLString serial;
01565     nsCOMPtr<nsIInputStream> postDataStream;
01566 
01567     PRUint16 dataType;
01568     rv = aBody->GetDataType(&dataType);
01569     if (NS_FAILED(rv))
01570       return rv;
01571 
01572     switch (dataType) {
01573     case nsIDataType::VTYPE_INTERFACE:
01574     case nsIDataType::VTYPE_INTERFACE_IS:
01575       {
01576         nsCOMPtr<nsISupports> supports;
01577         nsID *iid;
01578         rv = aBody->GetAsInterface(&iid, getter_AddRefs(supports));
01579         if (NS_FAILED(rv))
01580           return rv;
01581         if (iid)
01582           nsMemory::Free(iid);
01583 
01584         // document?
01585         nsCOMPtr<nsIDOMDocument> doc(do_QueryInterface(supports));
01586         if (doc) {
01587           nsCOMPtr<nsIDOMSerializer> serializer(do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv));
01588           if (NS_FAILED(rv)) return rv;
01589 
01590           rv = serializer->SerializeToString(doc, serial);
01591           if (NS_FAILED(rv))
01592             return rv;
01593         } else {
01594           // nsISupportsString?
01595           nsCOMPtr<nsISupportsString> wstr(do_QueryInterface(supports));
01596           if (wstr) {
01597             wstr->GetData(serial);
01598           } else {
01599             // stream?
01600             nsCOMPtr<nsIInputStream> stream(do_QueryInterface(supports));
01601             if (stream) {
01602               postDataStream = stream;
01603             }
01604           }
01605         }
01606       }
01607       break;
01608     case nsIDataType::VTYPE_VOID:
01609     case nsIDataType::VTYPE_EMPTY:
01610       // Makes us act as if !aBody, don't upload anything
01611       break;
01612     case nsIDataType::VTYPE_EMPTY_ARRAY:
01613     case nsIDataType::VTYPE_ARRAY:
01614       // IE6 throws error here, so we do that as well
01615       return NS_ERROR_INVALID_ARG;
01616     default:
01617       // try variant string
01618       rv = aBody->GetAsWString(getter_Copies(serial));
01619       if (NS_FAILED(rv))
01620         return rv;
01621       break;
01622     }
01623 
01624     if (serial) {
01625       // Convert to a byte stream
01626       rv = GetStreamForWString(serial.get(),
01627                                nsCRT::strlen(serial.get()),
01628                                getter_AddRefs(postDataStream));
01629       if (NS_FAILED(rv))
01630         return rv;
01631     }
01632 
01633     if (postDataStream) {
01634       nsCOMPtr<nsIUploadChannel> uploadChannel(do_QueryInterface(httpChannel));
01635       NS_ASSERTION(uploadChannel, "http must support nsIUploadChannel");
01636 
01637       // If no content type header was set by the client, we set it to
01638       // application/xml.
01639       nsCAutoString contentType;
01640       if (NS_FAILED(httpChannel->
01641                       GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
01642                                        contentType)) ||
01643           contentType.IsEmpty()) {
01644         contentType = NS_LITERAL_CSTRING("application/xml");
01645       }
01646       
01647       rv = uploadChannel->SetUploadStream(postDataStream, contentType, -1);
01648       // Reset the method to its original value
01649       if (httpChannel) {
01650         httpChannel->SetRequestMethod(method);
01651       }
01652     }
01653   }
01654 
01655   // Reset responseBody
01656   mResponseBody.Truncate();
01657 
01658   // Reset responseXML
01659   mDocument = nsnull;
01660 
01661   nsCOMPtr<nsIEventQueue> modalEventQueue;
01662 
01663   if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
01664     if(!mEventQService) {
01665       mEventQService = do_GetService(kEventQueueServiceCID, &rv);
01666       NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
01667     }
01668 
01669     mState |= XML_HTTP_REQUEST_SYNCLOOPING;
01670 
01671     rv = mEventQService->PushThreadEventQueue(getter_AddRefs(modalEventQueue));
01672     if (NS_FAILED(rv)) {
01673       return rv;
01674     }
01675   }
01676 
01677   // Hook us up to listen to redirects and the like
01678   mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
01679   mChannel->SetNotificationCallbacks(this);
01680 
01681   nsCOMPtr<nsIStreamListener> listener;
01682   if (mState & XML_HTTP_REQUEST_MULTIPART) {
01683     listener = new nsMultipartProxyListener(this);
01684     if (!listener) {
01685       return NS_ERROR_OUT_OF_MEMORY;
01686     }
01687   } else {
01688     listener = this;
01689   }
01690 
01691   // Bypass the network cache in cases where it makes no sense:
01692   // 1) Multipart responses are very large and would likely be doomed by the
01693   //    cache once they grow too large, so they are not worth caching.
01694   // 2) POST responses are always unique, and we provide no API that would
01695   //    allow our consumers to specify a "cache key" to access old POST
01696   //    responses, so they are not worth caching.
01697   if ((mState & XML_HTTP_REQUEST_MULTIPART) || method.EqualsLiteral("POST")) {
01698     AddLoadFlags(mChannel,
01699         nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
01700   }
01701   // When we are sync loading, we need to bypass the local cache when it would
01702   // otherwise block us waiting for exclusive access to the cache.  If we don't
01703   // do this, then we could dead lock in some cases (see bug 309424).
01704   else if (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
01705     AddLoadFlags(mChannel,
01706         nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
01707   }
01708 
01709   // Start reading from the channel
01710   ChangeState(XML_HTTP_REQUEST_SENT);
01711   rv = mChannel->AsyncOpen(listener, nsnull);
01712 
01713   if (NS_FAILED(rv)) {
01714     if (modalEventQueue) {
01715       mEventQService->PopThreadEventQueue(modalEventQueue);
01716     }
01717 
01718     // Drop our ref to the channel to avoid cycles
01719     mChannel = nsnull;
01720     return rv;
01721   }
01722 
01723   // If we're synchronous, spin an event loop here and wait
01724   if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
01725     while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
01726       modalEventQueue->ProcessPendingEvents();
01727 
01728       // Be sure not to busy wait! (see bug 273578)
01729       if (mState & XML_HTTP_REQUEST_SYNCLOOPING)
01730         PR_Sleep(PR_MillisecondsToInterval(10));
01731     }
01732 
01733     mEventQService->PopThreadEventQueue(modalEventQueue);
01734   } else {
01735     // If we're asynchronous, we need to prevent our event listeners
01736     // from being garbage collected even if this object becomes
01737     // unreachable from script, since they can fire as a result of our
01738     // reachability from the network stack.
01739     rv = nsDOMClassInfo::SetExternallyReferenced(this);
01740     if (NS_SUCCEEDED(rv))
01741       mState |= XML_HTTP_REQUEST_ROOTED;
01742   }
01743 
01744   if (!mChannel) {
01745     return NS_ERROR_FAILURE;
01746   }
01747 
01748   return NS_OK;
01749 }
01750 
01751 /* void setRequestHeader (in AUTF8String header, in AUTF8String value); */
01752 NS_IMETHODIMP
01753 nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
01754                                    const nsACString& value)
01755 {
01756   if (!mChannel)             // open() initializes mChannel, and open()
01757     return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
01758 
01759   // Prevent modification to certain HTTP headers (see bug 302263), unless
01760   // the executing script has UniversalBrowserWrite permission.
01761 
01762   nsCOMPtr<nsIScriptSecurityManager> secMan = 
01763       do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
01764   if (!secMan)
01765     return NS_ERROR_FAILURE;
01766 
01767   PRBool privileged;
01768   nsresult rv = secMan->IsCapabilityEnabled("UniversalBrowserWrite",
01769                                             &privileged);
01770   if (NS_FAILED(rv))
01771     return NS_ERROR_FAILURE;
01772 
01773   if (!privileged) {
01774     const char *kInvalidHeaders[] = {
01775       "host", "content-length", "transfer-encoding", "via", "upgrade"
01776     };
01777     for (size_t i = 0; i < NS_ARRAY_LENGTH(kInvalidHeaders); ++i) {
01778       if (header.LowerCaseEqualsASCII(kInvalidHeaders[i])) {
01779         NS_WARNING("refusing to set request header");
01780         return NS_OK;
01781       }
01782     }
01783   }
01784 
01785   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
01786 
01787   if (httpChannel) {
01788     // We need to set, not add to, the header.
01789     return httpChannel->SetRequestHeader(header, value, PR_FALSE);
01790   }
01791 
01792   return NS_OK;
01793 }
01794 
01795 /* readonly attribute long readyState; */
01796 NS_IMETHODIMP
01797 nsXMLHttpRequest::GetReadyState(PRInt32 *aState)
01798 {
01799   NS_ENSURE_ARG_POINTER(aState);
01800   // Translate some of our internal states for external consumers
01801   if (mState & XML_HTTP_REQUEST_UNINITIALIZED) {
01802     *aState = 0; // UNINITIALIZED
01803   } else  if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
01804     *aState = 1; // LOADING
01805   } else if (mState & XML_HTTP_REQUEST_LOADED) {
01806     *aState = 2; // LOADED
01807   } else if (mState & (XML_HTTP_REQUEST_INTERACTIVE | XML_HTTP_REQUEST_STOPPED)) {
01808     *aState = 3; // INTERACTIVE
01809   } else if (mState & XML_HTTP_REQUEST_COMPLETED) {
01810     *aState = 4; // COMPLETED
01811   } else {
01812     NS_ERROR("Should not happen");
01813   }
01814 
01815   return NS_OK;
01816 }
01817 
01818 /* void   overrideMimeType(in AUTF8String mimetype); */
01819 NS_IMETHODIMP
01820 nsXMLHttpRequest::OverrideMimeType(const nsACString& aMimeType)
01821 {
01822   // XXX Should we do some validation here?
01823   mOverrideMimeType.Assign(aMimeType);
01824   return NS_OK;
01825 }
01826 
01827 
01828 /* attribute boolean multipart; */
01829 NS_IMETHODIMP
01830 nsXMLHttpRequest::GetMultipart(PRBool *_retval)
01831 {
01832   *_retval = mState & XML_HTTP_REQUEST_MULTIPART;
01833 
01834   return NS_OK;
01835 }
01836 
01837 /* attribute boolean multipart; */
01838 NS_IMETHODIMP
01839 nsXMLHttpRequest::SetMultipart(PRBool aMultipart)
01840 {
01841   if (!(mState & XML_HTTP_REQUEST_UNINITIALIZED)) {
01842     // Can't change this while we're in the middle of something.
01843     return NS_ERROR_IN_PROGRESS;
01844   }
01845 
01846   if (aMultipart) {
01847     mState |= XML_HTTP_REQUEST_MULTIPART;
01848   } else {
01849     mState &= ~XML_HTTP_REQUEST_MULTIPART;
01850   }
01851 
01852   return NS_OK;
01853 }
01854 
01855 
01856 // nsIDOMEventListener
01857 nsresult
01858 nsXMLHttpRequest::HandleEvent(nsIDOMEvent* aEvent)
01859 {
01860   return NS_OK;
01861 }
01862 
01863 
01864 // nsIDOMLoadListener
01865 nsresult
01866 nsXMLHttpRequest::Load(nsIDOMEvent* aEvent)
01867 {
01868   // If we had an XML error in the data, the parser terminated and
01869   // we received the load event, even though we might still be
01870   // loading data into responseBody/responseText. We will delay
01871   // sending the load event until OnStopRequest(). In normal case
01872   // there is no harm done, we will get OnStopRequest() immediately
01873   // after the load event.
01874   //
01875   // However, if the data we were loading caused the parser to stop,
01876   // for example when loading external stylesheets, we can receive
01877   // the OnStopRequest() call before the parser has finished building
01878   // the document. In that case, we obviously should not fire the event
01879   // in OnStopRequest(). For those documents, we must wait for the load
01880   // event from the document to fire our RequestCompleted().
01881   if (mState & XML_HTTP_REQUEST_STOPPED) {
01882     RequestCompleted();
01883   }
01884   return NS_OK;
01885 }
01886 
01887 nsresult
01888 nsXMLHttpRequest::Unload(nsIDOMEvent* aEvent)
01889 {
01890   return NS_OK;
01891 }
01892 
01893 nsresult
01894 nsXMLHttpRequest::BeforeUnload(nsIDOMEvent* aEvent)
01895 {
01896   return NS_OK;
01897 }
01898 
01899 nsresult
01900 nsXMLHttpRequest::Abort(nsIDOMEvent* aEvent)
01901 {
01902   Abort();
01903 
01904   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
01905 
01906   return NS_OK;
01907 }
01908 
01909 nsresult
01910 nsXMLHttpRequest::Error(nsIDOMEvent* aEvent)
01911 {
01912   // We need to create the event before nulling out mDocument
01913   nsCOMPtr<nsIDOMEvent> event(do_QueryInterface(aEvent));
01914   // There is no NS_PAGE_ERROR event but NS_SCRIPT_ERROR should be ok.
01915   nsEvent evt(PR_TRUE, NS_SCRIPT_ERROR);
01916   if (!event) {
01917     CreateEvent(&evt, getter_AddRefs(event));
01918   }
01919 
01920   mDocument = nsnull;
01921   ChangeState(XML_HTTP_REQUEST_COMPLETED);
01922 
01923   mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
01924 
01925   nsCOMPtr<nsIDOMEventListener> onErrorListener = mOnErrorListener.Get();
01926   PRUint32 count = mErrorEventListeners.Length();
01927   nsCOMArray<nsIDOMEventListener> listenersCopy(count);
01928   for (PRUint32 i = 0; i < count; ++i)
01929     listenersCopy.ReplaceObjectAt(nsCOMPtr<nsIDOMEventListener>(mErrorEventListeners[i]->Get()), i);
01930 
01931   ClearEventListeners();
01932   
01933   NotifyEventListeners(onErrorListener, &listenersCopy, event);
01934 
01935   return NS_OK;
01936 }
01937 
01938 nsresult
01939 nsXMLHttpRequest::ChangeState(PRUint32 aState, PRBool aBroadcast,
01940                               PRBool aClearEventListeners)
01941 {
01942   // If we are setting one of the mutually exclusive states,
01943   // unset those state bits first.
01944   if (aState & XML_HTTP_REQUEST_LOADSTATES) {
01945     mState &= ~XML_HTTP_REQUEST_LOADSTATES;
01946   }
01947   mState |= aState;
01948   nsresult rv = NS_OK;
01949 
01950   // Take ref to the one listener we need
01951   nsCOMPtr<nsIOnReadyStateChangeHandler> onReadyStateChangeListener =
01952     mOnReadystatechangeListener.Get();
01953 
01954   if (aClearEventListeners) {
01955     ClearEventListeners();
01956   }
01957 
01958   if ((mState & XML_HTTP_REQUEST_ASYNC) &&
01959       (aState & XML_HTTP_REQUEST_LOADSTATES) && // Broadcast load states only
01960       aBroadcast &&
01961       onReadyStateChangeListener &&
01962       NS_SUCCEEDED(CheckInnerWindowCorrectness())) {
01963     nsCOMPtr<nsIJSContextStack> stack;
01964     JSContext *cx = nsnull;
01965 
01966     if (mScriptContext) {
01967       stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
01968 
01969       if (stack) {
01970         cx = (JSContext *)mScriptContext->GetNativeContext();
01971 
01972         if (cx) {
01973           stack->Push(cx);
01974         }
01975       }
01976     }
01977 
01978     rv = onReadyStateChangeListener->HandleEvent();
01979 
01980     if (cx) {
01981       stack->Pop(&cx);
01982     }
01983   }
01984 
01985   return rv;
01986 }
01987 
01989 // nsIChannelEventSink methods:
01990 //
01991 NS_IMETHODIMP
01992 nsXMLHttpRequest::OnChannelRedirect(nsIChannel *aOldChannel,
01993                                     nsIChannel *aNewChannel,
01994                                     PRUint32    aFlags)
01995 {
01996   NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
01997 
01998   if (!(mState & XML_HTTP_REQUEST_XSITEENABLED)) {
01999     nsresult rv = NS_ERROR_FAILURE;
02000 
02001     nsCOMPtr<nsIURI> oldURI;
02002     rv = aOldChannel->GetURI(getter_AddRefs(oldURI));
02003     NS_ENSURE_SUCCESS(rv, rv);
02004 
02005     nsCOMPtr<nsIURI> newURI;
02006     rv = aNewChannel->GetURI(getter_AddRefs(newURI)); // The redirected URI
02007     if (NS_FAILED(rv))
02008       return rv;
02009 
02010     nsCOMPtr<nsIScriptSecurityManager> secMan =
02011              do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
02012     if (NS_FAILED(rv))
02013       return rv;
02014 
02015     rv = secMan->CheckSameOriginURI(oldURI, newURI);
02016     if (NS_FAILED(rv))
02017       return rv;
02018   }
02019 
02020   if (mChannelEventSink) {
02021     nsresult rv =
02022       mChannelEventSink->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
02023     if (NS_FAILED(rv)) {
02024       return rv;
02025     }
02026   }
02027 
02028   mChannel = aNewChannel;
02029 
02030   return NS_OK;
02031 }
02032 
02034 // nsIProgressEventSink methods:
02035 //
02036 
02037 NS_IMETHODIMP
02038 nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, PRUint64 aProgress, PRUint64 aProgressMax)
02039 {
02040   nsCOMPtr<nsIDOMEventListener> listener = mOnProgressListener.Get();
02041   if (listener) {
02042     nsCOMPtr<nsIDOMEvent> event;
02043     nsEvent evt(PR_TRUE, NS_EVENT_NULL); // what name do we make up here? 
02044     nsresult rv = CreateEvent(&evt, getter_AddRefs(event));
02045     NS_ENSURE_SUCCESS(rv, rv);
02046     
02047     nsXMLHttpProgressEvent * progressEvent = new nsXMLHttpProgressEvent(event, aProgress, aProgressMax); 
02048     if (!progressEvent)
02049       return NS_ERROR_OUT_OF_MEMORY;
02050 
02051     event = do_QueryInterface(progressEvent); 
02052     NotifyEventListeners(listener, nsnull, event);
02053   }
02054 
02055   if (mProgressEventSink) {
02056     mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
02057                                    aProgressMax);
02058   }
02059 
02060   return NS_OK;
02061 }
02062 
02063 NS_IMETHODIMP
02064 nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatus, const PRUnichar *aStatusArg)
02065 {
02066   if (mProgressEventSink) {
02067     mProgressEventSink->OnStatus(aRequest, aContext, aStatus, aStatusArg);
02068   }
02069 
02070   return NS_OK;
02071 }
02072 
02074 // nsIInterfaceRequestor methods:
02075 //
02076 NS_IMETHODIMP
02077 nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
02078 {
02079   // Make sure to return ourselves for the channel event sink interface and
02080   // progress event sink interface, no matter what.  We can forward these to
02081   // mNotificationCallbacks if it wants to get notifications for them.  But we
02082   // need to see these notifications for proper functioning.
02083   if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
02084     mChannelEventSink = do_GetInterface(mNotificationCallbacks);
02085     *aResult = NS_STATIC_CAST(nsIChannelEventSink*, this);
02086     NS_ADDREF_THIS();
02087     return NS_OK;
02088   } else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
02089     mProgressEventSink = do_GetInterface(mNotificationCallbacks);
02090     *aResult = NS_STATIC_CAST(nsIProgressEventSink*, this);
02091     NS_ADDREF_THIS();
02092     return NS_OK;
02093   }
02094 
02095   // Now give mNotificationCallbacks (if non-null) a chance to return the
02096   // desired interface.  Note that this means that it can override our
02097   // nsIAuthPrompt impl, but that's fine, if it has a better auth prompt idea.
02098   if (mNotificationCallbacks) {
02099     nsresult rv = mNotificationCallbacks->GetInterface(aIID, aResult);
02100     if (NS_SUCCEEDED(rv)) {
02101       NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
02102       return rv;
02103     }
02104   }
02105   
02106   if (aIID.Equals(NS_GET_IID(nsIAuthPrompt))) {    
02107     *aResult = nsnull;
02108 
02109     nsresult rv;
02110     nsCOMPtr<nsIWindowWatcher> ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
02111     if (NS_FAILED(rv))
02112       return rv;
02113 
02114     nsCOMPtr<nsIAuthPrompt> prompt;
02115     rv = ww->GetNewAuthPrompter(nsnull, getter_AddRefs(prompt));
02116     if (NS_FAILED(rv))
02117       return rv;
02118 
02119     nsIAuthPrompt *p = prompt.get();
02120     NS_ADDREF(p);
02121     *aResult = p;
02122     return NS_OK;
02123   }
02124 
02125   return QueryInterface(aIID, aResult);
02126 }
02127 
02129 // nsIDOMGCParticipant methods:
02130 //
02131 /* virtual */ nsIDOMGCParticipant*
02132 nsXMLHttpRequest::GetSCCIndex()
02133 {
02134   return this;
02135 }
02136 
02137 /* virtual */ void
02138 nsXMLHttpRequest::AppendReachableList(nsCOMArray<nsIDOMGCParticipant>& aArray)
02139 {
02140   nsCOMPtr<nsIDOMGCParticipant> gcp = do_QueryInterface(mDocument);
02141   if (gcp)
02142     aArray.AppendObject(gcp);
02143 }
02144 
02145 
02146 NS_IMPL_ISUPPORTS1(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
02147 
02148 NS_IMETHODIMP nsXMLHttpRequest::
02149 nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value)
02150 {
02151     mHeaders.Append(header);
02152     mHeaders.Append(": ");
02153     mHeaders.Append(value);
02154     mHeaders.Append('\n');
02155     return NS_OK;
02156 }
02157 
02158 // DOM event class to handle progress notifications
02159 nsXMLHttpProgressEvent::nsXMLHttpProgressEvent(nsIDOMEvent * aInner, PRUint64 aCurrentProgress, PRUint64 aMaxProgress)
02160 {
02161   mInner = aInner; 
02162   mCurProgress = aCurrentProgress;
02163   mMaxProgress = aMaxProgress;
02164 }
02165 
02166 nsXMLHttpProgressEvent::~nsXMLHttpProgressEvent()
02167 {}
02168 
02169 // QueryInterface implementation for nsXMLHttpRequest
02170 NS_INTERFACE_MAP_BEGIN(nsXMLHttpProgressEvent)
02171   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMLSProgressEvent)
02172   NS_INTERFACE_MAP_ENTRY(nsIDOMLSProgressEvent)
02173   NS_INTERFACE_MAP_ENTRY(nsIDOMEvent)
02174   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(XMLHttpProgressEvent)
02175 NS_INTERFACE_MAP_END
02176 
02177 NS_IMPL_ADDREF(nsXMLHttpProgressEvent)
02178 NS_IMPL_RELEASE(nsXMLHttpProgressEvent)
02179 
02180 NS_IMETHODIMP nsXMLHttpProgressEvent::GetInput(nsIDOMLSInput * *aInput)
02181 {
02182   *aInput = nsnull;
02183   return NS_ERROR_NOT_IMPLEMENTED;
02184 }
02185 
02186 NS_IMETHODIMP nsXMLHttpProgressEvent::GetPosition(PRUint32 *aPosition)
02187 {
02188   // XXX can we change the iface?
02189   LL_L2UI(*aPosition, mCurProgress);
02190   return NS_OK;
02191 }
02192 
02193 NS_IMETHODIMP nsXMLHttpProgressEvent::GetTotalSize(PRUint32 *aTotalSize)
02194 {
02195   // XXX can we change the iface?
02196   LL_L2UI(*aTotalSize, mMaxProgress);
02197   return NS_OK;
02198 }