1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "mozilla/Util.h"
39 :
40 : #include "nsXMLHttpRequest.h"
41 : #include "nsISimpleEnumerator.h"
42 : #include "nsIXPConnect.h"
43 : #include "nsICharsetConverterManager.h"
44 : #include "nsLayoutCID.h"
45 : #include "nsXPIDLString.h"
46 : #include "nsReadableUtils.h"
47 : #include "nsIURI.h"
48 : #include "nsILoadGroup.h"
49 : #include "nsNetUtil.h"
50 : #include "nsStreamUtils.h"
51 : #include "nsThreadUtils.h"
52 : #include "nsIUploadChannel.h"
53 : #include "nsIUploadChannel2.h"
54 : #include "nsIDOMSerializer.h"
55 : #include "nsXPCOM.h"
56 : #include "nsISupportsPrimitives.h"
57 : #include "nsGUIEvent.h"
58 : #include "nsIPrivateDOMEvent.h"
59 : #include "prprf.h"
60 : #include "nsIDOMEventListener.h"
61 : #include "nsIJSContextStack.h"
62 : #include "nsIScriptSecurityManager.h"
63 : #include "nsWeakPtr.h"
64 : #include "nsCharsetAlias.h"
65 : #include "nsIScriptGlobalObject.h"
66 : #include "nsDOMClassInfoID.h"
67 : #include "nsIDOMElement.h"
68 : #include "nsIDOMWindow.h"
69 : #include "nsIMIMEService.h"
70 : #include "nsCExternalHandlerService.h"
71 : #include "nsIVariant.h"
72 : #include "nsVariant.h"
73 : #include "nsIScriptError.h"
74 : #include "xpcpublic.h"
75 : #include "nsStringStream.h"
76 : #include "nsIStreamConverterService.h"
77 : #include "nsICachingChannel.h"
78 : #include "nsContentUtils.h"
79 : #include "nsEventDispatcher.h"
80 : #include "nsDOMJSUtils.h"
81 : #include "nsCOMArray.h"
82 : #include "nsIScriptableUConv.h"
83 : #include "nsCycleCollectionParticipant.h"
84 : #include "nsIContentPolicy.h"
85 : #include "nsContentPolicyUtils.h"
86 : #include "nsContentErrors.h"
87 : #include "nsLayoutStatics.h"
88 : #include "nsCrossSiteListenerProxy.h"
89 : #include "nsDOMError.h"
90 : #include "nsIHTMLDocument.h"
91 : #include "nsIMultiPartChannel.h"
92 : #include "nsIScriptObjectPrincipal.h"
93 : #include "nsIStorageStream.h"
94 : #include "nsIPromptFactory.h"
95 : #include "nsIWindowWatcher.h"
96 : #include "nsCharSeparatedTokenizer.h"
97 : #include "nsIConsoleService.h"
98 : #include "nsIChannelPolicy.h"
99 : #include "nsChannelPolicy.h"
100 : #include "nsIContentSecurityPolicy.h"
101 : #include "nsAsyncRedirectVerifyHelper.h"
102 : #include "jstypedarray.h"
103 : #include "nsStringBuffer.h"
104 : #include "nsDOMFile.h"
105 : #include "nsIFileChannel.h"
106 : #include "mozilla/Telemetry.h"
107 : #include "sampler.h"
108 : #include "nsWrapperCacheInlines.h"
109 :
110 : using namespace mozilla;
111 :
112 : #define LOAD_STR "load"
113 : #define ERROR_STR "error"
114 : #define ABORT_STR "abort"
115 : #define TIMEOUT_STR "timeout"
116 : #define LOADSTART_STR "loadstart"
117 : #define PROGRESS_STR "progress"
118 : #define UPLOADPROGRESS_STR "uploadprogress"
119 : #define READYSTATE_STR "readystatechange"
120 : #define LOADEND_STR "loadend"
121 :
122 : // CIDs
123 :
124 : // State
125 : #define XML_HTTP_REQUEST_UNSENT (1 << 0) // 0 UNSENT
126 : #define XML_HTTP_REQUEST_OPENED (1 << 1) // 1 OPENED
127 : #define XML_HTTP_REQUEST_HEADERS_RECEIVED (1 << 2) // 2 HEADERS_RECEIVED
128 : #define XML_HTTP_REQUEST_LOADING (1 << 3) // 3 LOADING
129 : #define XML_HTTP_REQUEST_DONE (1 << 4) // 4 DONE
130 : #define XML_HTTP_REQUEST_SENT (1 << 5) // Internal, OPENED in IE and external view
131 : #define XML_HTTP_REQUEST_STOPPED (1 << 6) // Internal, LOADING in IE and external view
132 : // The above states are mutually exclusive, change with ChangeState() only.
133 : // The states below can be combined.
134 : #define XML_HTTP_REQUEST_ABORTED (1 << 7) // Internal
135 : #define XML_HTTP_REQUEST_ASYNC (1 << 8) // Internal
136 : #define XML_HTTP_REQUEST_PARSEBODY (1 << 9) // Internal
137 : #define XML_HTTP_REQUEST_SYNCLOOPING (1 << 10) // Internal
138 : #define XML_HTTP_REQUEST_MULTIPART (1 << 11) // Internal
139 : #define XML_HTTP_REQUEST_GOT_FINAL_STOP (1 << 12) // Internal
140 : #define XML_HTTP_REQUEST_BACKGROUND (1 << 13) // Internal
141 : // This is set when we've got the headers for a multipart XMLHttpRequest,
142 : // but haven't yet started to process the first part.
143 : #define XML_HTTP_REQUEST_MPART_HEADERS (1 << 14) // Internal
144 : #define XML_HTTP_REQUEST_USE_XSITE_AC (1 << 15) // Internal
145 : #define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 16) // Internal
146 : #define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 17) // Internal
147 : #define XML_HTTP_REQUEST_TIMED_OUT (1 << 18) // Internal
148 : #define XML_HTTP_REQUEST_DELETED (1 << 19) // Internal
149 :
150 : #define XML_HTTP_REQUEST_LOADSTATES \
151 : (XML_HTTP_REQUEST_UNSENT | \
152 : XML_HTTP_REQUEST_OPENED | \
153 : XML_HTTP_REQUEST_HEADERS_RECEIVED | \
154 : XML_HTTP_REQUEST_LOADING | \
155 : XML_HTTP_REQUEST_DONE | \
156 : XML_HTTP_REQUEST_SENT | \
157 : XML_HTTP_REQUEST_STOPPED)
158 :
159 : #define NS_BADCERTHANDLER_CONTRACTID \
160 : "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
161 :
162 : #define NS_PROGRESS_EVENT_INTERVAL 50
163 :
164 0 : NS_IMPL_ISUPPORTS1(nsXHRParseEndListener, nsIDOMEventListener)
165 :
166 : class nsResumeTimeoutsEvent : public nsRunnable
167 0 : {
168 : public:
169 0 : nsResumeTimeoutsEvent(nsPIDOMWindow* aWindow) : mWindow(aWindow) {}
170 :
171 0 : NS_IMETHOD Run()
172 : {
173 0 : mWindow->ResumeTimeouts(false);
174 0 : return NS_OK;
175 : }
176 :
177 : private:
178 : nsCOMPtr<nsPIDOMWindow> mWindow;
179 : };
180 :
181 :
182 : // This helper function adds the given load flags to the request's existing
183 : // load flags.
184 62 : static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
185 : {
186 : nsLoadFlags flags;
187 62 : request->GetLoadFlags(&flags);
188 62 : flags |= newFlags;
189 62 : request->SetLoadFlags(flags);
190 62 : }
191 :
192 200 : static nsresult IsCapabilityEnabled(const char *capability, bool *enabled)
193 : {
194 200 : nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
195 200 : if (!secMan)
196 0 : return NS_ERROR_FAILURE;
197 :
198 200 : return secMan->IsCapabilityEnabled(capability, enabled);
199 : }
200 :
201 : // Helper proxy class to be used when expecting an
202 : // multipart/x-mixed-replace stream of XML documents.
203 :
204 : class nsMultipartProxyListener : public nsIStreamListener
205 : {
206 : public:
207 : nsMultipartProxyListener(nsIStreamListener *dest);
208 : virtual ~nsMultipartProxyListener();
209 :
210 : /* additional members */
211 : NS_DECL_ISUPPORTS
212 : NS_DECL_NSISTREAMLISTENER
213 : NS_DECL_NSIREQUESTOBSERVER
214 :
215 : private:
216 : nsCOMPtr<nsIStreamListener> mDestListener;
217 : };
218 :
219 :
220 0 : nsMultipartProxyListener::nsMultipartProxyListener(nsIStreamListener *dest)
221 0 : : mDestListener(dest)
222 : {
223 0 : }
224 :
225 0 : nsMultipartProxyListener::~nsMultipartProxyListener()
226 : {
227 0 : }
228 :
229 0 : NS_IMPL_ISUPPORTS2(nsMultipartProxyListener, nsIStreamListener,
230 : nsIRequestObserver)
231 :
232 : /** nsIRequestObserver methods **/
233 :
234 : NS_IMETHODIMP
235 0 : nsMultipartProxyListener::OnStartRequest(nsIRequest *aRequest,
236 : nsISupports *ctxt)
237 : {
238 0 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
239 0 : NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
240 :
241 0 : nsCAutoString contentType;
242 0 : nsresult rv = channel->GetContentType(contentType);
243 :
244 0 : if (!contentType.EqualsLiteral("multipart/x-mixed-replace")) {
245 0 : return NS_ERROR_INVALID_ARG;
246 : }
247 :
248 : // If multipart/x-mixed-replace content, we'll insert a MIME
249 : // decoder in the pipeline to handle the content and pass it along
250 : // to our original listener.
251 :
252 0 : nsCOMPtr<nsIXMLHttpRequest> xhr = do_QueryInterface(mDestListener);
253 :
254 : nsCOMPtr<nsIStreamConverterService> convServ =
255 0 : do_GetService("@mozilla.org/streamConverters;1", &rv);
256 0 : if (NS_SUCCEEDED(rv)) {
257 0 : nsCOMPtr<nsIStreamListener> toListener(mDestListener);
258 0 : nsCOMPtr<nsIStreamListener> fromListener;
259 :
260 0 : rv = convServ->AsyncConvertData("multipart/x-mixed-replace",
261 : "*/*",
262 : toListener,
263 : nsnull,
264 0 : getter_AddRefs(fromListener));
265 0 : NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && fromListener, NS_ERROR_UNEXPECTED);
266 :
267 0 : mDestListener = fromListener;
268 : }
269 :
270 0 : if (xhr) {
271 0 : static_cast<nsXMLHttpRequest*>(xhr.get())->mState |=
272 0 : XML_HTTP_REQUEST_MPART_HEADERS;
273 : }
274 :
275 0 : return mDestListener->OnStartRequest(aRequest, ctxt);
276 : }
277 :
278 : NS_IMETHODIMP
279 0 : nsMultipartProxyListener::OnStopRequest(nsIRequest *aRequest,
280 : nsISupports *ctxt,
281 : nsresult status)
282 : {
283 0 : return mDestListener->OnStopRequest(aRequest, ctxt, status);
284 : }
285 :
286 : /** nsIStreamListener methods **/
287 :
288 : NS_IMETHODIMP
289 0 : nsMultipartProxyListener::OnDataAvailable(nsIRequest *aRequest,
290 : nsISupports *ctxt,
291 : nsIInputStream *inStr,
292 : PRUint32 sourceOffset,
293 : PRUint32 count)
294 : {
295 0 : return mDestListener->OnDataAvailable(aRequest, ctxt, inStr, sourceOffset,
296 0 : count);
297 : }
298 :
299 : /////////////////////////////////////////////
300 :
301 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
302 :
303 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXHREventTarget,
304 : nsDOMEventTargetHelper)
305 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadListener)
306 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener)
307 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnAbortListener)
308 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadStartListener)
309 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnProgressListener)
310 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnLoadendListener)
311 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnTimeoutListener)
312 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
313 :
314 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXHREventTarget,
315 : nsDOMEventTargetHelper)
316 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadListener)
317 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener)
318 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnAbortListener)
319 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadStartListener)
320 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnProgressListener)
321 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnLoadendListener)
322 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnTimeoutListener)
323 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
324 :
325 38012 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXHREventTarget)
326 38012 : NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
327 38012 : NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
328 :
329 83921 : NS_IMPL_ADDREF_INHERITED(nsXHREventTarget, nsDOMEventTargetHelper)
330 83921 : NS_IMPL_RELEASE_INHERITED(nsXHREventTarget, nsDOMEventTargetHelper)
331 :
332 : void
333 0 : nsXHREventTarget::DisconnectFromOwner()
334 : {
335 0 : nsDOMEventTargetHelper::DisconnectFromOwner();
336 0 : NS_DISCONNECT_EVENT_HANDLER(Load)
337 0 : NS_DISCONNECT_EVENT_HANDLER(Error)
338 0 : NS_DISCONNECT_EVENT_HANDLER(Abort)
339 0 : NS_DISCONNECT_EVENT_HANDLER(Load)
340 0 : NS_DISCONNECT_EVENT_HANDLER(Progress)
341 0 : NS_DISCONNECT_EVENT_HANDLER(Loadend)
342 0 : NS_DISCONNECT_EVENT_HANDLER(Timeout)
343 0 : }
344 :
345 : NS_IMETHODIMP
346 0 : nsXHREventTarget::GetOnload(nsIDOMEventListener** aOnLoad)
347 : {
348 0 : return GetInnerEventListener(mOnLoadListener, aOnLoad);
349 : }
350 :
351 : NS_IMETHODIMP
352 0 : nsXHREventTarget::SetOnload(nsIDOMEventListener* aOnLoad)
353 : {
354 0 : return RemoveAddEventListener(NS_LITERAL_STRING(LOAD_STR),
355 0 : mOnLoadListener, aOnLoad);
356 : }
357 :
358 : NS_IMETHODIMP
359 0 : nsXHREventTarget::GetOnerror(nsIDOMEventListener** aOnerror)
360 : {
361 0 : return GetInnerEventListener(mOnErrorListener, aOnerror);
362 : }
363 :
364 : NS_IMETHODIMP
365 0 : nsXHREventTarget::SetOnerror(nsIDOMEventListener* aOnerror)
366 : {
367 0 : return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_STR),
368 0 : mOnErrorListener, aOnerror);
369 : }
370 :
371 : NS_IMETHODIMP
372 0 : nsXHREventTarget::GetOnabort(nsIDOMEventListener** aOnabort)
373 : {
374 0 : return GetInnerEventListener(mOnAbortListener, aOnabort);
375 : }
376 :
377 : NS_IMETHODIMP
378 0 : nsXHREventTarget::SetOnabort(nsIDOMEventListener* aOnabort)
379 : {
380 0 : return RemoveAddEventListener(NS_LITERAL_STRING(ABORT_STR),
381 0 : mOnAbortListener, aOnabort);
382 : }
383 :
384 : NS_IMETHODIMP
385 0 : nsXHREventTarget::GetOnloadstart(nsIDOMEventListener** aOnloadstart)
386 : {
387 0 : return GetInnerEventListener(mOnLoadStartListener, aOnloadstart);
388 : }
389 :
390 : NS_IMETHODIMP
391 0 : nsXHREventTarget::SetOnloadstart(nsIDOMEventListener* aOnloadstart)
392 : {
393 0 : return RemoveAddEventListener(NS_LITERAL_STRING(LOADSTART_STR),
394 0 : mOnLoadStartListener, aOnloadstart);
395 : }
396 :
397 : NS_IMETHODIMP
398 0 : nsXHREventTarget::GetOnprogress(nsIDOMEventListener** aOnprogress)
399 : {
400 0 : return GetInnerEventListener(mOnProgressListener, aOnprogress);
401 : }
402 :
403 : NS_IMETHODIMP
404 0 : nsXHREventTarget::SetOnprogress(nsIDOMEventListener* aOnprogress)
405 : {
406 0 : return RemoveAddEventListener(NS_LITERAL_STRING(PROGRESS_STR),
407 0 : mOnProgressListener, aOnprogress);
408 : }
409 :
410 : /* attribute nsIDOMEventListener ontimeout; */
411 : NS_IMETHODIMP
412 0 : nsXHREventTarget::GetOntimeout(nsIDOMEventListener * *aOntimeout)
413 : {
414 0 : return GetInnerEventListener(mOnTimeoutListener, aOntimeout);
415 : }
416 : NS_IMETHODIMP
417 0 : nsXHREventTarget::SetOntimeout(nsIDOMEventListener *aOntimeout)
418 : {
419 0 : return RemoveAddEventListener(NS_LITERAL_STRING(TIMEOUT_STR),
420 0 : mOnTimeoutListener, aOntimeout);
421 : }
422 :
423 : NS_IMETHODIMP
424 0 : nsXHREventTarget::GetOnloadend(nsIDOMEventListener** aOnLoadend)
425 : {
426 0 : return GetInnerEventListener(mOnLoadendListener, aOnLoadend);
427 : }
428 :
429 : NS_IMETHODIMP
430 0 : nsXHREventTarget::SetOnloadend(nsIDOMEventListener* aOnLoadend)
431 : {
432 0 : return RemoveAddEventListener(NS_LITERAL_STRING(LOADEND_STR),
433 0 : mOnLoadendListener, aOnLoadend);
434 : }
435 :
436 : /////////////////////////////////////////////
437 :
438 : DOMCI_DATA(XMLHttpRequestUpload, nsXMLHttpRequestUpload)
439 :
440 0 : NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequestUpload)
441 0 : NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestUpload)
442 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XMLHttpRequestUpload)
443 0 : NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
444 :
445 0 : NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
446 0 : NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
447 :
448 : /////////////////////////////////////////////
449 : //
450 : //
451 : /////////////////////////////////////////////
452 :
453 596 : nsXMLHttpRequest::nsXMLHttpRequest()
454 : : mResponseBodyDecodedPos(0),
455 : mResponseType(XML_HTTP_RESPONSE_TYPE_DEFAULT),
456 : mRequestObserver(nsnull), mState(XML_HTTP_REQUEST_UNSENT),
457 : mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
458 : mProgressSinceLastProgressEvent(false),
459 : mUploadProgress(0), mUploadProgressMax(0),
460 : mRequestSentTime(0), mTimeoutMilliseconds(0),
461 : mErrorLoad(false), mWaitingForOnStopRequest(false),
462 : mProgressTimerIsActive(false), mProgressEventWasDelayed(false),
463 : mIsHtml(false),
464 : mWarnAboutMultipartHtml(false),
465 : mWarnAboutSyncHtml(false),
466 : mLoadLengthComputable(false), mLoadTotal(0),
467 : mFirstStartRequestSeen(false),
468 : mInLoadProgressEvent(false),
469 : mResultJSON(JSVAL_VOID),
470 596 : mResultArrayBuffer(nsnull)
471 : {
472 596 : nsLayoutStatics::AddRef();
473 596 : }
474 :
475 1788 : nsXMLHttpRequest::~nsXMLHttpRequest()
476 : {
477 596 : mState |= XML_HTTP_REQUEST_DELETED;
478 :
479 596 : if (mState & (XML_HTTP_REQUEST_STOPPED |
480 : XML_HTTP_REQUEST_SENT |
481 : XML_HTTP_REQUEST_LOADING)) {
482 0 : Abort();
483 : }
484 :
485 596 : NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
486 596 : mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
487 :
488 596 : nsLayoutStatics::Release();
489 2384 : }
490 :
491 : void
492 0 : nsXMLHttpRequest::RootResultArrayBuffer()
493 : {
494 : nsContentUtils::PreserveWrapper(
495 : static_cast<nsIDOMEventTarget*>(
496 0 : static_cast<nsDOMEventTargetHelper*>(this)), this);
497 0 : }
498 :
499 : /**
500 : * This Init method is called from the factory constructor.
501 : */
502 : nsresult
503 596 : nsXMLHttpRequest::Init()
504 : {
505 : // Set the original mPrincipal, if available.
506 : // Get JSContext from stack.
507 : nsCOMPtr<nsIJSContextStack> stack =
508 1192 : do_GetService("@mozilla.org/js/xpc/ContextStack;1");
509 :
510 596 : if (!stack) {
511 0 : return NS_OK;
512 : }
513 :
514 : JSContext *cx;
515 :
516 596 : if (NS_FAILED(stack->Peek(&cx)) || !cx) {
517 1 : return NS_OK;
518 : }
519 :
520 595 : nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
521 1190 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
522 595 : if (secMan) {
523 595 : nsresult rv = secMan->GetSubjectPrincipal(getter_AddRefs(subjectPrincipal));
524 595 : NS_ENSURE_SUCCESS(rv, rv);
525 : }
526 595 : NS_ENSURE_STATE(subjectPrincipal);
527 595 : mPrincipal = subjectPrincipal;
528 :
529 595 : nsIScriptContext* context = GetScriptContextFromJSContext(cx);
530 595 : if (context) {
531 : nsCOMPtr<nsPIDOMWindow> window =
532 0 : do_QueryInterface(context->GetGlobalObject());
533 0 : BindToOwner(window ? window->GetCurrentInnerWindow() : nsnull);
534 : }
535 :
536 595 : return NS_OK;
537 : }
538 : /**
539 : * This Init method should only be called by C++ consumers.
540 : */
541 : NS_IMETHODIMP
542 1 : nsXMLHttpRequest::Init(nsIPrincipal* aPrincipal,
543 : nsIScriptContext* aScriptContext,
544 : nsPIDOMWindow* aOwnerWindow,
545 : nsIURI* aBaseURI)
546 : {
547 1 : NS_ENSURE_ARG_POINTER(aPrincipal);
548 :
549 1 : mPrincipal = aPrincipal;
550 1 : BindToOwner(aOwnerWindow ? aOwnerWindow->GetCurrentInnerWindow() : nsnull);
551 1 : mBaseURI = aBaseURI;
552 :
553 1 : return NS_OK;
554 : }
555 :
556 : /**
557 : * This Initialize method is called from XPConnect via nsIJSNativeInitializer.
558 : */
559 : NS_IMETHODIMP
560 0 : nsXMLHttpRequest::Initialize(nsISupports* aOwner, JSContext* cx, JSObject* obj,
561 : PRUint32 argc, jsval *argv)
562 : {
563 0 : nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aOwner);
564 0 : if (!owner) {
565 0 : NS_WARNING("Unexpected nsIJSNativeInitializer owner");
566 0 : return NS_OK;
567 : }
568 :
569 : // This XHR object is bound to a |window|,
570 : // so re-set principal and script context.
571 0 : nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = do_QueryInterface(aOwner);
572 0 : NS_ENSURE_STATE(scriptPrincipal);
573 0 : mPrincipal = scriptPrincipal->GetPrincipal();
574 0 : BindToOwner(owner);
575 0 : return NS_OK;
576 : }
577 :
578 : void
579 1194 : nsXMLHttpRequest::ResetResponse()
580 : {
581 1194 : mResponseXML = nsnull;
582 1194 : mResponseBody.Truncate();
583 1194 : mResponseText.Truncate();
584 1194 : mResponseBlob = nsnull;
585 1194 : mDOMFile = nsnull;
586 1194 : mBuilder = nsnull;
587 1194 : mResultArrayBuffer = nsnull;
588 1194 : mResultJSON = JSVAL_VOID;
589 1194 : mLoadTransferred = 0;
590 1194 : mResponseBodyDecodedPos = 0;
591 1194 : }
592 :
593 : void
594 0 : nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
595 : {
596 0 : mRequestObserver = aObserver;
597 0 : }
598 :
599 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
600 :
601 324 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsXMLHttpRequest)
602 324 : bool isBlack = tmp->IsBlack();
603 324 : if (isBlack || tmp->mWaitingForOnStopRequest) {
604 189 : if (tmp->mListenerManager) {
605 187 : tmp->mListenerManager->UnmarkGrayJSListeners();
606 187 : NS_UNMARK_LISTENER_WRAPPER(Load)
607 187 : NS_UNMARK_LISTENER_WRAPPER(Error)
608 187 : NS_UNMARK_LISTENER_WRAPPER(Abort)
609 187 : NS_UNMARK_LISTENER_WRAPPER(LoadStart)
610 187 : NS_UNMARK_LISTENER_WRAPPER(Progress)
611 187 : NS_UNMARK_LISTENER_WRAPPER(Loadend)
612 187 : NS_UNMARK_LISTENER_WRAPPER(UploadProgress)
613 187 : NS_UNMARK_LISTENER_WRAPPER(Readystatechange)
614 : }
615 189 : if (!isBlack) {
616 32 : xpc_UnmarkGrayObject(tmp->PreservingWrapper() ?
617 0 : tmp->GetWrapperPreserveColor() :
618 32 : tmp->GetExpandoObjectPreserveColor());
619 : }
620 189 : return true;
621 : }
622 135 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
623 :
624 23 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsXMLHttpRequest)
625 23 : return tmp->IsBlack();
626 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
627 :
628 38 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXMLHttpRequest)
629 38 : return tmp->IsBlack();
630 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
631 :
632 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
633 : nsXHREventTarget)
634 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContext)
635 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannel)
636 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mReadRequest)
637 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mResponseXML)
638 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCORSPreflightChannel)
639 :
640 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnUploadProgressListener)
641 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnReadystatechangeListener)
642 :
643 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mXMLParserStreamListener)
644 :
645 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mChannelEventSink)
646 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mProgressEventSink)
647 :
648 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mUpload,
649 : nsIXMLHttpRequestUpload)
650 25 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
651 :
652 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
653 : nsXHREventTarget)
654 10 : tmp->mResultArrayBuffer = nsnull;
655 10 : tmp->mResultJSON = JSVAL_VOID;
656 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
657 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannel)
658 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mReadRequest)
659 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mResponseXML)
660 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCORSPreflightChannel)
661 :
662 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnUploadProgressListener)
663 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnReadystatechangeListener)
664 :
665 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXMLParserStreamListener)
666 :
667 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mChannelEventSink)
668 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mProgressEventSink)
669 :
670 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mUpload)
671 10 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
672 :
673 25 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsXMLHttpRequest,
674 : nsXHREventTarget)
675 25 : if(tmp->mResultArrayBuffer) {
676 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mResultArrayBuffer,
677 : "mResultArrayBuffer")
678 : }
679 25 : if (JSVAL_IS_GCTHING(tmp->mResultJSON)) {
680 0 : void *gcThing = JSVAL_TO_GCTHING(tmp->mResultJSON);
681 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mResultJSON")
682 : }
683 25 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
684 :
685 : DOMCI_DATA(XMLHttpRequest, nsXMLHttpRequest)
686 :
687 : // QueryInterface implementation for nsXMLHttpRequest
688 87404 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXMLHttpRequest)
689 46432 : NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
690 42409 : NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
691 42269 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
692 42269 : NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
693 40574 : NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
694 40539 : NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
695 39924 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
696 38633 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
697 38633 : NS_INTERFACE_MAP_ENTRY(nsIJSNativeInitializer)
698 38633 : NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
699 38629 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XMLHttpRequest)
700 38012 : NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
701 :
702 83921 : NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
703 83921 : NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
704 :
705 : void
706 0 : nsXMLHttpRequest::DisconnectFromOwner()
707 : {
708 0 : nsXHREventTarget::DisconnectFromOwner();
709 0 : NS_DISCONNECT_EVENT_HANDLER(UploadProgress)
710 0 : NS_DISCONNECT_EVENT_HANDLER(Readystatechange)
711 0 : Abort();
712 0 : }
713 :
714 : NS_IMETHODIMP
715 0 : nsXMLHttpRequest::GetOnreadystatechange(nsIDOMEventListener * *aOnreadystatechange)
716 : {
717 : return
718 : nsXHREventTarget::GetInnerEventListener(mOnReadystatechangeListener,
719 0 : aOnreadystatechange);
720 : }
721 :
722 : NS_IMETHODIMP
723 0 : nsXMLHttpRequest::SetOnreadystatechange(nsIDOMEventListener * aOnreadystatechange)
724 : {
725 : return
726 0 : nsXHREventTarget::RemoveAddEventListener(NS_LITERAL_STRING(READYSTATE_STR),
727 : mOnReadystatechangeListener,
728 0 : aOnreadystatechange);
729 : }
730 :
731 : NS_IMETHODIMP
732 0 : nsXMLHttpRequest::GetOnuploadprogress(nsIDOMEventListener * *aOnuploadprogress)
733 : {
734 : return
735 : nsXHREventTarget::GetInnerEventListener(mOnUploadProgressListener,
736 0 : aOnuploadprogress);
737 : }
738 :
739 : NS_IMETHODIMP
740 0 : nsXMLHttpRequest::SetOnuploadprogress(nsIDOMEventListener * aOnuploadprogress)
741 : {
742 : return
743 0 : nsXHREventTarget::RemoveAddEventListener(NS_LITERAL_STRING(UPLOADPROGRESS_STR),
744 : mOnUploadProgressListener,
745 0 : aOnuploadprogress);
746 : }
747 :
748 : /* readonly attribute nsIChannel channel; */
749 : NS_IMETHODIMP
750 1913 : nsXMLHttpRequest::GetChannel(nsIChannel **aChannel)
751 : {
752 1913 : NS_ENSURE_ARG_POINTER(aChannel);
753 1913 : NS_IF_ADDREF(*aChannel = mChannel);
754 :
755 1913 : return NS_OK;
756 : }
757 :
758 0 : static void LogMessage(const char* aWarning, nsPIDOMWindow* aWindow)
759 : {
760 0 : nsCOMPtr<nsIDocument> doc;
761 0 : if (aWindow) {
762 0 : doc = do_QueryInterface(aWindow->GetExtantDocument());
763 : }
764 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
765 : "DOM", doc,
766 : nsContentUtils::eDOM_PROPERTIES,
767 0 : aWarning);
768 0 : }
769 :
770 : /* readonly attribute nsIDOMDocument responseXML; */
771 : NS_IMETHODIMP
772 393 : nsXMLHttpRequest::GetResponseXML(nsIDOMDocument **aResponseXML)
773 : {
774 393 : NS_ENSURE_ARG_POINTER(aResponseXML);
775 393 : *aResponseXML = nsnull;
776 393 : if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
777 : mResponseType != XML_HTTP_RESPONSE_TYPE_DOCUMENT) {
778 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
779 : }
780 393 : if ((XML_HTTP_REQUEST_DONE & mState) && mResponseXML) {
781 393 : *aResponseXML = mResponseXML;
782 393 : NS_ADDREF(*aResponseXML);
783 : }
784 393 : if (mWarnAboutMultipartHtml) {
785 0 : mWarnAboutMultipartHtml = false;
786 0 : LogMessage("HTMLMultipartXHRWarning", GetOwner());
787 : }
788 393 : if (mWarnAboutSyncHtml) {
789 0 : mWarnAboutSyncHtml = false;
790 0 : LogMessage("HTMLSyncXHRWarning", GetOwner());
791 : }
792 393 : return NS_OK;
793 : }
794 :
795 : /*
796 : * This piece copied from nsXMLDocument, we try to get the charset
797 : * from HTTP headers.
798 : */
799 : nsresult
800 585 : nsXMLHttpRequest::DetectCharset()
801 : {
802 585 : mResponseCharset.Truncate();
803 585 : mDecoder = nsnull;
804 :
805 585 : if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
806 : mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT &&
807 : mResponseType != XML_HTTP_RESPONSE_TYPE_JSON &&
808 : mResponseType != XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
809 0 : return NS_OK;
810 : }
811 :
812 1170 : nsCOMPtr<nsIChannel> channel = do_QueryInterface(mReadRequest);
813 585 : if (!channel) {
814 0 : channel = mChannel;
815 : }
816 :
817 1170 : nsCAutoString charsetVal;
818 585 : nsresult rv = channel ? channel->GetContentCharset(charsetVal) :
819 1170 : NS_ERROR_FAILURE;
820 585 : if (NS_SUCCEEDED(rv)) {
821 514 : rv = nsCharsetAlias::GetPreferred(charsetVal, mResponseCharset);
822 : }
823 :
824 585 : if (NS_FAILED(rv) || mResponseCharset.IsEmpty()) {
825 : // MS documentation states UTF-8 is default for responseText
826 581 : mResponseCharset.AssignLiteral("UTF-8");
827 : }
828 :
829 585 : if (mResponseType == XML_HTTP_RESPONSE_TYPE_JSON &&
830 0 : !mResponseCharset.EqualsLiteral("UTF-8")) {
831 : // The XHR spec says only UTF-8 is supported for responseType == "json"
832 0 : LogMessage("JSONCharsetWarning", GetOwner());
833 0 : mResponseCharset.AssignLiteral("UTF-8");
834 : }
835 :
836 : nsCOMPtr<nsICharsetConverterManager> ccm =
837 1170 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
838 585 : NS_ENSURE_SUCCESS(rv, rv);
839 :
840 585 : return ccm->GetUnicodeDecoderRaw(mResponseCharset.get(),
841 585 : getter_AddRefs(mDecoder));
842 : }
843 :
844 : nsresult
845 309 : nsXMLHttpRequest::AppendToResponseText(const char * aSrcBuffer,
846 : PRUint32 aSrcBufferLen)
847 : {
848 309 : NS_ENSURE_STATE(mDecoder);
849 :
850 : PRInt32 destBufferLen;
851 309 : nsresult rv = mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen,
852 309 : &destBufferLen);
853 309 : NS_ENSURE_SUCCESS(rv, rv);
854 :
855 309 : if (!mResponseText.SetCapacity(mResponseText.Length() + destBufferLen)) {
856 0 : return NS_ERROR_OUT_OF_MEMORY;
857 : }
858 :
859 309 : PRUnichar* destBuffer = mResponseText.BeginWriting() + mResponseText.Length();
860 :
861 309 : PRInt32 totalChars = mResponseText.Length();
862 :
863 : // This code here is basically a copy of a similar thing in
864 : // nsScanner::Append(const char* aBuffer, PRUint32 aLen).
865 : // If we get illegal characters in the input we replace
866 : // them and don't just fail.
867 601 : do {
868 601 : PRInt32 srclen = (PRInt32)aSrcBufferLen;
869 601 : PRInt32 destlen = (PRInt32)destBufferLen;
870 601 : rv = mDecoder->Convert(aSrcBuffer,
871 : &srclen,
872 : destBuffer,
873 601 : &destlen);
874 601 : if (NS_FAILED(rv)) {
875 : // We consume one byte, replace it with U+FFFD
876 : // and try the conversion again.
877 :
878 292 : destBuffer[destlen] = (PRUnichar)0xFFFD; // add replacement character
879 292 : destlen++; // skip written replacement character
880 292 : destBuffer += destlen;
881 292 : destBufferLen -= destlen;
882 :
883 292 : if (srclen < (PRInt32)aSrcBufferLen) {
884 292 : srclen++; // Consume the invalid character
885 : }
886 292 : aSrcBuffer += srclen;
887 292 : aSrcBufferLen -= srclen;
888 :
889 292 : mDecoder->Reset();
890 : }
891 :
892 601 : totalChars += destlen;
893 :
894 601 : } while (NS_FAILED(rv) && aSrcBufferLen > 0);
895 :
896 309 : mResponseText.SetLength(totalChars);
897 :
898 309 : return NS_OK;
899 : }
900 :
901 : /* readonly attribute AString responseText; */
902 352 : NS_IMETHODIMP nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
903 : {
904 352 : aResponseText.Truncate();
905 :
906 352 : if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT &&
907 : mResponseType != XML_HTTP_RESPONSE_TYPE_TEXT &&
908 : mResponseType != XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
909 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
910 : }
911 :
912 352 : if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT &&
913 0 : !mInLoadProgressEvent) {
914 0 : aResponseText.SetIsVoid(true);
915 0 : return NS_OK;
916 : }
917 :
918 352 : if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
919 0 : return NS_OK;
920 : }
921 :
922 : // We only decode text lazily if we're also parsing to a doc.
923 : // Also, if we've decoded all current data already, then no need to decode
924 : // more.
925 698 : if (!mResponseXML ||
926 346 : mResponseBodyDecodedPos == mResponseBody.Length()) {
927 50 : aResponseText = mResponseText;
928 50 : return NS_OK;
929 : }
930 :
931 : nsresult rv;
932 :
933 604 : nsCOMPtr<nsIDocument> document = do_QueryInterface(mResponseXML);
934 302 : if (mResponseCharset != document->GetDocumentCharacterSet()) {
935 0 : mResponseCharset = document->GetDocumentCharacterSet();
936 0 : mResponseText.Truncate();
937 0 : mResponseBodyDecodedPos = 0;
938 :
939 : nsCOMPtr<nsICharsetConverterManager> ccm =
940 0 : do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
941 0 : NS_ENSURE_SUCCESS(rv, rv);
942 :
943 0 : rv = ccm->GetUnicodeDecoderRaw(mResponseCharset.get(),
944 0 : getter_AddRefs(mDecoder));
945 0 : NS_ENSURE_SUCCESS(rv, rv);
946 : }
947 :
948 302 : NS_ASSERTION(mResponseBodyDecodedPos < mResponseBody.Length(),
949 : "Unexpected mResponseBodyDecodedPos");
950 302 : rv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
951 604 : mResponseBody.Length() - mResponseBodyDecodedPos);
952 302 : NS_ENSURE_SUCCESS(rv, rv);
953 :
954 302 : mResponseBodyDecodedPos = mResponseBody.Length();
955 :
956 302 : if (mState & XML_HTTP_REQUEST_DONE) {
957 : // Free memory buffer which we no longer need
958 302 : mResponseBody.Truncate();
959 302 : mResponseBodyDecodedPos = 0;
960 : }
961 :
962 302 : aResponseText = mResponseText;
963 :
964 302 : return NS_OK;
965 : }
966 :
967 : nsresult
968 0 : nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx)
969 : {
970 0 : if (!aCx) {
971 0 : return NS_ERROR_FAILURE;
972 : }
973 : // The Unicode converter has already zapped the BOM if there was one
974 0 : if (!JS_ParseJSON(aCx,
975 0 : (jschar*)mResponseText.get(),
976 0 : mResponseText.Length(), &mResultJSON)) {
977 0 : return NS_ERROR_FAILURE;
978 : }
979 :
980 0 : return NS_OK;
981 : }
982 :
983 : nsresult
984 0 : nsXMLHttpRequest::CreatePartialBlob()
985 : {
986 0 : if (mDOMFile) {
987 0 : if (mLoadTotal == mLoadTransferred) {
988 0 : mResponseBlob = mDOMFile;
989 : } else {
990 : mResponseBlob =
991 0 : mDOMFile->CreateSlice(0, mLoadTransferred, EmptyString());
992 : }
993 0 : return NS_OK;
994 : }
995 :
996 0 : nsCAutoString contentType;
997 0 : if (mLoadTotal == mLoadTransferred) {
998 0 : mChannel->GetContentType(contentType);
999 : }
1000 :
1001 0 : return mBuilder->GetBlobInternal(NS_ConvertASCIItoUTF16(contentType),
1002 0 : false, getter_AddRefs(mResponseBlob));
1003 : }
1004 :
1005 : /* attribute AString responseType; */
1006 0 : NS_IMETHODIMP nsXMLHttpRequest::GetResponseType(nsAString& aResponseType)
1007 : {
1008 0 : switch (mResponseType) {
1009 : case XML_HTTP_RESPONSE_TYPE_DEFAULT:
1010 0 : aResponseType.Truncate();
1011 0 : break;
1012 : case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
1013 0 : aResponseType.AssignLiteral("arraybuffer");
1014 0 : break;
1015 : case XML_HTTP_RESPONSE_TYPE_BLOB:
1016 0 : aResponseType.AssignLiteral("blob");
1017 0 : break;
1018 : case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
1019 0 : aResponseType.AssignLiteral("document");
1020 0 : break;
1021 : case XML_HTTP_RESPONSE_TYPE_TEXT:
1022 0 : aResponseType.AssignLiteral("text");
1023 0 : break;
1024 : case XML_HTTP_RESPONSE_TYPE_JSON:
1025 0 : aResponseType.AssignLiteral("json");
1026 0 : break;
1027 : case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
1028 0 : aResponseType.AssignLiteral("moz-chunked-text");
1029 0 : break;
1030 : case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
1031 0 : aResponseType.AssignLiteral("moz-chunked-arraybuffer");
1032 0 : break;
1033 : case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
1034 0 : aResponseType.AssignLiteral("moz-blob");
1035 0 : break;
1036 : default:
1037 0 : NS_ERROR("Should not happen");
1038 : }
1039 :
1040 0 : return NS_OK;
1041 : }
1042 :
1043 : /* attribute AString responseType; */
1044 1 : NS_IMETHODIMP nsXMLHttpRequest::SetResponseType(const nsAString& aResponseType)
1045 : {
1046 : // If the state is not OPENED or HEADERS_RECEIVED raise an
1047 : // INVALID_STATE_ERR exception and terminate these steps.
1048 1 : if (!(mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT |
1049 1 : XML_HTTP_REQUEST_HEADERS_RECEIVED)))
1050 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1051 :
1052 : // sync request is not allowed setting responseType in window context
1053 1 : if (HasOrHasHadOwner() &&
1054 0 : !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
1055 0 : LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
1056 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1057 : }
1058 :
1059 : // Set the responseType attribute's value to the given value.
1060 1 : if (aResponseType.IsEmpty()) {
1061 1 : mResponseType = XML_HTTP_RESPONSE_TYPE_DEFAULT;
1062 0 : } else if (aResponseType.EqualsLiteral("arraybuffer")) {
1063 0 : mResponseType = XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER;
1064 0 : } else if (aResponseType.EqualsLiteral("blob")) {
1065 0 : mResponseType = XML_HTTP_RESPONSE_TYPE_BLOB;
1066 0 : } else if (aResponseType.EqualsLiteral("document")) {
1067 0 : mResponseType = XML_HTTP_RESPONSE_TYPE_DOCUMENT;
1068 0 : } else if (aResponseType.EqualsLiteral("text")) {
1069 0 : mResponseType = XML_HTTP_RESPONSE_TYPE_TEXT;
1070 0 : } else if (aResponseType.EqualsLiteral("json")) {
1071 0 : mResponseType = XML_HTTP_RESPONSE_TYPE_JSON;
1072 0 : } else if (aResponseType.EqualsLiteral("moz-chunked-text")) {
1073 0 : if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
1074 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1075 : }
1076 0 : mResponseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT;
1077 0 : } else if (aResponseType.EqualsLiteral("moz-chunked-arraybuffer")) {
1078 0 : if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
1079 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1080 : }
1081 0 : mResponseType = XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER;
1082 0 : } else if (aResponseType.EqualsLiteral("moz-blob")) {
1083 0 : mResponseType = XML_HTTP_RESPONSE_TYPE_MOZ_BLOB;
1084 : }
1085 : // If the given value is not the empty string, "arraybuffer",
1086 : // "blob", "document", or "text" terminate these steps.
1087 :
1088 : // If the state is OPENED, SetCacheAsFile would have no effect here
1089 : // because the channel hasn't initialized the cache entry yet.
1090 : // SetCacheAsFile will be called from OnStartRequest.
1091 : // If the state is HEADERS_RECEIVED, however, we need to call
1092 : // it immediately because OnStartRequest is already dispatched.
1093 1 : if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
1094 0 : nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
1095 0 : if (cc) {
1096 0 : cc->SetCacheAsFile(mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
1097 0 : mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB);
1098 : }
1099 : }
1100 :
1101 1 : return NS_OK;
1102 : }
1103 :
1104 : /* readonly attribute jsval response; */
1105 0 : NS_IMETHODIMP nsXMLHttpRequest::GetResponse(JSContext *aCx, jsval *aResult)
1106 : {
1107 0 : nsresult rv = NS_OK;
1108 :
1109 0 : switch (mResponseType) {
1110 : case XML_HTTP_RESPONSE_TYPE_DEFAULT:
1111 : case XML_HTTP_RESPONSE_TYPE_TEXT:
1112 : case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
1113 : {
1114 0 : nsString str;
1115 0 : rv = GetResponseText(str);
1116 0 : if (NS_FAILED(rv)) return rv;
1117 0 : NS_ENSURE_TRUE(xpc::StringToJsval(aCx, str, aResult),
1118 : NS_ERROR_OUT_OF_MEMORY);
1119 : }
1120 0 : break;
1121 :
1122 : case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
1123 : case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
1124 0 : if ((mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
1125 : mState & XML_HTTP_REQUEST_DONE) ||
1126 : (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER &&
1127 : mInLoadProgressEvent)) {
1128 0 : if (!mResultArrayBuffer) {
1129 0 : RootResultArrayBuffer();
1130 : rv = nsContentUtils::CreateArrayBuffer(aCx, mResponseBody,
1131 0 : &mResultArrayBuffer);
1132 0 : NS_ENSURE_SUCCESS(rv, rv);
1133 : }
1134 0 : *aResult = OBJECT_TO_JSVAL(mResultArrayBuffer);
1135 : } else {
1136 0 : *aResult = JSVAL_NULL;
1137 : }
1138 0 : break;
1139 :
1140 : case XML_HTTP_RESPONSE_TYPE_BLOB:
1141 : case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
1142 0 : *aResult = JSVAL_NULL;
1143 0 : if (mState & XML_HTTP_REQUEST_DONE) {
1144 : // do nothing here
1145 0 : } else if (mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
1146 0 : if (!mResponseBlob) {
1147 0 : rv = CreatePartialBlob();
1148 0 : NS_ENSURE_SUCCESS(rv, rv);
1149 : }
1150 : } else {
1151 0 : return rv;
1152 : }
1153 0 : if (mResponseBlob) {
1154 0 : JSObject* scope = JS_GetGlobalForScopeChain(aCx);
1155 : rv = nsContentUtils::WrapNative(aCx, scope, mResponseBlob, aResult,
1156 0 : nsnull, true);
1157 : }
1158 0 : break;
1159 :
1160 : case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
1161 0 : if (mState & XML_HTTP_REQUEST_DONE && mResponseXML) {
1162 0 : JSObject* scope = JS_GetGlobalForScopeChain(aCx);
1163 : rv = nsContentUtils::WrapNative(aCx, scope, mResponseXML, aResult,
1164 0 : nsnull, true);
1165 : } else {
1166 0 : *aResult = JSVAL_NULL;
1167 : }
1168 0 : break;
1169 :
1170 : case XML_HTTP_RESPONSE_TYPE_JSON:
1171 0 : if (mState & XML_HTTP_REQUEST_DONE) {
1172 0 : if (mResultJSON == JSVAL_VOID) {
1173 0 : rv = CreateResponseParsedJSON(aCx);
1174 0 : mResponseText.Truncate();
1175 0 : if (NS_FAILED(rv)) {
1176 : // Per spec, errors aren't propagated. null is returned instead.
1177 0 : rv = NS_OK;
1178 : // It would be nice to log the error to the console. That's hard to
1179 : // do without calling window.onerror as a side effect, though.
1180 0 : JS_ClearPendingException(aCx);
1181 0 : mResultJSON = JSVAL_NULL;
1182 : }
1183 : }
1184 0 : *aResult = mResultJSON;
1185 : } else {
1186 0 : *aResult = JSVAL_NULL;
1187 : }
1188 0 : break;
1189 :
1190 : default:
1191 0 : NS_ERROR("Should not happen");
1192 : }
1193 :
1194 0 : return rv;
1195 : }
1196 :
1197 : /* readonly attribute unsigned long status; */
1198 : NS_IMETHODIMP
1199 538 : nsXMLHttpRequest::GetStatus(PRUint32 *aStatus)
1200 : {
1201 538 : *aStatus = 0;
1202 :
1203 538 : if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
1204 : // Make sure we don't leak status information from denied cross-site
1205 : // requests.
1206 0 : if (mChannel) {
1207 : nsresult status;
1208 0 : mChannel->GetStatus(&status);
1209 0 : if (NS_FAILED(status)) {
1210 0 : return NS_OK;
1211 : }
1212 : }
1213 : }
1214 :
1215 1076 : nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1216 :
1217 538 : if (httpChannel) {
1218 538 : nsresult rv = httpChannel->GetResponseStatus(aStatus);
1219 538 : if (rv == NS_ERROR_NOT_AVAILABLE) {
1220 : // Someone's calling this before we got a response... Check our
1221 : // ReadyState. If we're at 3 or 4, then this means the connection
1222 : // errored before we got any data; return 0 in that case.
1223 : PRUint16 readyState;
1224 68 : GetReadyState(&readyState);
1225 68 : if (readyState >= LOADING) {
1226 68 : *aStatus = 0;
1227 68 : return NS_OK;
1228 : }
1229 : }
1230 :
1231 470 : return rv;
1232 : }
1233 :
1234 0 : return NS_OK;
1235 : }
1236 :
1237 : /* readonly attribute AUTF8String statusText; */
1238 : NS_IMETHODIMP
1239 0 : nsXMLHttpRequest::GetStatusText(nsACString& aStatusText)
1240 : {
1241 0 : nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1242 :
1243 0 : aStatusText.Truncate();
1244 :
1245 0 : if (httpChannel) {
1246 0 : if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
1247 : // Make sure we don't leak status information from denied cross-site
1248 : // requests.
1249 0 : if (mChannel) {
1250 : nsresult status;
1251 0 : mChannel->GetStatus(&status);
1252 0 : if (NS_FAILED(status)) {
1253 0 : return NS_OK;
1254 : }
1255 : }
1256 : }
1257 :
1258 0 : httpChannel->GetResponseStatusText(aStatusText);
1259 : }
1260 :
1261 0 : return NS_OK;
1262 : }
1263 :
1264 : void
1265 12 : nsXMLHttpRequest::CloseRequestWithError(const nsAString& aType,
1266 : const PRUint32 aFlag)
1267 : {
1268 12 : if (mReadRequest) {
1269 0 : mReadRequest->Cancel(NS_BINDING_ABORTED);
1270 : }
1271 12 : if (mChannel) {
1272 12 : mChannel->Cancel(NS_BINDING_ABORTED);
1273 : }
1274 12 : if (mCORSPreflightChannel) {
1275 0 : mCORSPreflightChannel->Cancel(NS_BINDING_ABORTED);
1276 : }
1277 12 : if (mTimeoutTimer) {
1278 0 : mTimeoutTimer->Cancel();
1279 : }
1280 12 : PRUint32 responseLength = mResponseBody.Length();
1281 12 : ResetResponse();
1282 12 : mState |= aFlag;
1283 :
1284 : // If we're in the destructor, don't risk dispatching an event.
1285 12 : if (mState & XML_HTTP_REQUEST_DELETED) {
1286 0 : mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
1287 0 : return;
1288 : }
1289 :
1290 12 : if (!(mState & (XML_HTTP_REQUEST_UNSENT |
1291 : XML_HTTP_REQUEST_OPENED |
1292 12 : XML_HTTP_REQUEST_DONE))) {
1293 12 : ChangeState(XML_HTTP_REQUEST_DONE, true);
1294 :
1295 12 : if (!(mState & XML_HTTP_REQUEST_SYNCLOOPING)) {
1296 : DispatchProgressEvent(this, aType, mLoadLengthComputable, responseLength,
1297 12 : mLoadTotal);
1298 12 : if (mUpload && !mUploadComplete) {
1299 0 : mUploadComplete = true;
1300 : DispatchProgressEvent(mUpload, aType, true, mUploadTransferred,
1301 0 : mUploadTotal);
1302 : }
1303 : }
1304 : }
1305 :
1306 : // The ChangeState call above calls onreadystatechange handlers which
1307 : // if they load a new url will cause nsXMLHttpRequest::Open to clear
1308 : // the abort state bit. If this occurs we're not uninitialized (bug 361773).
1309 12 : if (mState & XML_HTTP_REQUEST_ABORTED) {
1310 12 : ChangeState(XML_HTTP_REQUEST_UNSENT, false); // IE seems to do it
1311 : }
1312 :
1313 12 : mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
1314 : }
1315 :
1316 : /* void abort (); */
1317 : NS_IMETHODIMP
1318 12 : nsXMLHttpRequest::Abort()
1319 : {
1320 12 : CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR), XML_HTTP_REQUEST_ABORTED);
1321 12 : return NS_OK;
1322 : }
1323 :
1324 : /* DOMString getAllResponseHeaders(); */
1325 : NS_IMETHODIMP
1326 0 : nsXMLHttpRequest::GetAllResponseHeaders(nsAString& aResponseHeaders)
1327 : {
1328 0 : aResponseHeaders.Truncate();
1329 :
1330 : // If the state is UNSENT or OPENED,
1331 : // return the empty string and terminate these steps.
1332 0 : if (mState & (XML_HTTP_REQUEST_UNSENT |
1333 : XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
1334 0 : return NS_OK;
1335 : }
1336 :
1337 0 : if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
1338 0 : return NS_OK;
1339 : }
1340 :
1341 0 : if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
1342 0 : nsRefPtr<nsHeaderVisitor> visitor = new nsHeaderVisitor();
1343 0 : if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
1344 0 : aResponseHeaders = NS_ConvertUTF8toUTF16(visitor->Headers());
1345 : }
1346 0 : return NS_OK;
1347 : }
1348 :
1349 0 : if (!mChannel) {
1350 0 : return NS_OK;
1351 : }
1352 :
1353 : // Even non-http channels supply content type.
1354 0 : nsCAutoString value;
1355 0 : if (NS_SUCCEEDED(mChannel->GetContentType(value))) {
1356 0 : aResponseHeaders.AppendLiteral("Content-Type: ");
1357 0 : aResponseHeaders.Append(NS_ConvertUTF8toUTF16(value));
1358 0 : if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
1359 0 : !value.IsEmpty()) {
1360 0 : aResponseHeaders.AppendLiteral(";charset=");
1361 0 : aResponseHeaders.Append(NS_ConvertUTF8toUTF16(value));
1362 : }
1363 0 : aResponseHeaders.Append('\n');
1364 : }
1365 0 : return NS_OK;
1366 : }
1367 :
1368 : /* ACString getResponseHeader (in AUTF8String header); */
1369 : NS_IMETHODIMP
1370 34 : nsXMLHttpRequest::GetResponseHeader(const nsACString& header,
1371 : nsACString& _retval)
1372 : {
1373 34 : nsresult rv = NS_OK;
1374 34 : _retval.SetIsVoid(true);
1375 :
1376 68 : nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1377 :
1378 34 : if (!httpChannel) {
1379 : // If the state is UNSENT or OPENED,
1380 : // return null and terminate these steps.
1381 0 : if (mState & (XML_HTTP_REQUEST_UNSENT |
1382 : XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
1383 0 : return NS_OK;
1384 : }
1385 :
1386 : // Even non-http channels supply content type.
1387 : // Remember we don't leak header information from denied cross-site
1388 : // requests.
1389 : nsresult status;
1390 0 : if (!mChannel ||
1391 0 : NS_FAILED(mChannel->GetStatus(&status)) ||
1392 0 : NS_FAILED(status) ||
1393 0 : !header.LowerCaseEqualsASCII("content-type")) {
1394 0 : return NS_OK;
1395 : }
1396 :
1397 0 : if (NS_FAILED(mChannel->GetContentType(_retval))) {
1398 : // Means no content type
1399 0 : _retval.SetIsVoid(true);
1400 0 : return NS_OK;
1401 : }
1402 :
1403 0 : nsCString value;
1404 0 : if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
1405 0 : !value.IsEmpty()) {
1406 0 : _retval.Append(";charset=");
1407 0 : _retval.Append(value);
1408 : }
1409 :
1410 0 : return NS_OK;
1411 : }
1412 :
1413 : // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
1414 34 : bool chrome = false; // default to false in case IsCapabilityEnabled fails
1415 34 : IsCapabilityEnabled("UniversalXPConnect", &chrome);
1416 34 : if (!chrome &&
1417 0 : (header.LowerCaseEqualsASCII("set-cookie") ||
1418 0 : header.LowerCaseEqualsASCII("set-cookie2"))) {
1419 0 : NS_WARNING("blocked access to response header");
1420 0 : return NS_OK;
1421 : }
1422 :
1423 : // Check for dangerous headers
1424 34 : if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
1425 : // Make sure we don't leak header information from denied cross-site
1426 : // requests.
1427 0 : if (mChannel) {
1428 : nsresult status;
1429 0 : mChannel->GetStatus(&status);
1430 0 : if (NS_FAILED(status)) {
1431 0 : return NS_OK;
1432 : }
1433 : }
1434 :
1435 : const char *kCrossOriginSafeHeaders[] = {
1436 : "cache-control", "content-language", "content-type", "expires",
1437 : "last-modified", "pragma"
1438 0 : };
1439 0 : bool safeHeader = false;
1440 : PRUint32 i;
1441 0 : for (i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
1442 0 : if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
1443 0 : safeHeader = true;
1444 0 : break;
1445 : }
1446 : }
1447 :
1448 0 : if (!safeHeader) {
1449 0 : nsCAutoString headerVal;
1450 : // The "Access-Control-Expose-Headers" header contains a comma separated
1451 : // list of method names.
1452 0 : httpChannel->
1453 0 : GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"),
1454 0 : headerVal);
1455 0 : nsCCharSeparatedTokenizer exposeTokens(headerVal, ',');
1456 0 : while(exposeTokens.hasMoreTokens()) {
1457 0 : const nsDependentCSubstring& token = exposeTokens.nextToken();
1458 0 : if (token.IsEmpty()) {
1459 0 : continue;
1460 : }
1461 0 : if (!IsValidHTTPToken(token)) {
1462 0 : return NS_OK;
1463 : }
1464 0 : if (header.Equals(token, nsCaseInsensitiveCStringComparator())) {
1465 0 : safeHeader = true;
1466 : }
1467 : }
1468 : }
1469 :
1470 0 : if (!safeHeader) {
1471 0 : return NS_OK;
1472 : }
1473 : }
1474 :
1475 34 : rv = httpChannel->GetResponseHeader(header, _retval);
1476 34 : if (rv == NS_ERROR_NOT_AVAILABLE) {
1477 : // Means no header
1478 0 : _retval.SetIsVoid(true);
1479 0 : rv = NS_OK;
1480 : }
1481 :
1482 34 : return rv;
1483 : }
1484 :
1485 : already_AddRefed<nsILoadGroup>
1486 598 : nsXMLHttpRequest::GetLoadGroup() const
1487 : {
1488 598 : if (mState & XML_HTTP_REQUEST_BACKGROUND) {
1489 87 : return nsnull;
1490 : }
1491 :
1492 511 : nsresult rv = NS_ERROR_FAILURE;
1493 : nsIScriptContext* sc =
1494 511 : const_cast<nsXMLHttpRequest*>(this)->GetContextForEventHandlers(&rv);
1495 : nsCOMPtr<nsIDocument> doc =
1496 1022 : nsContentUtils::GetDocumentFromScriptContext(sc);
1497 511 : if (doc) {
1498 0 : return doc->GetDocumentLoadGroup();
1499 : }
1500 :
1501 511 : return nsnull;
1502 : }
1503 :
1504 : nsresult
1505 2815 : nsXMLHttpRequest::CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent)
1506 : {
1507 : nsresult rv = nsEventDispatcher::CreateEvent(nsnull, nsnull,
1508 2815 : NS_LITERAL_STRING("Events"),
1509 2815 : aDOMEvent);
1510 2815 : if (NS_FAILED(rv)) {
1511 0 : return rv;
1512 : }
1513 :
1514 5630 : nsCOMPtr<nsIPrivateDOMEvent> privevent(do_QueryInterface(*aDOMEvent));
1515 2815 : if (!privevent) {
1516 0 : NS_IF_RELEASE(*aDOMEvent);
1517 0 : return NS_ERROR_FAILURE;
1518 : }
1519 :
1520 2815 : (*aDOMEvent)->InitEvent(NS_LITERAL_STRING(READYSTATE_STR),
1521 2815 : false, false);
1522 :
1523 : // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1524 2815 : privevent->SetTrusted(true);
1525 :
1526 2815 : return NS_OK;
1527 : }
1528 :
1529 : void
1530 2255 : nsXMLHttpRequest::DispatchProgressEvent(nsDOMEventTargetHelper* aTarget,
1531 : const nsAString& aType,
1532 : bool aUseLSEventWrapper,
1533 : bool aLengthComputable,
1534 : PRUint64 aLoaded, PRUint64 aTotal,
1535 : PRUint64 aPosition, PRUint64 aTotalSize)
1536 : {
1537 2255 : NS_ASSERTION(aTarget, "null target");
1538 2255 : NS_ASSERTION(!aType.IsEmpty(), "missing event type");
1539 :
1540 4510 : if (NS_FAILED(CheckInnerWindowCorrectness()) ||
1541 2255 : (!AllowUploadProgress() &&
1542 0 : (aTarget == mUpload || aType.EqualsLiteral(UPLOADPROGRESS_STR)))) {
1543 0 : return;
1544 : }
1545 :
1546 2255 : bool dispatchLoadend = aType.EqualsLiteral(LOAD_STR) ||
1547 1741 : aType.EqualsLiteral(ERROR_STR) ||
1548 1670 : aType.EqualsLiteral(TIMEOUT_STR) ||
1549 5666 : aType.EqualsLiteral(ABORT_STR);
1550 :
1551 4510 : nsCOMPtr<nsIDOMEvent> event;
1552 : nsresult rv = nsEventDispatcher::CreateEvent(nsnull, nsnull,
1553 2255 : NS_LITERAL_STRING("ProgressEvent"),
1554 4510 : getter_AddRefs(event));
1555 2255 : if (NS_FAILED(rv)) {
1556 : return;
1557 : }
1558 :
1559 4510 : nsCOMPtr<nsIPrivateDOMEvent> privevent(do_QueryInterface(event));
1560 2255 : if (!privevent) {
1561 : return;
1562 : }
1563 2255 : privevent->SetTrusted(true);
1564 :
1565 4510 : nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
1566 2255 : if (!progress) {
1567 : return;
1568 : }
1569 :
1570 2255 : progress->InitProgressEvent(aType, false, false, aLengthComputable,
1571 2255 : aLoaded, (aTotal == LL_MAXUINT) ? 0 : aTotal);
1572 :
1573 2255 : if (aUseLSEventWrapper) {
1574 : nsCOMPtr<nsIDOMProgressEvent> xhrprogressEvent =
1575 1566 : new nsXMLHttpProgressEvent(progress, aPosition, aTotalSize, GetOwner());
1576 522 : event = xhrprogressEvent;
1577 : }
1578 2255 : aTarget->DispatchDOMEvent(nsnull, event, nsnull, nsnull);
1579 :
1580 2255 : if (dispatchLoadend) {
1581 597 : DispatchProgressEvent(aTarget, NS_LITERAL_STRING(LOADEND_STR),
1582 : aUseLSEventWrapper, aLengthComputable,
1583 597 : aLoaded, aTotal, aPosition, aTotalSize);
1584 : }
1585 : }
1586 :
1587 : already_AddRefed<nsIHttpChannel>
1588 572 : nsXMLHttpRequest::GetCurrentHttpChannel()
1589 : {
1590 572 : nsIHttpChannel *httpChannel = nsnull;
1591 :
1592 572 : if (mReadRequest) {
1593 0 : CallQueryInterface(mReadRequest, &httpChannel);
1594 : }
1595 :
1596 572 : if (!httpChannel && mChannel) {
1597 572 : CallQueryInterface(mChannel, &httpChannel);
1598 : }
1599 :
1600 572 : return httpChannel;
1601 : }
1602 :
1603 : bool
1604 2878 : nsXMLHttpRequest::IsSystemXHR()
1605 : {
1606 2878 : return !!nsContentUtils::IsSystemPrincipal(mPrincipal);
1607 : }
1608 :
1609 : nsresult
1610 632 : nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
1611 : {
1612 : // First check if cross-site requests are enabled...
1613 632 : if (IsSystemXHR()) {
1614 632 : return NS_OK;
1615 : }
1616 :
1617 : // ...or if this is a same-origin request.
1618 0 : if (nsContentUtils::CheckMayLoad(mPrincipal, aChannel)) {
1619 0 : return NS_OK;
1620 : }
1621 :
1622 : // exempt data URIs from the same origin check.
1623 0 : nsCOMPtr<nsIURI> channelURI;
1624 0 : bool dataScheme = false;
1625 0 : if (NS_SUCCEEDED(NS_GetFinalChannelURI(aChannel,
1626 : getter_AddRefs(channelURI))) &&
1627 0 : NS_SUCCEEDED(channelURI->SchemeIs("data", &dataScheme)) &&
1628 : dataScheme) {
1629 0 : return NS_OK;
1630 : }
1631 :
1632 : // This is a cross-site request
1633 0 : mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
1634 :
1635 : // Check if we need to do a preflight request.
1636 0 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
1637 0 : NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
1638 :
1639 0 : nsCAutoString method;
1640 0 : httpChannel->GetRequestMethod(method);
1641 0 : if (!mCORSUnsafeHeaders.IsEmpty() ||
1642 0 : HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) ||
1643 0 : (mUpload && mUpload->HasListeners()) ||
1644 0 : (!method.LowerCaseEqualsLiteral("get") &&
1645 0 : !method.LowerCaseEqualsLiteral("post") &&
1646 0 : !method.LowerCaseEqualsLiteral("head"))) {
1647 0 : mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
1648 : }
1649 :
1650 0 : return NS_OK;
1651 : }
1652 :
1653 : NS_IMETHODIMP
1654 599 : nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
1655 : bool async, const nsAString& user,
1656 : const nsAString& password, PRUint8 optional_argc)
1657 : {
1658 599 : NS_ENSURE_ARG(!method.IsEmpty());
1659 :
1660 599 : if (!optional_argc) {
1661 : // No optional arguments were passed in. Default async to true.
1662 0 : async = true;
1663 : }
1664 : Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC,
1665 599 : async ? 0 : 1);
1666 :
1667 599 : NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
1668 :
1669 : // Disallow HTTP/1.1 TRACE method (see bug 302489)
1670 : // and MS IIS equivalent TRACK (see bug 381264)
1671 1196 : if (method.LowerCaseEqualsLiteral("trace") ||
1672 598 : method.LowerCaseEqualsLiteral("track")) {
1673 0 : return NS_ERROR_INVALID_ARG;
1674 : }
1675 :
1676 : // sync request is not allowed using withCredential or responseType
1677 : // in window context
1678 598 : if (!async && HasOrHasHadOwner() &&
1679 : (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS ||
1680 : mTimeoutMilliseconds ||
1681 : mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT)) {
1682 0 : if (mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS) {
1683 0 : LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
1684 : }
1685 0 : if (mTimeoutMilliseconds) {
1686 0 : LogMessage("TimeoutSyncXHRWarning", GetOwner());
1687 : }
1688 0 : if (mResponseType != XML_HTTP_RESPONSE_TYPE_DEFAULT) {
1689 0 : LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
1690 : }
1691 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
1692 : }
1693 :
1694 : nsresult rv;
1695 1196 : nsCOMPtr<nsIURI> uri;
1696 :
1697 598 : if (mState & (XML_HTTP_REQUEST_OPENED |
1698 : XML_HTTP_REQUEST_HEADERS_RECEIVED |
1699 : XML_HTTP_REQUEST_LOADING |
1700 : XML_HTTP_REQUEST_SENT |
1701 : XML_HTTP_REQUEST_STOPPED)) {
1702 : // IE aborts as well
1703 0 : Abort();
1704 :
1705 : // XXX We should probably send a warning to the JS console
1706 : // that load was aborted and event listeners were cleared
1707 : // since this looks like a situation that could happen
1708 : // by accident and you could spend a lot of time wondering
1709 : // why things didn't work.
1710 : }
1711 :
1712 : // Unset any pre-existing aborted and timed-out states.
1713 598 : mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
1714 :
1715 598 : if (async) {
1716 539 : mState |= XML_HTTP_REQUEST_ASYNC;
1717 : } else {
1718 59 : mState &= ~XML_HTTP_REQUEST_ASYNC;
1719 : }
1720 :
1721 598 : mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
1722 :
1723 598 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
1724 598 : NS_ENSURE_SUCCESS(rv, rv);
1725 : nsCOMPtr<nsIDocument> doc =
1726 1196 : nsContentUtils::GetDocumentFromScriptContext(sc);
1727 :
1728 1196 : nsCOMPtr<nsIURI> baseURI;
1729 598 : if (mBaseURI) {
1730 0 : baseURI = mBaseURI;
1731 : }
1732 598 : else if (doc) {
1733 0 : baseURI = doc->GetBaseURI();
1734 : }
1735 :
1736 598 : rv = NS_NewURI(getter_AddRefs(uri), url, nsnull, baseURI);
1737 598 : if (NS_FAILED(rv)) return rv;
1738 :
1739 598 : rv = CheckInnerWindowCorrectness();
1740 598 : NS_ENSURE_SUCCESS(rv, rv);
1741 598 : PRInt16 shouldLoad = nsIContentPolicy::ACCEPT;
1742 : rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_XMLHTTPREQUEST,
1743 : uri,
1744 : mPrincipal,
1745 : doc,
1746 598 : EmptyCString(), //mime guess
1747 : nsnull, //extra
1748 : &shouldLoad,
1749 : nsContentUtils::GetContentPolicy(),
1750 1196 : nsContentUtils::GetSecurityManager());
1751 598 : if (NS_FAILED(rv)) return rv;
1752 598 : if (NS_CP_REJECTED(shouldLoad)) {
1753 : // Disallowed by content policy
1754 0 : return NS_ERROR_CONTENT_BLOCKED;
1755 : }
1756 :
1757 598 : if (!user.IsEmpty()) {
1758 0 : nsCAutoString userpass;
1759 0 : CopyUTF16toUTF8(user, userpass);
1760 0 : if (!password.IsEmpty()) {
1761 0 : userpass.Append(':');
1762 0 : AppendUTF16toUTF8(password, userpass);
1763 : }
1764 0 : uri->SetUserPass(userpass);
1765 : }
1766 :
1767 : // When we are called from JS we can find the load group for the page,
1768 : // and add ourselves to it. This way any pending requests
1769 : // will be automatically aborted if the user leaves the page.
1770 1196 : nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
1771 :
1772 : // get Content Security Policy from principal to pass into channel
1773 1196 : nsCOMPtr<nsIChannelPolicy> channelPolicy;
1774 1196 : nsCOMPtr<nsIContentSecurityPolicy> csp;
1775 598 : rv = mPrincipal->GetCsp(getter_AddRefs(csp));
1776 598 : NS_ENSURE_SUCCESS(rv, rv);
1777 598 : if (csp) {
1778 0 : channelPolicy = do_CreateInstance("@mozilla.org/nschannelpolicy;1");
1779 0 : channelPolicy->SetContentSecurityPolicy(csp);
1780 0 : channelPolicy->SetLoadType(nsIContentPolicy::TYPE_XMLHTTPREQUEST);
1781 : }
1782 598 : rv = NS_NewChannel(getter_AddRefs(mChannel),
1783 : uri,
1784 : nsnull, // ioService
1785 : loadGroup,
1786 : nsnull, // callbacks
1787 : nsIRequest::LOAD_BACKGROUND,
1788 598 : channelPolicy);
1789 598 : if (NS_FAILED(rv)) return rv;
1790 :
1791 : mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
1792 598 : XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
1793 :
1794 1196 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
1795 598 : if (httpChannel) {
1796 595 : rv = httpChannel->SetRequestMethod(method);
1797 595 : NS_ENSURE_SUCCESS(rv, rv);
1798 : }
1799 :
1800 598 : ChangeState(XML_HTTP_REQUEST_OPENED);
1801 :
1802 598 : return rv;
1803 : }
1804 :
1805 : /*
1806 : * "Copy" from a stream.
1807 : */
1808 : NS_METHOD
1809 564 : nsXMLHttpRequest::StreamReaderFunc(nsIInputStream* in,
1810 : void* closure,
1811 : const char* fromRawSegment,
1812 : PRUint32 toOffset,
1813 : PRUint32 count,
1814 : PRUint32 *writeCount)
1815 : {
1816 564 : nsXMLHttpRequest* xmlHttpRequest = static_cast<nsXMLHttpRequest*>(closure);
1817 564 : if (!xmlHttpRequest || !writeCount) {
1818 0 : NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
1819 0 : return NS_ERROR_FAILURE;
1820 : }
1821 :
1822 564 : nsresult rv = NS_OK;
1823 :
1824 564 : if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
1825 : xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
1826 0 : if (!xmlHttpRequest->mDOMFile) {
1827 0 : if (!xmlHttpRequest->mBuilder) {
1828 0 : xmlHttpRequest->mBuilder = new nsDOMBlobBuilder();
1829 : }
1830 0 : rv = xmlHttpRequest->mBuilder->AppendVoidPtr(fromRawSegment, count);
1831 : }
1832 : // Clear the cache so that the blob size is updated.
1833 0 : if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
1834 0 : xmlHttpRequest->mResponseBlob = nsnull;
1835 : }
1836 0 : if (NS_SUCCEEDED(rv)) {
1837 0 : *writeCount = count;
1838 : }
1839 0 : return rv;
1840 : }
1841 :
1842 1128 : if ((xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT &&
1843 564 : xmlHttpRequest->mResponseXML) ||
1844 : xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER ||
1845 : xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
1846 : // Copy for our own use
1847 557 : PRUint32 previousLength = xmlHttpRequest->mResponseBody.Length();
1848 557 : xmlHttpRequest->mResponseBody.Append(fromRawSegment,count);
1849 557 : if (count > 0 && xmlHttpRequest->mResponseBody.Length() == previousLength) {
1850 0 : return NS_ERROR_OUT_OF_MEMORY;
1851 : }
1852 7 : } else if (xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
1853 : xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_TEXT ||
1854 : xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_JSON ||
1855 : xmlHttpRequest->mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT) {
1856 7 : NS_ASSERTION(!xmlHttpRequest->mResponseXML,
1857 : "We shouldn't be parsing a doc here");
1858 7 : xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
1859 : }
1860 :
1861 564 : if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
1862 : // Give the same data to the parser.
1863 :
1864 : // We need to wrap the data in a new lightweight stream and pass that
1865 : // to the parser, because calling ReadSegments() recursively on the same
1866 : // stream is not supported.
1867 1114 : nsCOMPtr<nsIInputStream> copyStream;
1868 557 : rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
1869 :
1870 557 : if (NS_SUCCEEDED(rv) && xmlHttpRequest->mXMLParserStreamListener) {
1871 557 : NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
1872 : nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
1873 557 : ->OnDataAvailable(xmlHttpRequest->mReadRequest,
1874 : xmlHttpRequest->mContext,
1875 557 : copyStream, toOffset, count);
1876 :
1877 : // No use to continue parsing if we failed here, but we
1878 : // should still finish reading the stream
1879 557 : if (NS_FAILED(parsingResult)) {
1880 0 : xmlHttpRequest->mState &= ~XML_HTTP_REQUEST_PARSEBODY;
1881 : }
1882 : }
1883 : }
1884 :
1885 564 : if (NS_SUCCEEDED(rv)) {
1886 564 : *writeCount = count;
1887 : } else {
1888 0 : *writeCount = 0;
1889 : }
1890 :
1891 564 : return rv;
1892 : }
1893 :
1894 0 : bool nsXMLHttpRequest::CreateDOMFile(nsIRequest *request)
1895 : {
1896 0 : nsCOMPtr<nsIFile> file;
1897 0 : nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(request));
1898 0 : if (cc) {
1899 0 : cc->GetCacheFile(getter_AddRefs(file));
1900 : } else {
1901 0 : nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(request);
1902 0 : if (fc) {
1903 0 : fc->GetFile(getter_AddRefs(file));
1904 : }
1905 : }
1906 0 : bool fromFile = false;
1907 0 : if (file) {
1908 0 : nsCAutoString contentType;
1909 0 : mChannel->GetContentType(contentType);
1910 0 : nsCOMPtr<nsISupports> cacheToken;
1911 0 : if (cc) {
1912 0 : cc->GetCacheToken(getter_AddRefs(cacheToken));
1913 : // We need to call IsFromCache to determine whether the response is
1914 : // fully cached (i.e. whether we can skip reading the response).
1915 0 : cc->IsFromCache(&fromFile);
1916 : } else {
1917 : // If the response is coming from the local resource, we can skip
1918 : // reading the response unconditionally.
1919 0 : fromFile = true;
1920 : }
1921 :
1922 : mDOMFile =
1923 0 : new nsDOMFileFile(file, NS_ConvertASCIItoUTF16(contentType), cacheToken);
1924 0 : mBuilder = nsnull;
1925 0 : NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
1926 : }
1927 0 : return fromFile;
1928 : }
1929 :
1930 : NS_IMETHODIMP
1931 564 : nsXMLHttpRequest::OnDataAvailable(nsIRequest *request,
1932 : nsISupports *ctxt,
1933 : nsIInputStream *inStr,
1934 : PRUint32 sourceOffset,
1935 : PRUint32 count)
1936 : {
1937 564 : NS_ENSURE_ARG_POINTER(inStr);
1938 :
1939 564 : NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context");
1940 :
1941 564 : mProgressSinceLastProgressEvent = true;
1942 :
1943 564 : bool cancelable = false;
1944 564 : if ((mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
1945 0 : mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) && !mDOMFile) {
1946 0 : cancelable = CreateDOMFile(request);
1947 : // The nsIStreamListener contract mandates us
1948 : // to read from the stream before returning.
1949 : }
1950 :
1951 : PRUint32 totalRead;
1952 : nsresult rv = inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc,
1953 564 : (void*)this, count, &totalRead);
1954 564 : NS_ENSURE_SUCCESS(rv, rv);
1955 :
1956 564 : if (cancelable) {
1957 : // We don't have to read from the local file for the blob response
1958 0 : mDOMFile->GetSize(&mLoadTransferred);
1959 0 : ChangeState(XML_HTTP_REQUEST_LOADING);
1960 0 : return request->Cancel(NS_OK);
1961 : }
1962 :
1963 564 : mLoadTransferred += totalRead;
1964 :
1965 564 : ChangeState(XML_HTTP_REQUEST_LOADING);
1966 :
1967 564 : MaybeDispatchProgressEvents(false);
1968 :
1969 564 : return NS_OK;
1970 : }
1971 :
1972 : bool
1973 1194 : IsSameOrBaseChannel(nsIRequest* aPossibleBase, nsIChannel* aChannel)
1974 : {
1975 2388 : nsCOMPtr<nsIMultiPartChannel> mpChannel = do_QueryInterface(aPossibleBase);
1976 1194 : if (mpChannel) {
1977 0 : nsCOMPtr<nsIChannel> baseChannel;
1978 0 : nsresult rv = mpChannel->GetBaseChannel(getter_AddRefs(baseChannel));
1979 0 : NS_ENSURE_SUCCESS(rv, false);
1980 :
1981 0 : return baseChannel == aChannel;
1982 : }
1983 :
1984 1194 : return aPossibleBase == aChannel;
1985 : }
1986 :
1987 : /* void onStartRequest (in nsIRequest request, in nsISupports ctxt); */
1988 : NS_IMETHODIMP
1989 597 : nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
1990 : {
1991 1194 : SAMPLE_LABEL("nsXMLHttpRequest", "OnStartRequest");
1992 597 : nsresult rv = NS_OK;
1993 597 : if (!mFirstStartRequestSeen && mRequestObserver) {
1994 0 : mFirstStartRequestSeen = true;
1995 0 : mRequestObserver->OnStartRequest(request, ctxt);
1996 : }
1997 :
1998 597 : if (!IsSameOrBaseChannel(request, mChannel)) {
1999 0 : return NS_OK;
2000 : }
2001 :
2002 : // Don't do anything if we have been aborted
2003 597 : if (mState & XML_HTTP_REQUEST_UNSENT)
2004 12 : return NS_OK;
2005 :
2006 : /* Apparently, Abort() should set XML_HTTP_REQUEST_UNSENT. See bug 361773.
2007 : XHR2 spec says this is correct. */
2008 585 : if (mState & XML_HTTP_REQUEST_ABORTED) {
2009 0 : NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
2010 :
2011 0 : return NS_ERROR_UNEXPECTED;
2012 : }
2013 :
2014 : // Don't do anything if we have timed out.
2015 585 : if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
2016 0 : return NS_OK;
2017 : }
2018 :
2019 1170 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
2020 585 : NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
2021 :
2022 1170 : nsCOMPtr<nsIPrincipal> documentPrincipal;
2023 585 : if (IsSystemXHR()) {
2024 : // Don't give this document the system principal. We need to keep track of
2025 : // mPrincipal being system because we use it for various security checks
2026 : // that should be passing, but the document data shouldn't get a system
2027 : // principal.
2028 : nsresult rv;
2029 585 : documentPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
2030 585 : NS_ENSURE_SUCCESS(rv, rv);
2031 : } else {
2032 0 : documentPrincipal = mPrincipal;
2033 : }
2034 :
2035 585 : channel->SetOwner(documentPrincipal);
2036 :
2037 : nsresult status;
2038 585 : request->GetStatus(&status);
2039 585 : mErrorLoad = mErrorLoad || NS_FAILED(status);
2040 :
2041 585 : if (mUpload && !mUploadComplete && !mErrorLoad &&
2042 : (mState & XML_HTTP_REQUEST_ASYNC)) {
2043 0 : if (mProgressTimerIsActive) {
2044 0 : mProgressTimerIsActive = false;
2045 0 : mProgressNotifier->Cancel();
2046 : }
2047 0 : MaybeDispatchProgressEvents(true);
2048 0 : mUploadComplete = true;
2049 0 : DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOAD_STR),
2050 0 : true, mUploadTotal, mUploadTotal);
2051 : }
2052 :
2053 585 : mReadRequest = request;
2054 585 : mContext = ctxt;
2055 585 : mState |= XML_HTTP_REQUEST_PARSEBODY;
2056 585 : mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
2057 585 : ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
2058 :
2059 585 : if (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
2060 : mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
2061 0 : nsCOMPtr<nsICachingChannel> cc(do_QueryInterface(mChannel));
2062 0 : if (cc) {
2063 0 : cc->SetCacheAsFile(true);
2064 : }
2065 : }
2066 :
2067 585 : ResetResponse();
2068 :
2069 585 : if (!mOverrideMimeType.IsEmpty()) {
2070 522 : channel->SetContentType(mOverrideMimeType);
2071 : }
2072 :
2073 585 : DetectCharset();
2074 :
2075 : // Set up responseXML
2076 : bool parseBody = mResponseType == XML_HTTP_RESPONSE_TYPE_DEFAULT ||
2077 585 : mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT;
2078 1170 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2079 585 : if (parseBody && httpChannel) {
2080 1166 : nsCAutoString method;
2081 583 : httpChannel->GetRequestMethod(method);
2082 583 : parseBody = !method.EqualsLiteral("HEAD");
2083 : }
2084 :
2085 585 : mIsHtml = false;
2086 585 : mWarnAboutMultipartHtml = false;
2087 585 : mWarnAboutSyncHtml = false;
2088 585 : if (parseBody && NS_SUCCEEDED(status)) {
2089 : // We can gain a huge performance win by not even trying to
2090 : // parse non-XML data. This also protects us from the situation
2091 : // where we have an XML document and sink, but HTML (or other)
2092 : // parser, which can produce unreliable results.
2093 1012 : nsCAutoString type;
2094 506 : channel->GetContentType(type);
2095 :
2096 506 : if ((mResponseType == XML_HTTP_RESPONSE_TYPE_DOCUMENT) &&
2097 0 : type.EqualsLiteral("text/html")) {
2098 : // HTML parsing is only supported for responseType == "document" to
2099 : // avoid running the parser and, worse, populating responseXML for
2100 : // legacy users of XHR who use responseType == "" for retrieving the
2101 : // responseText of text/html resources. This legacy case is so common
2102 : // that it's not useful to emit a warning about it.
2103 0 : if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
2104 : // We don't make cool new features available in the bad synchronous
2105 : // mode. The synchronous mode is for legacy only.
2106 0 : mWarnAboutSyncHtml = true;
2107 0 : mState &= ~XML_HTTP_REQUEST_PARSEBODY;
2108 0 : } else if (mState & XML_HTTP_REQUEST_MULTIPART) {
2109 : // HTML parsing is supported only for non-multipart responses. The
2110 : // multipart implementation assumes that it's OK to start the next part
2111 : // immediately after the last part. That doesn't work with the HTML
2112 : // parser, because when OnStopRequest for one part has fired, the
2113 : // parser thread still hasn't posted back the runnables that make the
2114 : // parsing appear finished.
2115 : //
2116 : // On the other hand, multipart support seems to be a legacy feature,
2117 : // so it isn't clear that use cases justify adding support for deferring
2118 : // the multipart stream events between parts to accommodate the
2119 : // asynchronous nature of the HTML parser.
2120 0 : mWarnAboutMultipartHtml = true;
2121 0 : mState &= ~XML_HTTP_REQUEST_PARSEBODY;
2122 : } else {
2123 0 : mIsHtml = true;
2124 : }
2125 506 : } else if (type.Find("xml") == kNotFound) {
2126 37 : mState &= ~XML_HTTP_REQUEST_PARSEBODY;
2127 506 : }
2128 : } else {
2129 : // The request failed, so we shouldn't be parsing anyway
2130 79 : mState &= ~XML_HTTP_REQUEST_PARSEBODY;
2131 : }
2132 :
2133 585 : if (mState & XML_HTTP_REQUEST_PARSEBODY) {
2134 938 : nsCOMPtr<nsIURI> baseURI, docURI;
2135 469 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
2136 469 : NS_ENSURE_SUCCESS(rv, rv);
2137 : nsCOMPtr<nsIDocument> doc =
2138 938 : nsContentUtils::GetDocumentFromScriptContext(sc);
2139 :
2140 469 : if (doc) {
2141 0 : docURI = doc->GetDocumentURI();
2142 0 : baseURI = doc->GetBaseURI();
2143 : }
2144 :
2145 : // Create an empty document from it. Here we have to cheat a little bit...
2146 : // Setting the base URI to |baseURI| won't work if the document has a null
2147 : // principal, so use mPrincipal when creating the document, then reset the
2148 : // principal.
2149 469 : const nsAString& emptyStr = EmptyString();
2150 938 : nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(GetOwner());
2151 : rv = nsContentUtils::CreateDocument(emptyStr, emptyStr, nsnull, docURI,
2152 : baseURI, mPrincipal, global,
2153 : mIsHtml ? DocumentFlavorHTML :
2154 : DocumentFlavorLegacyGuess,
2155 469 : getter_AddRefs(mResponseXML));
2156 469 : NS_ENSURE_SUCCESS(rv, rv);
2157 938 : nsCOMPtr<nsIDocument> responseDoc = do_QueryInterface(mResponseXML);
2158 469 : responseDoc->SetPrincipal(documentPrincipal);
2159 :
2160 469 : if (IsSystemXHR()) {
2161 469 : responseDoc->ForceEnableXULXBL();
2162 : }
2163 :
2164 469 : if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
2165 0 : nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
2166 0 : if (htmlDoc) {
2167 0 : htmlDoc->DisableCookieAccess();
2168 : }
2169 : }
2170 :
2171 938 : nsCOMPtr<nsIStreamListener> listener;
2172 938 : nsCOMPtr<nsILoadGroup> loadGroup;
2173 469 : channel->GetLoadGroup(getter_AddRefs(loadGroup));
2174 :
2175 469 : rv = responseDoc->StartDocumentLoad(kLoadAsData, channel, loadGroup,
2176 469 : nsnull, getter_AddRefs(listener),
2177 469 : !(mState & XML_HTTP_REQUEST_USE_XSITE_AC));
2178 469 : NS_ENSURE_SUCCESS(rv, rv);
2179 :
2180 469 : mXMLParserStreamListener = listener;
2181 469 : rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
2182 469 : NS_ENSURE_SUCCESS(rv, rv);
2183 : }
2184 :
2185 : // We won't get any progress events anyway if we didn't have progress
2186 : // events when starting the request - so maybe no need to start timer here.
2187 2224 : if (NS_SUCCEEDED(rv) &&
2188 : (mState & XML_HTTP_REQUEST_ASYNC) &&
2189 1639 : HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR))) {
2190 0 : StartProgressEventTimer();
2191 : }
2192 :
2193 585 : return NS_OK;
2194 : }
2195 :
2196 : /* void onStopRequest (in nsIRequest request, in nsISupports ctxt, in nsresult status, in wstring statusArg); */
2197 : NS_IMETHODIMP
2198 597 : nsXMLHttpRequest::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
2199 : {
2200 1194 : SAMPLE_LABEL("content", "nsXMLHttpRequest::OnStopRequest");
2201 597 : if (!IsSameOrBaseChannel(request, mChannel)) {
2202 0 : return NS_OK;
2203 : }
2204 :
2205 597 : mWaitingForOnStopRequest = false;
2206 :
2207 597 : nsresult rv = NS_OK;
2208 :
2209 : // If we're loading a multipart stream of XML documents, we'll get
2210 : // an OnStopRequest() for the last part in the stream, and then
2211 : // another one for the end of the initiating
2212 : // "multipart/x-mixed-replace" stream too. So we must check that we
2213 : // still have an xml parser stream listener before accessing it
2214 : // here.
2215 1194 : nsCOMPtr<nsIMultiPartChannel> mpChannel = do_QueryInterface(request);
2216 597 : if (mpChannel) {
2217 : bool last;
2218 0 : rv = mpChannel->GetIsLastPart(&last);
2219 0 : NS_ENSURE_SUCCESS(rv, rv);
2220 0 : if (last) {
2221 0 : mState |= XML_HTTP_REQUEST_GOT_FINAL_STOP;
2222 : }
2223 : }
2224 : else {
2225 597 : mState |= XML_HTTP_REQUEST_GOT_FINAL_STOP;
2226 : }
2227 :
2228 597 : if (mRequestObserver && mState & XML_HTTP_REQUEST_GOT_FINAL_STOP) {
2229 0 : NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
2230 0 : mFirstStartRequestSeen = false;
2231 0 : mRequestObserver->OnStopRequest(request, ctxt, status);
2232 : }
2233 :
2234 : // make sure to notify the listener if we were aborted
2235 : // XXX in fact, why don't we do the cleanup below in this case??
2236 : // XML_HTTP_REQUEST_UNSENT is for abort calls. See OnStartRequest above.
2237 597 : if ((mState & XML_HTTP_REQUEST_UNSENT) ||
2238 : (mState & XML_HTTP_REQUEST_TIMED_OUT)) {
2239 12 : if (mXMLParserStreamListener)
2240 0 : (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
2241 12 : return NS_OK;
2242 : }
2243 :
2244 : // Is this good enough here?
2245 585 : if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
2246 469 : mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
2247 : }
2248 :
2249 585 : mXMLParserStreamListener = nsnull;
2250 585 : mReadRequest = nsnull;
2251 585 : mContext = nsnull;
2252 :
2253 : // If we're received data since the last progress event, make sure to fire
2254 : // an event for it, except in the HTML case, defer the last progress event
2255 : // until the parser is done.
2256 585 : if (!mIsHtml) {
2257 585 : MaybeDispatchProgressEvents(true);
2258 : }
2259 :
2260 585 : if (NS_SUCCEEDED(status) &&
2261 : (mResponseType == XML_HTTP_RESPONSE_TYPE_BLOB ||
2262 : mResponseType == XML_HTTP_RESPONSE_TYPE_MOZ_BLOB)) {
2263 0 : if (!mDOMFile) {
2264 0 : CreateDOMFile(request);
2265 : }
2266 0 : if (mDOMFile) {
2267 0 : mResponseBlob = mDOMFile;
2268 0 : mDOMFile = nsnull;
2269 : } else {
2270 : // Smaller files may be written in cache map instead of separate files.
2271 : // Also, no-store response cannot be written in persistent cache.
2272 0 : nsCAutoString contentType;
2273 0 : mChannel->GetContentType(contentType);
2274 0 : mBuilder->GetBlobInternal(NS_ConvertASCIItoUTF16(contentType),
2275 0 : false, getter_AddRefs(mResponseBlob));
2276 0 : mBuilder = nsnull;
2277 : }
2278 0 : NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
2279 0 : NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
2280 : }
2281 :
2282 1170 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
2283 585 : NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
2284 :
2285 585 : channel->SetNotificationCallbacks(nsnull);
2286 585 : mNotificationCallbacks = nsnull;
2287 585 : mChannelEventSink = nsnull;
2288 585 : mProgressEventSink = nsnull;
2289 :
2290 585 : mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
2291 :
2292 585 : if (NS_FAILED(status)) {
2293 : // This can happen if the server is unreachable. Other possible
2294 : // reasons are that the user leaves the page or hits the ESC key.
2295 :
2296 71 : mErrorLoad = true;
2297 71 : mResponseXML = nsnull;
2298 : }
2299 :
2300 : // If we're uninitialized at this point, we encountered an error
2301 : // earlier and listeners have already been notified. Also we do
2302 : // not want to do this if we already completed.
2303 585 : if (mState & (XML_HTTP_REQUEST_UNSENT |
2304 : XML_HTTP_REQUEST_DONE)) {
2305 0 : return NS_OK;
2306 : }
2307 :
2308 585 : if (!mResponseXML) {
2309 116 : ChangeStateToDone();
2310 116 : return NS_OK;
2311 : }
2312 469 : if (mIsHtml) {
2313 0 : NS_ASSERTION(!(mState & XML_HTTP_REQUEST_SYNCLOOPING),
2314 : "We weren't supposed to support HTML parsing with XHR!");
2315 0 : nsCOMPtr<nsIDOMEventTarget> eventTarget = do_QueryInterface(mResponseXML);
2316 0 : nsEventListenerManager* manager = eventTarget->GetListenerManager(true);
2317 0 : manager->AddEventListenerByType(new nsXHRParseEndListener(this),
2318 0 : NS_LITERAL_STRING("DOMContentLoaded"),
2319 : NS_EVENT_FLAG_BUBBLE |
2320 0 : NS_EVENT_FLAG_SYSTEM_EVENT);
2321 0 : return NS_OK;
2322 : }
2323 : // We might have been sent non-XML data. If that was the case,
2324 : // we should null out the document member. The idea in this
2325 : // check here is that if there is no document element it is not
2326 : // an XML document. We might need a fancier check...
2327 938 : nsCOMPtr<nsIDOMElement> root;
2328 469 : mResponseXML->GetDocumentElement(getter_AddRefs(root));
2329 469 : if (!root) {
2330 0 : mResponseXML = nsnull;
2331 : }
2332 469 : ChangeStateToDone();
2333 469 : return NS_OK;
2334 : }
2335 :
2336 : void
2337 585 : nsXMLHttpRequest::ChangeStateToDone()
2338 : {
2339 585 : if (mIsHtml) {
2340 : // In the HTML case, this has to be deferred, because the parser doesn't
2341 : // do it's job synchronously.
2342 0 : MaybeDispatchProgressEvents(true);
2343 : }
2344 :
2345 585 : ChangeState(XML_HTTP_REQUEST_DONE, true);
2346 585 : if (mTimeoutTimer) {
2347 0 : mTimeoutTimer->Cancel();
2348 : }
2349 :
2350 1170 : NS_NAMED_LITERAL_STRING(errorStr, ERROR_STR);
2351 1170 : NS_NAMED_LITERAL_STRING(loadStr, LOAD_STR);
2352 : DispatchProgressEvent(this,
2353 : mErrorLoad ? errorStr : loadStr,
2354 585 : !mErrorLoad,
2355 : mLoadTransferred,
2356 1170 : mErrorLoad ? 0 : mLoadTransferred);
2357 585 : if (mErrorLoad && mUpload && !mUploadComplete) {
2358 : DispatchProgressEvent(mUpload, errorStr, true,
2359 0 : mUploadTransferred, mUploadTotal);
2360 : }
2361 :
2362 585 : if (mErrorLoad) {
2363 : // By nulling out channel here we make it so that Send() can test
2364 : // for that and throw. Also calling the various status
2365 : // methods/members will not throw.
2366 : // This matches what IE does.
2367 71 : mChannel = nsnull;
2368 71 : mCORSPreflightChannel = nsnull;
2369 : }
2370 514 : else if (!(mState & XML_HTTP_REQUEST_GOT_FINAL_STOP)) {
2371 : // We're a multipart request, so we're not done. Reset to opened.
2372 0 : ChangeState(XML_HTTP_REQUEST_OPENED);
2373 : }
2374 585 : }
2375 :
2376 : NS_IMETHODIMP
2377 0 : nsXMLHttpRequest::SendAsBinary(const nsAString &aBody)
2378 : {
2379 0 : char *data = static_cast<char*>(NS_Alloc(aBody.Length() + 1));
2380 0 : if (!data)
2381 0 : return NS_ERROR_OUT_OF_MEMORY;
2382 :
2383 0 : nsAString::const_iterator iter, end;
2384 0 : aBody.BeginReading(iter);
2385 0 : aBody.EndReading(end);
2386 0 : char *p = data;
2387 0 : while (iter != end) {
2388 0 : if (*iter & 0xFF00) {
2389 0 : NS_Free(data);
2390 0 : return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
2391 : }
2392 0 : *p++ = static_cast<char>(*iter++);
2393 : }
2394 0 : *p = '\0';
2395 :
2396 0 : nsCOMPtr<nsIInputStream> stream;
2397 0 : nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data,
2398 0 : aBody.Length(), NS_ASSIGNMENT_ADOPT);
2399 0 : if (NS_FAILED(rv))
2400 0 : NS_Free(data);
2401 0 : NS_ENSURE_SUCCESS(rv, rv);
2402 :
2403 0 : nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
2404 0 : if (!variant) return NS_ERROR_OUT_OF_MEMORY;
2405 :
2406 0 : rv = variant->SetAsISupports(stream);
2407 0 : NS_ENSURE_SUCCESS(rv, rv);
2408 :
2409 0 : return Send(variant);
2410 : }
2411 :
2412 : static nsresult
2413 44 : GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult,
2414 : nsACString& aContentType, nsACString& aCharset)
2415 : {
2416 44 : *aResult = nsnull;
2417 44 : aContentType.AssignLiteral("text/plain");
2418 44 : aCharset.AssignLiteral("UTF-8");
2419 :
2420 : PRUint16 dataType;
2421 44 : nsresult rv = aBody->GetDataType(&dataType);
2422 44 : NS_ENSURE_SUCCESS(rv, rv);
2423 :
2424 44 : if (dataType == nsIDataType::VTYPE_INTERFACE ||
2425 : dataType == nsIDataType::VTYPE_INTERFACE_IS) {
2426 8 : nsCOMPtr<nsISupports> supports;
2427 : nsID *iid;
2428 4 : rv = aBody->GetAsInterface(&iid, getter_AddRefs(supports));
2429 4 : NS_ENSURE_SUCCESS(rv, rv);
2430 :
2431 4 : nsMemory::Free(iid);
2432 :
2433 : // document?
2434 8 : nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(supports);
2435 4 : if (doc) {
2436 0 : aContentType.AssignLiteral("application/xml");
2437 0 : nsAutoString inputEncoding;
2438 0 : doc->GetInputEncoding(inputEncoding);
2439 0 : if (!DOMStringIsNull(inputEncoding)) {
2440 0 : CopyUTF16toUTF8(inputEncoding, aCharset);
2441 : }
2442 :
2443 : // Serialize to a stream so that the encoding used will
2444 : // match the document's.
2445 : nsCOMPtr<nsIDOMSerializer> serializer =
2446 0 : do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv);
2447 0 : NS_ENSURE_SUCCESS(rv, rv);
2448 :
2449 0 : nsCOMPtr<nsIStorageStream> storStream;
2450 : rv = NS_NewStorageStream(4096, PR_UINT32_MAX,
2451 0 : getter_AddRefs(storStream));
2452 0 : NS_ENSURE_SUCCESS(rv, rv);
2453 :
2454 0 : nsCOMPtr<nsIOutputStream> output;
2455 0 : rv = storStream->GetOutputStream(0, getter_AddRefs(output));
2456 0 : NS_ENSURE_SUCCESS(rv, rv);
2457 :
2458 : // Make sure to use the encoding we'll send
2459 0 : rv = serializer->SerializeToStream(doc, output, aCharset);
2460 0 : NS_ENSURE_SUCCESS(rv, rv);
2461 :
2462 0 : output->Close();
2463 :
2464 0 : return storStream->NewInputStream(0, aResult);
2465 : }
2466 :
2467 : // nsISupportsString?
2468 8 : nsCOMPtr<nsISupportsString> wstr = do_QueryInterface(supports);
2469 4 : if (wstr) {
2470 0 : nsAutoString string;
2471 0 : wstr->GetData(string);
2472 :
2473 : return NS_NewCStringInputStream(aResult,
2474 0 : NS_ConvertUTF16toUTF8(string));
2475 : }
2476 :
2477 : // nsIInputStream?
2478 8 : nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
2479 4 : if (stream) {
2480 4 : stream.forget(aResult);
2481 4 : aCharset.Truncate();
2482 :
2483 4 : return NS_OK;
2484 : }
2485 :
2486 : // nsIXHRSendable?
2487 0 : nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
2488 0 : if (sendable) {
2489 0 : return sendable->GetSendInfo(aResult, aContentType, aCharset);
2490 : }
2491 :
2492 : // ArrayBuffer?
2493 : jsval realVal;
2494 : JSObject* obj;
2495 0 : nsresult rv = aBody->GetAsJSVal(&realVal);
2496 0 : if (NS_SUCCEEDED(rv) && !JSVAL_IS_PRIMITIVE(realVal) &&
2497 : (obj = JSVAL_TO_OBJECT(realVal)) &&
2498 0 : (js_IsArrayBuffer(obj))) {
2499 :
2500 0 : aContentType.SetIsVoid(true);
2501 0 : PRInt32 abLength = JS_GetArrayBufferByteLength(obj);
2502 0 : char* data = (char*)JS_GetArrayBufferData(obj);
2503 :
2504 0 : nsCOMPtr<nsIInputStream> stream;
2505 0 : nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data,
2506 0 : abLength, NS_ASSIGNMENT_COPY);
2507 0 : NS_ENSURE_SUCCESS(rv, rv);
2508 0 : stream.forget(aResult);
2509 0 : aCharset.Truncate();
2510 :
2511 0 : return NS_OK;
2512 0 : }
2513 : }
2514 40 : else if (dataType == nsIDataType::VTYPE_VOID ||
2515 : dataType == nsIDataType::VTYPE_EMPTY) {
2516 : // Makes us act as if !aBody, don't upload anything
2517 40 : return NS_OK;
2518 : }
2519 :
2520 0 : PRUnichar* data = nsnull;
2521 0 : PRUint32 len = 0;
2522 0 : rv = aBody->GetAsWStringWithSize(&len, &data);
2523 0 : NS_ENSURE_SUCCESS(rv, rv);
2524 :
2525 0 : nsString string;
2526 0 : string.Adopt(data, len);
2527 :
2528 0 : return NS_NewCStringInputStream(aResult, NS_ConvertUTF16toUTF8(string));
2529 : }
2530 :
2531 : /* void send (in nsIVariant aBody); */
2532 : NS_IMETHODIMP
2533 597 : nsXMLHttpRequest::Send(nsIVariant *aBody)
2534 : {
2535 597 : NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
2536 :
2537 597 : nsresult rv = CheckInnerWindowCorrectness();
2538 597 : NS_ENSURE_SUCCESS(rv, rv);
2539 :
2540 : // Return error if we're already processing a request
2541 597 : if (XML_HTTP_REQUEST_SENT & mState) {
2542 0 : return NS_ERROR_FAILURE;
2543 : }
2544 :
2545 : // Make sure we've been opened
2546 597 : if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
2547 0 : return NS_ERROR_NOT_INITIALIZED;
2548 : }
2549 :
2550 :
2551 : // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
2552 : // in turn keeps STOP button from becoming active. If the consumer passed in
2553 : // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
2554 : // necko won't generate any progress notifications.
2555 2985 : if (HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR)) ||
2556 1791 : HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR)) ||
2557 1194 : (mUpload && mUpload->HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR)))) {
2558 : nsLoadFlags loadFlags;
2559 0 : mChannel->GetLoadFlags(&loadFlags);
2560 0 : loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
2561 0 : loadFlags |= nsIRequest::LOAD_NORMAL;
2562 0 : mChannel->SetLoadFlags(loadFlags);
2563 : }
2564 :
2565 : // XXX We should probably send a warning to the JS console
2566 : // if there are no event listeners set and we are doing
2567 : // an asynchronous call.
2568 :
2569 : // Ignore argument if method is GET, there is no point in trying to
2570 : // upload anything
2571 1194 : nsCAutoString method;
2572 1194 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2573 :
2574 597 : if (httpChannel) {
2575 595 : httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
2576 :
2577 595 : if (!IsSystemXHR()) {
2578 : // Get the referrer for the request.
2579 : //
2580 : // If it weren't for history.push/replaceState, we could just use the
2581 : // principal's URI here. But since we want changes to the URI effected
2582 : // by push/replaceState to be reflected in the XHR referrer, we have to
2583 : // be more clever.
2584 : //
2585 : // If the document's original URI (before any push/replaceStates) matches
2586 : // our principal, then we use the document's current URI (after
2587 : // push/replaceStates). Otherwise (if the document is, say, a data:
2588 : // URI), we just use the principal's URI.
2589 :
2590 0 : nsCOMPtr<nsIURI> principalURI;
2591 0 : mPrincipal->GetURI(getter_AddRefs(principalURI));
2592 :
2593 0 : nsIScriptContext* sc = GetContextForEventHandlers(&rv);
2594 0 : NS_ENSURE_SUCCESS(rv, rv);
2595 : nsCOMPtr<nsIDocument> doc =
2596 0 : nsContentUtils::GetDocumentFromScriptContext(sc);
2597 :
2598 0 : nsCOMPtr<nsIURI> docCurURI;
2599 0 : nsCOMPtr<nsIURI> docOrigURI;
2600 0 : if (doc) {
2601 0 : docCurURI = doc->GetDocumentURI();
2602 0 : docOrigURI = doc->GetOriginalURI();
2603 : }
2604 :
2605 0 : nsCOMPtr<nsIURI> referrerURI;
2606 :
2607 0 : if (principalURI && docCurURI && docOrigURI) {
2608 0 : bool equal = false;
2609 0 : principalURI->Equals(docOrigURI, &equal);
2610 0 : if (equal) {
2611 0 : referrerURI = docCurURI;
2612 : }
2613 : }
2614 :
2615 0 : if (!referrerURI)
2616 0 : referrerURI = principalURI;
2617 :
2618 0 : httpChannel->SetReferrer(referrerURI);
2619 : }
2620 :
2621 : // Some extensions override the http protocol handler and provide their own
2622 : // implementation. The channels returned from that implementation doesn't
2623 : // seem to always implement the nsIUploadChannel2 interface, presumably
2624 : // because it's a new interface.
2625 : // Eventually we should remove this and simply require that http channels
2626 : // implement the new interface.
2627 : // See bug 529041
2628 : nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
2629 1190 : do_QueryInterface(httpChannel);
2630 595 : if (!uploadChannel2) {
2631 : nsCOMPtr<nsIConsoleService> consoleService =
2632 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2633 0 : if (consoleService) {
2634 0 : consoleService->LogStringMessage(NS_LITERAL_STRING(
2635 : "Http channel implementation doesn't support nsIUploadChannel2. An extension has supplied a non-functional http protocol handler. This will break behavior and in future releases not work at all."
2636 0 : ).get());
2637 : }
2638 : }
2639 : }
2640 :
2641 597 : mUploadTransferred = 0;
2642 597 : mUploadTotal = 0;
2643 : // By default we don't have any upload, so mark upload complete.
2644 597 : mUploadComplete = true;
2645 597 : mErrorLoad = false;
2646 597 : mLoadLengthComputable = false;
2647 597 : mLoadTotal = 0;
2648 597 : mUploadProgress = 0;
2649 597 : mUploadProgressMax = 0;
2650 597 : if (aBody && httpChannel && !method.EqualsLiteral("GET")) {
2651 :
2652 88 : nsCAutoString charset;
2653 88 : nsCAutoString defaultContentType;
2654 88 : nsCOMPtr<nsIInputStream> postDataStream;
2655 :
2656 44 : rv = GetRequestBody(aBody, getter_AddRefs(postDataStream),
2657 44 : defaultContentType, charset);
2658 44 : NS_ENSURE_SUCCESS(rv, rv);
2659 :
2660 44 : if (postDataStream) {
2661 : // If no content type header was set by the client, we set it to
2662 : // application/xml.
2663 8 : nsCAutoString contentType;
2664 8 : if (NS_FAILED(httpChannel->
2665 : GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
2666 : contentType)) ||
2667 4 : contentType.IsEmpty()) {
2668 0 : contentType = defaultContentType;
2669 : }
2670 :
2671 : // We don't want to set a charset for streams.
2672 4 : if (!charset.IsEmpty()) {
2673 0 : nsCAutoString specifiedCharset;
2674 : bool haveCharset;
2675 : PRInt32 charsetStart, charsetEnd;
2676 : rv = NS_ExtractCharsetFromContentType(contentType, specifiedCharset,
2677 : &haveCharset, &charsetStart,
2678 0 : &charsetEnd);
2679 0 : if (NS_SUCCEEDED(rv)) {
2680 : // special case: the extracted charset is quoted with single quotes
2681 : // -- for the purpose of preserving what was set we want to handle
2682 : // them as delimiters (although they aren't really)
2683 0 : if (specifiedCharset.Length() >= 2 &&
2684 0 : specifiedCharset.First() == '\'' &&
2685 0 : specifiedCharset.Last() == '\'') {
2686 : specifiedCharset = Substring(specifiedCharset, 1,
2687 0 : specifiedCharset.Length() - 2);
2688 : }
2689 :
2690 : // If the content-type the page set already has a charset parameter,
2691 : // and it's the same charset, up to case, as |charset|, just send the
2692 : // page-set content-type header. Apparently at least
2693 : // google-web-toolkit is broken and relies on the exact case of its
2694 : // charset parameter, which makes things break if we use |charset|
2695 : // (which is always a fully resolved charset per our charset alias
2696 : // table, hence might be differently cased).
2697 0 : if (!specifiedCharset.Equals(charset,
2698 0 : nsCaseInsensitiveCStringComparator())) {
2699 0 : nsCAutoString newCharset("; charset=");
2700 0 : newCharset.Append(charset);
2701 : contentType.Replace(charsetStart, charsetEnd - charsetStart,
2702 0 : newCharset);
2703 : }
2704 : }
2705 : }
2706 :
2707 : // If necessary, wrap the stream in a buffered stream so as to guarantee
2708 : // support for our upload when calling ExplicitSetUploadStream.
2709 4 : if (!NS_InputStreamIsBuffered(postDataStream)) {
2710 0 : nsCOMPtr<nsIInputStream> bufferedStream;
2711 0 : rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
2712 : postDataStream,
2713 0 : 4096);
2714 0 : NS_ENSURE_SUCCESS(rv, rv);
2715 :
2716 0 : postDataStream = bufferedStream;
2717 : }
2718 :
2719 4 : mUploadComplete = false;
2720 4 : PRUint32 uploadTotal = 0;
2721 4 : postDataStream->Available(&uploadTotal);
2722 4 : mUploadTotal = uploadTotal;
2723 :
2724 : // We want to use a newer version of the upload channel that won't
2725 : // ignore the necessary headers for an empty Content-Type.
2726 12 : nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
2727 : // This assertion will fire if buggy extensions are installed
2728 4 : NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
2729 4 : if (uploadChannel2) {
2730 4 : uploadChannel2->ExplicitSetUploadStream(postDataStream, contentType,
2731 4 : -1, method, false);
2732 : }
2733 : else {
2734 : // http channel doesn't support the new nsIUploadChannel2. Emulate
2735 : // as best we can using nsIUploadChannel
2736 0 : if (contentType.IsEmpty()) {
2737 0 : contentType.AssignLiteral("application/octet-stream");
2738 : }
2739 : nsCOMPtr<nsIUploadChannel> uploadChannel =
2740 0 : do_QueryInterface(httpChannel);
2741 0 : uploadChannel->SetUploadStream(postDataStream, contentType, -1);
2742 : // Reset the method to its original value
2743 0 : httpChannel->SetRequestMethod(method);
2744 : }
2745 : }
2746 : }
2747 :
2748 597 : if (httpChannel) {
2749 1190 : nsCAutoString contentTypeHeader;
2750 1190 : rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
2751 595 : contentTypeHeader);
2752 595 : if (NS_SUCCEEDED(rv)) {
2753 8 : nsCAutoString contentType, charset;
2754 4 : rv = NS_ParseContentType(contentTypeHeader, contentType, charset);
2755 4 : NS_ENSURE_SUCCESS(rv, rv);
2756 :
2757 12 : if (!contentType.LowerCaseEqualsLiteral("text/plain") &&
2758 4 : !contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") &&
2759 4 : !contentType.LowerCaseEqualsLiteral("multipart/form-data")) {
2760 4 : mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
2761 : }
2762 : }
2763 : }
2764 :
2765 597 : ResetResponse();
2766 :
2767 597 : rv = CheckChannelForCrossSiteRequest(mChannel);
2768 597 : NS_ENSURE_SUCCESS(rv, rv);
2769 :
2770 597 : bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
2771 :
2772 : // Hook us up to listen to redirects and the like
2773 597 : mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
2774 597 : mChannel->SetNotificationCallbacks(this);
2775 :
2776 : // Create our listener
2777 1194 : nsCOMPtr<nsIStreamListener> listener = this;
2778 597 : if (mState & XML_HTTP_REQUEST_MULTIPART) {
2779 0 : listener = new nsMultipartProxyListener(listener);
2780 0 : if (!listener) {
2781 0 : return NS_ERROR_OUT_OF_MEMORY;
2782 : }
2783 : }
2784 :
2785 597 : if (!IsSystemXHR()) {
2786 : // Always create a nsCORSListenerProxy here even if it's
2787 : // a same-origin request right now, since it could be redirected.
2788 : listener = new nsCORSListenerProxy(listener, mPrincipal, mChannel,
2789 0 : withCredentials, true, &rv);
2790 0 : NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
2791 0 : NS_ENSURE_SUCCESS(rv, rv);
2792 : }
2793 :
2794 : // Bypass the network cache in cases where it makes no sense:
2795 : // 1) Multipart responses are very large and would likely be doomed by the
2796 : // cache once they grow too large, so they are not worth caching.
2797 : // 2) POST responses are always unique, and we provide no API that would
2798 : // allow our consumers to specify a "cache key" to access old POST
2799 : // responses, so they are not worth caching.
2800 597 : if ((mState & XML_HTTP_REQUEST_MULTIPART) || method.EqualsLiteral("POST")) {
2801 : AddLoadFlags(mChannel,
2802 12 : nsIRequest::LOAD_BYPASS_CACHE | nsIRequest::INHIBIT_CACHING);
2803 : }
2804 : // When we are sync loading, we need to bypass the local cache when it would
2805 : // otherwise block us waiting for exclusive access to the cache. If we don't
2806 : // do this, then we could dead lock in some cases (see bug 309424).
2807 585 : else if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
2808 : AddLoadFlags(mChannel,
2809 50 : nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
2810 : }
2811 :
2812 : // Since we expect XML data, set the type hint accordingly
2813 : // This means that we always try to parse local files as XML
2814 : // ignoring return value, as this is not critical
2815 597 : mChannel->SetContentType(NS_LITERAL_CSTRING("application/xml"));
2816 :
2817 : // We're about to send the request. Start our timeout.
2818 597 : mRequestSentTime = PR_Now();
2819 597 : StartTimeoutTimer();
2820 :
2821 : // Set up the preflight if needed
2822 597 : if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
2823 : // Check to see if this initial OPTIONS request has already been cached
2824 : // in our special Access Control Cache.
2825 :
2826 : rv = NS_StartCORSPreflight(mChannel, listener,
2827 : mPrincipal, withCredentials,
2828 : mCORSUnsafeHeaders,
2829 0 : getter_AddRefs(mCORSPreflightChannel));
2830 0 : NS_ENSURE_SUCCESS(rv, rv);
2831 : }
2832 : else {
2833 : // Start reading from the channel
2834 597 : rv = mChannel->AsyncOpen(listener, nsnull);
2835 : }
2836 :
2837 597 : if (NS_FAILED(rv)) {
2838 : // Drop our ref to the channel to avoid cycles
2839 0 : mChannel = nsnull;
2840 0 : mCORSPreflightChannel = nsnull;
2841 0 : return rv;
2842 : }
2843 :
2844 : // Either AsyncOpen was called, or CORS will open the channel later.
2845 597 : mWaitingForOnStopRequest = true;
2846 :
2847 : // If we're synchronous, spin an event loop here and wait
2848 597 : if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
2849 58 : mState |= XML_HTTP_REQUEST_SYNCLOOPING;
2850 :
2851 116 : nsCOMPtr<nsIDocument> suspendedDoc;
2852 116 : nsCOMPtr<nsIRunnable> resumeTimeoutRunnable;
2853 58 : if (GetOwner()) {
2854 0 : nsCOMPtr<nsIDOMWindow> topWindow;
2855 0 : if (NS_SUCCEEDED(GetOwner()->GetTop(getter_AddRefs(topWindow)))) {
2856 0 : nsCOMPtr<nsPIDOMWindow> suspendedWindow(do_QueryInterface(topWindow));
2857 0 : if (suspendedWindow &&
2858 0 : (suspendedWindow = suspendedWindow->GetCurrentInnerWindow())) {
2859 0 : suspendedDoc = do_QueryInterface(suspendedWindow->GetExtantDocument());
2860 0 : if (suspendedDoc) {
2861 0 : suspendedDoc->SuppressEventHandling();
2862 : }
2863 0 : suspendedWindow->SuspendTimeouts(1, false);
2864 0 : resumeTimeoutRunnable = new nsResumeTimeoutsEvent(suspendedWindow);
2865 : }
2866 : }
2867 : }
2868 :
2869 58 : ChangeState(XML_HTTP_REQUEST_SENT);
2870 : // Note, calling ChangeState may have cleared
2871 : // XML_HTTP_REQUEST_SYNCLOOPING flag.
2872 58 : nsIThread *thread = NS_GetCurrentThread();
2873 58 : while (mState & XML_HTTP_REQUEST_SYNCLOOPING) {
2874 1230 : if (!NS_ProcessNextEvent(thread)) {
2875 0 : rv = NS_ERROR_UNEXPECTED;
2876 0 : break;
2877 : }
2878 : }
2879 :
2880 58 : if (suspendedDoc) {
2881 0 : suspendedDoc->UnsuppressEventHandlingAndFireEvents(true);
2882 : }
2883 :
2884 58 : if (resumeTimeoutRunnable) {
2885 0 : NS_DispatchToCurrentThread(resumeTimeoutRunnable);
2886 : }
2887 : } else {
2888 : // Now that we've successfully opened the channel, we can change state. Note
2889 : // that this needs to come after the AsyncOpen() and rv check, because this
2890 : // can run script that would try to restart this request, and that could end
2891 : // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
2892 539 : ChangeState(XML_HTTP_REQUEST_SENT);
2893 1625 : if ((!mUploadComplete &&
2894 547 : HasListenersFor(NS_LITERAL_STRING(UPLOADPROGRESS_STR))) ||
2895 1078 : (mUpload && mUpload->HasListenersFor(NS_LITERAL_STRING(PROGRESS_STR)))) {
2896 0 : StartProgressEventTimer();
2897 : }
2898 539 : DispatchProgressEvent(this, NS_LITERAL_STRING(LOADSTART_STR), false,
2899 539 : 0, 0);
2900 539 : if (mUpload && !mUploadComplete) {
2901 0 : DispatchProgressEvent(mUpload, NS_LITERAL_STRING(LOADSTART_STR), true,
2902 0 : 0, mUploadTotal);
2903 : }
2904 : }
2905 :
2906 597 : if (!mChannel) {
2907 0 : return NS_ERROR_FAILURE;
2908 : }
2909 :
2910 597 : return rv;
2911 : }
2912 :
2913 : /* void setRequestHeader (in AUTF8String header, in AUTF8String value); */
2914 : NS_IMETHODIMP
2915 80 : nsXMLHttpRequest::SetRequestHeader(const nsACString& header,
2916 : const nsACString& value)
2917 : {
2918 : nsresult rv;
2919 :
2920 : // Make sure we don't store an invalid header name in mCORSUnsafeHeaders
2921 80 : if (!IsValidHTTPToken(header)) {
2922 0 : return NS_ERROR_FAILURE;
2923 : }
2924 :
2925 : // Check that we haven't already opened the channel. We can't rely on
2926 : // the channel throwing from mChannel->SetRequestHeader since we might
2927 : // still be waiting for mCORSPreflightChannel to actually open mChannel
2928 80 : if (mCORSPreflightChannel) {
2929 : bool pending;
2930 0 : rv = mCORSPreflightChannel->IsPending(&pending);
2931 0 : NS_ENSURE_SUCCESS(rv, rv);
2932 :
2933 0 : if (pending) {
2934 0 : return NS_ERROR_IN_PROGRESS;
2935 : }
2936 : }
2937 :
2938 80 : if (!(mState & XML_HTTP_REQUEST_OPENED))
2939 1 : return NS_ERROR_IN_PROGRESS;
2940 :
2941 79 : if (!mChannel) // open() initializes mChannel, and open()
2942 0 : return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
2943 :
2944 158 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2945 79 : if (!httpChannel) {
2946 0 : return NS_OK;
2947 : }
2948 :
2949 : // Prevent modification to certain HTTP headers (see bug 302263), unless
2950 : // the executing script has UniversalXPConnect.
2951 :
2952 : bool privileged;
2953 79 : rv = IsCapabilityEnabled("UniversalXPConnect", &privileged);
2954 79 : if (NS_FAILED(rv))
2955 0 : return NS_ERROR_FAILURE;
2956 :
2957 79 : if (!privileged) {
2958 : // Check for dangerous headers
2959 : const char *kInvalidHeaders[] = {
2960 : "accept-charset", "accept-encoding", "access-control-request-headers",
2961 : "access-control-request-method", "connection", "content-length",
2962 : "cookie", "cookie2", "content-transfer-encoding", "date", "expect",
2963 : "host", "keep-alive", "origin", "referer", "te", "trailer",
2964 : "transfer-encoding", "upgrade", "user-agent", "via"
2965 0 : };
2966 : PRUint32 i;
2967 0 : for (i = 0; i < ArrayLength(kInvalidHeaders); ++i) {
2968 0 : if (header.LowerCaseEqualsASCII(kInvalidHeaders[i])) {
2969 0 : NS_WARNING("refusing to set request header");
2970 0 : return NS_OK;
2971 : }
2972 : }
2973 0 : if (StringBeginsWith(header, NS_LITERAL_CSTRING("proxy-"),
2974 0 : nsCaseInsensitiveCStringComparator()) ||
2975 0 : StringBeginsWith(header, NS_LITERAL_CSTRING("sec-"),
2976 0 : nsCaseInsensitiveCStringComparator())) {
2977 0 : NS_WARNING("refusing to set request header");
2978 0 : return NS_OK;
2979 : }
2980 :
2981 : // Check for dangerous cross-site headers
2982 0 : bool safeHeader = IsSystemXHR();
2983 0 : if (!safeHeader) {
2984 : // Content-Type isn't always safe, but we'll deal with it in Send()
2985 : const char *kCrossOriginSafeHeaders[] = {
2986 : "accept", "accept-language", "content-language", "content-type",
2987 : "last-event-id"
2988 0 : };
2989 0 : for (i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
2990 0 : if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
2991 0 : safeHeader = true;
2992 0 : break;
2993 : }
2994 : }
2995 : }
2996 :
2997 0 : if (!safeHeader) {
2998 0 : mCORSUnsafeHeaders.AppendElement(header);
2999 : }
3000 : }
3001 :
3002 : // We need to set, not add to, the header.
3003 79 : rv = httpChannel->SetRequestHeader(header, value, false);
3004 79 : if (NS_SUCCEEDED(rv)) {
3005 : // We'll want to duplicate this header for any replacement channels (eg. on redirect)
3006 : RequestHeader reqHeader = {
3007 : nsCString(header), nsCString(value)
3008 158 : };
3009 79 : mModifiedRequestHeaders.AppendElement(reqHeader);
3010 : }
3011 :
3012 79 : return rv;
3013 : }
3014 :
3015 : /* attribute unsigned long timeout; */
3016 : NS_IMETHODIMP
3017 0 : nsXMLHttpRequest::GetTimeout(PRUint32 *aTimeout)
3018 : {
3019 0 : *aTimeout = mTimeoutMilliseconds;
3020 0 : return NS_OK;
3021 : }
3022 : NS_IMETHODIMP
3023 0 : nsXMLHttpRequest::SetTimeout(PRUint32 aTimeout)
3024 : {
3025 0 : if ((mState & (XML_HTTP_REQUEST_ASYNC | XML_HTTP_REQUEST_UNSENT)) ||
3026 0 : !HasOrHasHadOwner()) {
3027 0 : mTimeoutMilliseconds = aTimeout;
3028 0 : if (mRequestSentTime) {
3029 0 : StartTimeoutTimer();
3030 : }
3031 0 : return NS_OK;
3032 : }
3033 :
3034 : /* Timeout is not supported for synchronous requests with an owning window,
3035 : per XHR2 spec. */
3036 0 : LogMessage("TimeoutSyncXHRWarning", GetOwner());
3037 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
3038 : }
3039 :
3040 : void
3041 597 : nsXMLHttpRequest::StartTimeoutTimer()
3042 : {
3043 597 : NS_ABORT_IF_FALSE(mRequestSentTime,
3044 : "StartTimeoutTimer mustn't be called before the request was sent!");
3045 597 : if (mState & XML_HTTP_REQUEST_DONE) {
3046 : // do nothing!
3047 0 : return;
3048 : }
3049 :
3050 597 : if (mTimeoutTimer) {
3051 0 : mTimeoutTimer->Cancel();
3052 : }
3053 :
3054 597 : if (!mTimeoutMilliseconds) {
3055 597 : return;
3056 : }
3057 :
3058 0 : if (!mTimeoutTimer) {
3059 0 : mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
3060 : }
3061 : PRUint32 elapsed =
3062 0 : (PRUint32)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
3063 0 : mTimeoutTimer->InitWithCallback(
3064 : this,
3065 : mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
3066 : nsITimer::TYPE_ONE_SHOT
3067 0 : );
3068 : }
3069 :
3070 : /* readonly attribute long readyState; */
3071 : NS_IMETHODIMP
3072 124 : nsXMLHttpRequest::GetReadyState(PRUint16 *aState)
3073 : {
3074 124 : NS_ENSURE_ARG_POINTER(aState);
3075 : // Translate some of our internal states for external consumers
3076 124 : if (mState & XML_HTTP_REQUEST_UNSENT) {
3077 0 : *aState = UNSENT;
3078 124 : } else if (mState & (XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT)) {
3079 2 : *aState = OPENED;
3080 122 : } else if (mState & XML_HTTP_REQUEST_HEADERS_RECEIVED) {
3081 2 : *aState = HEADERS_RECEIVED;
3082 120 : } else if (mState & (XML_HTTP_REQUEST_LOADING | XML_HTTP_REQUEST_STOPPED)) {
3083 1 : *aState = LOADING;
3084 119 : } else if (mState & XML_HTTP_REQUEST_DONE) {
3085 119 : *aState = DONE;
3086 : } else {
3087 0 : NS_ERROR("Should not happen");
3088 : }
3089 :
3090 124 : return NS_OK;
3091 : }
3092 :
3093 : /* void overrideMimeType(in AUTF8String mimetype); */
3094 : NS_IMETHODIMP
3095 534 : nsXMLHttpRequest::OverrideMimeType(const nsACString& aMimeType)
3096 : {
3097 : // XXX Should we do some validation here?
3098 534 : mOverrideMimeType.Assign(aMimeType);
3099 534 : return NS_OK;
3100 : }
3101 :
3102 :
3103 : /* attribute boolean multipart; */
3104 : NS_IMETHODIMP
3105 0 : nsXMLHttpRequest::GetMultipart(bool *_retval)
3106 : {
3107 0 : *_retval = !!(mState & XML_HTTP_REQUEST_MULTIPART);
3108 :
3109 0 : return NS_OK;
3110 : }
3111 :
3112 : /* attribute boolean multipart; */
3113 : NS_IMETHODIMP
3114 0 : nsXMLHttpRequest::SetMultipart(bool aMultipart)
3115 : {
3116 0 : if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
3117 : // Can't change this while we're in the middle of something.
3118 0 : return NS_ERROR_IN_PROGRESS;
3119 : }
3120 :
3121 0 : if (aMultipart) {
3122 0 : mState |= XML_HTTP_REQUEST_MULTIPART;
3123 : } else {
3124 0 : mState &= ~XML_HTTP_REQUEST_MULTIPART;
3125 : }
3126 :
3127 0 : return NS_OK;
3128 : }
3129 :
3130 : /* attribute boolean mozBackgroundRequest; */
3131 : NS_IMETHODIMP
3132 0 : nsXMLHttpRequest::GetMozBackgroundRequest(bool *_retval)
3133 : {
3134 0 : *_retval = !!(mState & XML_HTTP_REQUEST_BACKGROUND);
3135 :
3136 0 : return NS_OK;
3137 : }
3138 :
3139 : /* attribute boolean mozBackgroundRequest; */
3140 : NS_IMETHODIMP
3141 87 : nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest)
3142 : {
3143 : bool privileged;
3144 :
3145 87 : nsresult rv = IsCapabilityEnabled("UniversalXPConnect", &privileged);
3146 87 : NS_ENSURE_SUCCESS(rv, rv);
3147 :
3148 87 : if (!privileged)
3149 0 : return NS_ERROR_DOM_SECURITY_ERR;
3150 :
3151 87 : if (!(mState & XML_HTTP_REQUEST_UNSENT)) {
3152 : // Can't change this while we're in the middle of something.
3153 0 : return NS_ERROR_IN_PROGRESS;
3154 : }
3155 :
3156 87 : if (aMozBackgroundRequest) {
3157 87 : mState |= XML_HTTP_REQUEST_BACKGROUND;
3158 : } else {
3159 0 : mState &= ~XML_HTTP_REQUEST_BACKGROUND;
3160 : }
3161 :
3162 87 : return NS_OK;
3163 : }
3164 :
3165 : /* attribute boolean withCredentials; */
3166 : NS_IMETHODIMP
3167 0 : nsXMLHttpRequest::GetWithCredentials(bool *_retval)
3168 : {
3169 0 : *_retval = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
3170 :
3171 0 : return NS_OK;
3172 : }
3173 :
3174 : /* attribute boolean withCredentials; */
3175 : NS_IMETHODIMP
3176 1 : nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials)
3177 : {
3178 : // Return error if we're already processing a request
3179 1 : if (XML_HTTP_REQUEST_SENT & mState) {
3180 0 : return NS_ERROR_FAILURE;
3181 : }
3182 :
3183 : // sync request is not allowed setting withCredentials in window context
3184 1 : if (HasOrHasHadOwner() &&
3185 0 : !(mState & (XML_HTTP_REQUEST_UNSENT | XML_HTTP_REQUEST_ASYNC))) {
3186 0 : LogMessage("WithCredentialsSyncXHRWarning", GetOwner());
3187 0 : return NS_ERROR_DOM_INVALID_ACCESS_ERR;
3188 : }
3189 :
3190 1 : if (aWithCredentials) {
3191 0 : mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
3192 : }
3193 : else {
3194 1 : mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
3195 : }
3196 1 : return NS_OK;
3197 : }
3198 :
3199 : nsresult
3200 2953 : nsXMLHttpRequest::ChangeState(PRUint32 aState, bool aBroadcast)
3201 : {
3202 : // If we are setting one of the mutually exclusive states,
3203 : // unset those state bits first.
3204 2953 : if (aState & XML_HTTP_REQUEST_LOADSTATES) {
3205 2953 : mState &= ~XML_HTTP_REQUEST_LOADSTATES;
3206 : }
3207 2953 : mState |= aState;
3208 2953 : nsresult rv = NS_OK;
3209 :
3210 3507 : if (mProgressNotifier &&
3211 3507 : !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
3212 416 : mProgressTimerIsActive = false;
3213 416 : mProgressNotifier->Cancel();
3214 : }
3215 :
3216 2953 : if ((aState & XML_HTTP_REQUEST_LOADSTATES) && // Broadcast load states only
3217 : aBroadcast &&
3218 : (mState & XML_HTTP_REQUEST_ASYNC ||
3219 : aState & XML_HTTP_REQUEST_OPENED ||
3220 : aState & XML_HTTP_REQUEST_DONE)) {
3221 5630 : nsCOMPtr<nsIDOMEvent> event;
3222 2815 : rv = CreateReadystatechangeEvent(getter_AddRefs(event));
3223 2815 : NS_ENSURE_SUCCESS(rv, rv);
3224 :
3225 5630 : DispatchDOMEvent(nsnull, event, nsnull, nsnull);
3226 : }
3227 :
3228 2953 : return rv;
3229 : }
3230 :
3231 : /*
3232 : * Simple helper class that just forwards the redirect callback back
3233 : * to the nsXMLHttpRequest.
3234 : */
3235 : class AsyncVerifyRedirectCallbackForwarder : public nsIAsyncVerifyRedirectCallback
3236 0 : {
3237 : public:
3238 0 : AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest *xhr)
3239 0 : : mXHR(xhr)
3240 : {
3241 0 : }
3242 :
3243 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
3244 1464 : NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
3245 :
3246 : // nsIAsyncVerifyRedirectCallback implementation
3247 0 : NS_IMETHOD OnRedirectVerifyCallback(nsresult result)
3248 : {
3249 0 : mXHR->OnRedirectVerifyCallback(result);
3250 :
3251 0 : return NS_OK;
3252 : }
3253 :
3254 : private:
3255 : nsRefPtr<nsXMLHttpRequest> mXHR;
3256 : };
3257 :
3258 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
3259 :
3260 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AsyncVerifyRedirectCallbackForwarder)
3261 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mXHR, nsIXMLHttpRequest)
3262 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3263 :
3264 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AsyncVerifyRedirectCallbackForwarder)
3265 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mXHR)
3266 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3267 :
3268 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
3269 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
3270 0 : NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
3271 0 : NS_INTERFACE_MAP_END
3272 :
3273 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
3274 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)
3275 :
3276 :
3277 : /////////////////////////////////////////////////////
3278 : // nsIChannelEventSink methods:
3279 : //
3280 : NS_IMETHODIMP
3281 35 : nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
3282 : nsIChannel *aNewChannel,
3283 : PRUint32 aFlags,
3284 : nsIAsyncVerifyRedirectCallback *callback)
3285 : {
3286 35 : NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
3287 :
3288 : nsresult rv;
3289 :
3290 35 : if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
3291 35 : rv = CheckChannelForCrossSiteRequest(aNewChannel);
3292 35 : if (NS_FAILED(rv)) {
3293 : NS_WARNING("nsXMLHttpRequest::OnChannelRedirect: "
3294 0 : "CheckChannelForCrossSiteRequest returned failure");
3295 0 : return rv;
3296 : }
3297 :
3298 : // Disable redirects for preflighted cross-site requests entirely for now
3299 : // Note, do this after the call to CheckChannelForCrossSiteRequest
3300 : // to make sure that XML_HTTP_REQUEST_USE_XSITE_AC is up-to-date
3301 35 : if ((mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT)) {
3302 0 : return NS_ERROR_DOM_BAD_URI;
3303 : }
3304 : }
3305 :
3306 : // Prepare to receive callback
3307 35 : mRedirectCallback = callback;
3308 35 : mNewRedirectChannel = aNewChannel;
3309 :
3310 35 : if (mChannelEventSink) {
3311 : nsRefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
3312 0 : new AsyncVerifyRedirectCallbackForwarder(this);
3313 :
3314 0 : rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
3315 : aNewChannel,
3316 0 : aFlags, fwd);
3317 0 : if (NS_FAILED(rv)) {
3318 0 : mRedirectCallback = nsnull;
3319 0 : mNewRedirectChannel = nsnull;
3320 : }
3321 0 : return rv;
3322 : }
3323 35 : OnRedirectVerifyCallback(NS_OK);
3324 35 : return NS_OK;
3325 : }
3326 :
3327 : void
3328 35 : nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
3329 : {
3330 35 : NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
3331 35 : NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
3332 :
3333 35 : if (NS_SUCCEEDED(result)) {
3334 35 : mChannel = mNewRedirectChannel;
3335 :
3336 70 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
3337 35 : if (httpChannel) {
3338 : // Ensure all original headers are duplicated for the new channel (bug #553888)
3339 71 : for (PRUint32 i = mModifiedRequestHeaders.Length(); i > 0; ) {
3340 1 : --i;
3341 2 : httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
3342 1 : mModifiedRequestHeaders[i].value,
3343 3 : false);
3344 : }
3345 : }
3346 : } else {
3347 0 : mErrorLoad = true;
3348 : }
3349 :
3350 35 : mNewRedirectChannel = nsnull;
3351 :
3352 35 : mRedirectCallback->OnRedirectVerifyCallback(result);
3353 35 : mRedirectCallback = nsnull;
3354 35 : }
3355 :
3356 : /////////////////////////////////////////////////////
3357 : // nsIProgressEventSink methods:
3358 : //
3359 :
3360 : void
3361 1153 : nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
3362 : {
3363 1153 : if (aFinalProgress && mProgressTimerIsActive) {
3364 416 : mProgressTimerIsActive = false;
3365 416 : mProgressNotifier->Cancel();
3366 : }
3367 :
3368 2710 : if (mProgressTimerIsActive ||
3369 1015 : !mProgressSinceLastProgressEvent ||
3370 : mErrorLoad ||
3371 542 : !(mState & XML_HTTP_REQUEST_ASYNC)) {
3372 631 : return;
3373 : }
3374 :
3375 522 : if (!aFinalProgress) {
3376 420 : StartProgressEventTimer();
3377 : }
3378 :
3379 : // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
3380 : // XML_HTTP_REQUEST_SENT
3381 522 : if ((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState) {
3382 0 : if (aFinalProgress) {
3383 0 : mUploadTotal = mUploadTransferred;
3384 0 : mUploadProgressMax = mUploadProgress;
3385 0 : mUploadLengthComputable = true;
3386 : }
3387 0 : DispatchProgressEvent(this, NS_LITERAL_STRING(UPLOADPROGRESS_STR),
3388 : true, mUploadLengthComputable, mUploadTransferred,
3389 : mUploadTotal, mUploadProgress,
3390 0 : mUploadProgressMax);
3391 0 : if (mUpload && !mUploadComplete) {
3392 0 : DispatchProgressEvent(mUpload, NS_LITERAL_STRING(PROGRESS_STR),
3393 : true, mUploadLengthComputable, mUploadTransferred,
3394 : mUploadTotal, mUploadProgress,
3395 0 : mUploadProgressMax);
3396 : }
3397 : } else {
3398 522 : if (aFinalProgress) {
3399 102 : mLoadTotal = mLoadTransferred;
3400 102 : mLoadLengthComputable = true;
3401 : }
3402 522 : mInLoadProgressEvent = true;
3403 522 : DispatchProgressEvent(this, NS_LITERAL_STRING(PROGRESS_STR),
3404 : true, mLoadLengthComputable, mLoadTransferred,
3405 522 : mLoadTotal, mLoadTransferred, mLoadTotal);
3406 522 : mInLoadProgressEvent = false;
3407 522 : if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT ||
3408 : mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER) {
3409 0 : mResponseBody.Truncate();
3410 0 : mResponseText.Truncate();
3411 0 : mResultArrayBuffer = nsnull;
3412 : }
3413 : }
3414 :
3415 522 : mProgressSinceLastProgressEvent = false;
3416 : }
3417 :
3418 : NS_IMETHODIMP
3419 1 : nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, PRUint64 aProgress, PRUint64 aProgressMax)
3420 : {
3421 : // We're in middle of processing multipart headers and we don't want to report
3422 : // any progress because upload's 'load' is dispatched when we start to load
3423 : // the first response.
3424 1 : if (XML_HTTP_REQUEST_MPART_HEADERS & mState) {
3425 0 : return NS_OK;
3426 : }
3427 :
3428 : // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
3429 : // XML_HTTP_REQUEST_SENT
3430 1 : bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
3431 : // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
3432 : // So, try to remove the headers, if possible.
3433 1 : bool lengthComputable = (aProgressMax != LL_MAXUINT);
3434 1 : if (upload) {
3435 0 : PRUint64 loaded = aProgress;
3436 0 : PRUint64 total = aProgressMax;
3437 0 : if (lengthComputable) {
3438 0 : PRUint64 headerSize = aProgressMax - mUploadTotal;
3439 0 : loaded -= headerSize;
3440 0 : total -= headerSize;
3441 : }
3442 0 : mUploadLengthComputable = lengthComputable;
3443 0 : mUploadTransferred = loaded;
3444 0 : mUploadProgress = aProgress;
3445 0 : mUploadProgressMax = aProgressMax;
3446 0 : mProgressSinceLastProgressEvent = true;
3447 :
3448 0 : MaybeDispatchProgressEvents(false);
3449 : } else {
3450 1 : mLoadLengthComputable = lengthComputable;
3451 1 : mLoadTotal = lengthComputable ? aProgressMax : 0;
3452 :
3453 : // Don't dispatch progress events here. OnDataAvailable will take care
3454 : // of that.
3455 : }
3456 :
3457 1 : if (mProgressEventSink) {
3458 0 : mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
3459 0 : aProgressMax);
3460 : }
3461 :
3462 1 : return NS_OK;
3463 : }
3464 :
3465 : NS_IMETHODIMP
3466 4 : nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatus, const PRUnichar *aStatusArg)
3467 : {
3468 4 : if (mProgressEventSink) {
3469 0 : mProgressEventSink->OnStatus(aRequest, aContext, aStatus, aStatusArg);
3470 : }
3471 :
3472 4 : return NS_OK;
3473 : }
3474 :
3475 : bool
3476 2255 : nsXMLHttpRequest::AllowUploadProgress()
3477 : {
3478 2255 : return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
3479 2255 : (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
3480 : }
3481 :
3482 : /////////////////////////////////////////////////////
3483 : // nsIInterfaceRequestor methods:
3484 : //
3485 : NS_IMETHODIMP
3486 1324 : nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
3487 : {
3488 : nsresult rv;
3489 :
3490 : // Make sure to return ourselves for the channel event sink interface and
3491 : // progress event sink interface, no matter what. We can forward these to
3492 : // mNotificationCallbacks if it wants to get notifications for them. But we
3493 : // need to see these notifications for proper functioning.
3494 1324 : if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
3495 35 : mChannelEventSink = do_GetInterface(mNotificationCallbacks);
3496 35 : *aResult = static_cast<nsIChannelEventSink*>(this);
3497 35 : NS_ADDREF_THIS();
3498 35 : return NS_OK;
3499 1289 : } else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
3500 615 : mProgressEventSink = do_GetInterface(mNotificationCallbacks);
3501 615 : *aResult = static_cast<nsIProgressEventSink*>(this);
3502 615 : NS_ADDREF_THIS();
3503 615 : return NS_OK;
3504 : }
3505 :
3506 : // Now give mNotificationCallbacks (if non-null) a chance to return the
3507 : // desired interface.
3508 674 : if (mNotificationCallbacks) {
3509 448 : rv = mNotificationCallbacks->GetInterface(aIID, aResult);
3510 448 : if (NS_SUCCEEDED(rv)) {
3511 0 : NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
3512 0 : return rv;
3513 : }
3514 : }
3515 :
3516 674 : if (mState & XML_HTTP_REQUEST_BACKGROUND) {
3517 182 : nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
3518 :
3519 : // Ignore failure to get component, we may not have all its dependencies
3520 : // available
3521 91 : if (NS_SUCCEEDED(rv)) {
3522 91 : rv = badCertHandler->GetInterface(aIID, aResult);
3523 91 : if (NS_SUCCEEDED(rv))
3524 0 : return rv;
3525 : }
3526 : }
3527 1166 : else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
3528 583 : aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
3529 : nsCOMPtr<nsIPromptFactory> wwatch =
3530 0 : do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
3531 0 : NS_ENSURE_SUCCESS(rv, rv);
3532 :
3533 : // Get the an auth prompter for our window so that the parenting
3534 : // of the dialogs works as it should when using tabs.
3535 :
3536 0 : nsCOMPtr<nsIDOMWindow> window;
3537 0 : if (GetOwner()) {
3538 0 : window = GetOwner()->GetOuterWindow();
3539 : }
3540 :
3541 0 : return wwatch->GetPrompt(window, aIID,
3542 0 : reinterpret_cast<void**>(aResult));
3543 :
3544 : }
3545 :
3546 674 : return QueryInterface(aIID, aResult);
3547 : }
3548 :
3549 : NS_IMETHODIMP
3550 0 : nsXMLHttpRequest::GetUpload(nsIXMLHttpRequestUpload** aUpload)
3551 : {
3552 0 : *aUpload = nsnull;
3553 :
3554 : nsresult rv;
3555 : nsIScriptContext* scriptContext =
3556 0 : GetContextForEventHandlers(&rv);
3557 0 : NS_ENSURE_SUCCESS(rv, rv);
3558 0 : if (!mUpload) {
3559 0 : mUpload = new nsXMLHttpRequestUpload(this);
3560 0 : NS_ENSURE_TRUE(mUpload, NS_ERROR_OUT_OF_MEMORY);
3561 : }
3562 0 : NS_ADDREF(*aUpload = mUpload);
3563 0 : return NS_OK;
3564 : }
3565 :
3566 : void
3567 0 : nsXMLHttpRequest::HandleTimeoutCallback()
3568 : {
3569 0 : if (mState & XML_HTTP_REQUEST_DONE) {
3570 0 : NS_NOTREACHED("nsXMLHttpRequest::HandleTimeoutCallback with completed request");
3571 : // do nothing!
3572 0 : return;
3573 : }
3574 :
3575 0 : CloseRequestWithError(NS_LITERAL_STRING(TIMEOUT_STR),
3576 0 : XML_HTTP_REQUEST_TIMED_OUT);
3577 : }
3578 :
3579 : NS_IMETHODIMP
3580 4 : nsXMLHttpRequest::Notify(nsITimer* aTimer)
3581 : {
3582 4 : if (mProgressNotifier == aTimer) {
3583 4 : HandleProgressTimerCallback();
3584 4 : return NS_OK;
3585 : }
3586 :
3587 0 : if (mTimeoutTimer == aTimer) {
3588 0 : HandleTimeoutCallback();
3589 0 : return NS_OK;
3590 : }
3591 :
3592 : // Just in case some JS user wants to QI to nsITimerCallback and play with us...
3593 0 : NS_WARNING("Unexpected timer!");
3594 0 : return NS_ERROR_INVALID_POINTER;
3595 : }
3596 :
3597 : void
3598 4 : nsXMLHttpRequest::HandleProgressTimerCallback()
3599 : {
3600 4 : mProgressTimerIsActive = false;
3601 4 : if (!(XML_HTTP_REQUEST_MPART_HEADERS & mState)) {
3602 4 : MaybeDispatchProgressEvents(false);
3603 : }
3604 4 : }
3605 :
3606 : void
3607 420 : nsXMLHttpRequest::StartProgressEventTimer()
3608 : {
3609 420 : if (!mProgressNotifier) {
3610 416 : mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
3611 : }
3612 420 : if (mProgressNotifier) {
3613 420 : mProgressEventWasDelayed = false;
3614 420 : mProgressTimerIsActive = true;
3615 420 : mProgressNotifier->Cancel();
3616 420 : mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
3617 420 : nsITimer::TYPE_ONE_SHOT);
3618 : }
3619 420 : }
3620 :
3621 0 : NS_IMPL_ISUPPORTS1(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
3622 :
3623 0 : NS_IMETHODIMP nsXMLHttpRequest::
3624 : nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value)
3625 : {
3626 : // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
3627 0 : bool chrome = false; // default to false in case IsCapabilityEnabled fails
3628 0 : IsCapabilityEnabled("UniversalXPConnect", &chrome);
3629 0 : if (!chrome &&
3630 0 : (header.LowerCaseEqualsASCII("set-cookie") ||
3631 0 : header.LowerCaseEqualsASCII("set-cookie2"))) {
3632 0 : NS_WARNING("blocked access to response header");
3633 : } else {
3634 0 : mHeaders.Append(header);
3635 0 : mHeaders.Append(": ");
3636 0 : mHeaders.Append(value);
3637 0 : mHeaders.Append('\n');
3638 : }
3639 0 : return NS_OK;
3640 : }
3641 :
3642 : // DOM event class to handle progress notifications
3643 522 : nsXMLHttpProgressEvent::nsXMLHttpProgressEvent(nsIDOMProgressEvent* aInner,
3644 : PRUint64 aCurrentProgress,
3645 : PRUint64 aMaxProgress,
3646 : nsPIDOMWindow* aWindow)
3647 522 : : mWindow(aWindow)
3648 : {
3649 522 : mInner = static_cast<nsDOMProgressEvent*>(aInner);
3650 522 : mCurProgress = aCurrentProgress;
3651 522 : mMaxProgress = aMaxProgress;
3652 522 : }
3653 :
3654 1044 : nsXMLHttpProgressEvent::~nsXMLHttpProgressEvent()
3655 2088 : {}
3656 :
3657 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpProgressEvent)
3658 :
3659 : DOMCI_DATA(XMLHttpProgressEvent, nsXMLHttpProgressEvent)
3660 :
3661 : // QueryInterface implementation for nsXMLHttpProgressEvent
3662 4698 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpProgressEvent)
3663 2610 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMProgressEvent)
3664 2610 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEvent, nsIDOMProgressEvent)
3665 2088 : NS_INTERFACE_MAP_ENTRY(nsIDOMNSEvent)
3666 1566 : NS_INTERFACE_MAP_ENTRY(nsIPrivateDOMEvent)
3667 522 : NS_INTERFACE_MAP_ENTRY(nsIDOMProgressEvent)
3668 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMLSProgressEvent)
3669 0 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XMLHttpProgressEvent)
3670 0 : NS_INTERFACE_MAP_END
3671 :
3672 3654 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpProgressEvent)
3673 3654 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpProgressEvent)
3674 :
3675 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpProgressEvent)
3676 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mInner);
3677 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow);
3678 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3679 :
3680 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpProgressEvent)
3681 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mInner,
3682 : nsIDOMProgressEvent)
3683 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow);
3684 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3685 :
3686 0 : NS_IMETHODIMP nsXMLHttpProgressEvent::GetInput(nsIDOMLSInput * *aInput)
3687 : {
3688 0 : *aInput = nsnull;
3689 0 : return NS_ERROR_NOT_IMPLEMENTED;
3690 : }
3691 :
3692 : void
3693 0 : nsXMLHttpProgressEvent::WarnAboutLSProgressEvent(nsIDocument::DeprecatedOperations aOperation)
3694 : {
3695 0 : if (!mWindow) {
3696 0 : return;
3697 : }
3698 : nsCOMPtr<nsIDocument> document =
3699 0 : do_QueryInterface(mWindow->GetExtantDocument());
3700 0 : if (!document) {
3701 : return;
3702 : }
3703 0 : document->WarnOnceAbout(aOperation);
3704 : }
3705 :
3706 0 : NS_IMETHODIMP nsXMLHttpProgressEvent::GetPosition(PRUint32 *aPosition)
3707 : {
3708 0 : WarnAboutLSProgressEvent(nsIDocument::ePosition);
3709 : // XXX can we change the iface?
3710 0 : LL_L2UI(*aPosition, mCurProgress);
3711 0 : return NS_OK;
3712 : }
3713 :
3714 0 : NS_IMETHODIMP nsXMLHttpProgressEvent::GetTotalSize(PRUint32 *aTotalSize)
3715 : {
3716 0 : WarnAboutLSProgressEvent(nsIDocument::eTotalSize);
3717 : // XXX can we change the iface?
3718 0 : LL_L2UI(*aTotalSize, mMaxProgress);
3719 0 : return NS_OK;
3720 4392 : }
|