content/base/src/nsXMLHttpRequest.cpp

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

mercurial