Thu, 15 Jan 2015 21:03:48 +0100
Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)
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/. */
7 #include "nsXMLHttpRequest.h"
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"
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"
73 #include "nsWrapperCacheInlines.h"
75 using namespace mozilla;
76 using namespace mozilla::dom;
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)
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"
96 // CIDs
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
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)
126 #define NS_BADCERTHANDLER_CONTRACTID \
127 "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
129 #define NS_PROGRESS_EVENT_INTERVAL 50
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 }
141 NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
143 class nsResumeTimeoutsEvent : public nsRunnable
144 {
145 public:
146 nsResumeTimeoutsEvent(nsPIDOMWindow* aWindow) : mWindow(aWindow) {}
148 NS_IMETHOD Run()
149 {
150 mWindow->ResumeTimeouts(false);
151 return NS_OK;
152 }
154 private:
155 nsCOMPtr<nsPIDOMWindow> mWindow;
156 };
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 }
169 //-----------------------------------------------------------------------------
170 // XMLHttpRequestAuthPrompt
171 //-----------------------------------------------------------------------------
173 class XMLHttpRequestAuthPrompt : public nsIAuthPrompt
174 {
175 public:
176 NS_DECL_ISUPPORTS
177 NS_DECL_NSIAUTHPROMPT
179 XMLHttpRequestAuthPrompt();
180 virtual ~XMLHttpRequestAuthPrompt();
181 };
183 NS_IMPL_ISUPPORTS(XMLHttpRequestAuthPrompt, nsIAuthPrompt)
185 XMLHttpRequestAuthPrompt::XMLHttpRequestAuthPrompt()
186 {
187 MOZ_COUNT_CTOR(XMLHttpRequestAuthPrompt);
188 }
190 XMLHttpRequestAuthPrompt::~XMLHttpRequestAuthPrompt()
191 {
192 MOZ_COUNT_DTOR(XMLHttpRequestAuthPrompt);
193 }
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 }
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 }
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 }
233 /////////////////////////////////////////////
235 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
237 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXHREventTarget,
238 DOMEventTargetHelper)
239 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
241 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXHREventTarget,
242 DOMEventTargetHelper)
243 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
245 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXHREventTarget)
246 NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
247 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
249 NS_IMPL_ADDREF_INHERITED(nsXHREventTarget, DOMEventTargetHelper)
250 NS_IMPL_RELEASE_INHERITED(nsXHREventTarget, DOMEventTargetHelper)
252 void
253 nsXHREventTarget::DisconnectFromOwner()
254 {
255 DOMEventTargetHelper::DisconnectFromOwner();
256 }
258 /////////////////////////////////////////////
260 NS_INTERFACE_MAP_BEGIN(nsXMLHttpRequestUpload)
261 NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequestUpload)
262 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
264 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
265 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequestUpload, nsXHREventTarget)
267 /* virtual */ JSObject*
268 nsXMLHttpRequestUpload::WrapObject(JSContext* aCx)
269 {
270 return XMLHttpRequestUploadBinding::Wrap(aCx, this);
271 }
273 /////////////////////////////////////////////
274 //
275 //
276 /////////////////////////////////////////////
278 bool
279 nsXMLHttpRequest::sDontWarnAboutSyncXHR = false;
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 }
307 nsXMLHttpRequest::~nsXMLHttpRequest()
308 {
309 mState |= XML_HTTP_REQUEST_DELETED;
311 if (mState & (XML_HTTP_REQUEST_SENT |
312 XML_HTTP_REQUEST_LOADING)) {
313 Abort();
314 }
316 NS_ABORT_IF_FALSE(!(mState & XML_HTTP_REQUEST_SYNCLOOPING), "we rather crash than hang");
317 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
319 mResultJSON = JSVAL_VOID;
320 mResultArrayBuffer = nullptr;
321 mozilla::DropJSObjects(this);
322 }
324 void
325 nsXMLHttpRequest::RootJSResultObjects()
326 {
327 mozilla::HoldJSObjects(this);
328 }
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);
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 }
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);
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 }
372 Construct(aPrincipal, aGlobalObject, aBaseURI);
373 return NS_OK;
374 }
376 void
377 nsXMLHttpRequest::InitParameters(bool aAnon, bool aSystem)
378 {
379 if (!aAnon && !aSystem) {
380 return;
381 }
383 // Check for permissions.
384 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(GetOwner());
385 if (!window || !window->GetDocShell()) {
386 return;
387 }
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 }
397 nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
398 nsCOMPtr<nsIPermissionManager> permMgr =
399 do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
400 if (!permMgr)
401 return;
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 }
411 SetParameters(aAnon, aSystem);
412 }
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 }
430 void
431 nsXMLHttpRequest::SetRequestObserver(nsIRequestObserver* aObserver)
432 {
433 mRequestObserver = aObserver;
434 }
436 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequest)
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
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
457 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsXMLHttpRequest)
458 return tmp->IsBlack();
459 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
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)
468 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
470 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
471 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
473 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
474 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
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)
486 NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
488 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
489 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
491 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
492 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
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
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)
514 NS_IMPL_ADDREF_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
515 NS_IMPL_RELEASE_INHERITED(nsXMLHttpRequest, nsXHREventTarget)
517 NS_IMPL_EVENT_HANDLER(nsXMLHttpRequest, readystatechange)
519 void
520 nsXMLHttpRequest::DisconnectFromOwner()
521 {
522 nsXHREventTarget::DisconnectFromOwner();
523 Abort();
524 }
526 size_t
527 nsXMLHttpRequest::SizeOfEventTargetIncludingThis(
528 MallocSizeOf aMallocSizeOf) const
529 {
530 size_t n = aMallocSizeOf(this);
531 n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
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);
546 return n;
548 // Measurement of the following members may be added later if DMD finds it is
549 // worthwhile:
550 // - lots
551 }
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);
560 return NS_OK;
561 }
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 }
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 }
585 if (!responseXML) {
586 *aResponseXML = nullptr;
587 return NS_OK;
588 }
590 return CallQueryInterface(responseXML, aResponseXML);
591 }
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 }
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;
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 }
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 }
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 }
641 mDecoder = EncodingUtils::DecoderForEncoding(mResponseCharset);
643 return NS_OK;
644 }
646 nsresult
647 nsXMLHttpRequest::AppendToResponseText(const char * aSrcBuffer,
648 uint32_t aSrcBufferLen)
649 {
650 NS_ENSURE_STATE(mDecoder);
652 int32_t destBufferLen;
653 nsresult rv = mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen,
654 &destBufferLen);
655 NS_ENSURE_SUCCESS(rv, rv);
657 if (!mResponseText.SetCapacity(mResponseText.Length() + destBufferLen, fallible_t())) {
658 return NS_ERROR_OUT_OF_MEMORY;
659 }
661 char16_t* destBuffer = mResponseText.BeginWriting() + mResponseText.Length();
663 int32_t totalChars = mResponseText.Length();
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));
675 totalChars += destlen;
677 mResponseText.SetLength(totalChars);
679 return NS_OK;
680 }
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 }
693 void
694 nsXMLHttpRequest::GetResponseText(nsString& aResponseText, ErrorResult& aRv)
695 {
696 aResponseText.Truncate();
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 }
705 if (mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT &&
706 !mInLoadProgressEvent) {
707 aResponseText.SetIsVoid(true);
708 return;
709 }
711 if (!(mState & (XML_HTTP_REQUEST_DONE | XML_HTTP_REQUEST_LOADING))) {
712 return;
713 }
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 }
724 if (mResponseCharset != mResponseXML->GetDocumentCharacterSet()) {
725 mResponseCharset = mResponseXML->GetDocumentCharacterSet();
726 mResponseText.Truncate();
727 mResponseBodyDecodedPos = 0;
728 mDecoder = EncodingUtils::DecoderForEncoding(mResponseCharset);
729 }
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 }
739 mResponseBodyDecodedPos = mResponseBody.Length();
741 if (mState & XML_HTTP_REQUEST_DONE) {
742 // Free memory buffer which we no longer need
743 mResponseBody.Truncate();
744 mResponseBodyDecodedPos = 0;
745 }
747 aResponseText = mResponseText;
748 }
750 nsresult
751 nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx)
752 {
753 if (!aCx) {
754 return NS_ERROR_FAILURE;
755 }
756 RootJSResultObjects();
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 }
766 mResultJSON = value;
767 return NS_OK;
768 }
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 }
783 // mBlobSet can be null if the request has been canceled
784 if (!mBlobSet) {
785 return;
786 }
788 nsAutoCString contentType;
789 if (mLoadTotal == mLoadTransferred) {
790 mChannel->GetContentType(contentType);
791 }
793 mResponseBlob = mBlobSet->GetBlobInternal(contentType);
794 }
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 }
831 return NS_OK;
832 }
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")
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
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 }
883 ErrorResult rv;
884 SetResponseType(responseType, rv);
885 return rv.ErrorCode();
886 }
888 void
889 nsXMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aType,
890 ErrorResult& aRv)
891 {
892 SetResponseType(ResponseTypeEnum(static_cast<int>(aType)), aRv);
893 }
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 }
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 }
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 }
922 // Set the responseType attribute's value to the given value.
923 mResponseType = aResponseType;
925 }
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 }
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 }
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 }
968 if (!mResultArrayBuffer) {
969 RootJSResultObjects();
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 }
990 if (!mResponseBlob) {
991 CreatePartialBlob();
992 }
993 }
995 if (!mResponseBlob) {
996 aResponse.setNull();
997 return;
998 }
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 }
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 }
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 }
1040 aResponse.setNull();
1041 }
1043 /* readonly attribute unsigned long status; */
1044 NS_IMETHODIMP
1045 nsXMLHttpRequest::GetStatus(uint32_t *aStatus)
1046 {
1047 *aStatus = Status();
1048 return NS_OK;
1049 }
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 }
1066 uint16_t readyState;
1067 GetReadyState(&readyState);
1068 if (readyState == UNSENT || readyState == OPENED) {
1069 return 0;
1070 }
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);
1079 if (status == NS_ERROR_FILE_NOT_FOUND) {
1080 return 404; // Not Found
1081 } else {
1082 return 500; // Internal Error
1083 }
1084 }
1086 return 0;
1087 }
1089 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1090 if (!httpChannel) {
1092 // Let's simulate the http protocol for jar/app requests:
1093 nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
1094 if (jarChannel) {
1095 return 200; // Ok
1096 }
1098 return 0;
1099 }
1101 uint32_t status;
1102 nsresult rv = httpChannel->GetResponseStatus(&status);
1103 if (NS_FAILED(rv)) {
1104 status = 0;
1105 }
1107 return status;
1108 }
1110 IMPL_CSTRING_GETTER(GetStatusText)
1111 void
1112 nsXMLHttpRequest::GetStatusText(nsCString& aStatusText)
1113 {
1114 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1116 aStatusText.Truncate();
1118 if (!httpChannel) {
1119 return;
1120 }
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 }
1134 httpChannel->GetResponseStatusText(aStatusText);
1136 }
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;
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 }
1161 if (!(mState & (XML_HTTP_REQUEST_UNSENT |
1162 XML_HTTP_REQUEST_OPENED |
1163 XML_HTTP_REQUEST_DONE))) {
1164 ChangeState(XML_HTTP_REQUEST_DONE, true);
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 }
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 }
1184 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
1185 }
1187 /* void abort (); */
1188 void
1189 nsXMLHttpRequest::Abort()
1190 {
1191 CloseRequestWithError(NS_LITERAL_STRING(ABORT_STR), XML_HTTP_REQUEST_ABORTED);
1192 }
1194 NS_IMETHODIMP
1195 nsXMLHttpRequest::SlowAbort()
1196 {
1197 Abort();
1198 return NS_OK;
1199 }
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 }
1259 /* ByteString getAllResponseHeaders(); */
1260 IMPL_CSTRING_GETTER(GetAllResponseHeaders)
1261 void
1262 nsXMLHttpRequest::GetAllResponseHeaders(nsCString& aResponseHeaders)
1263 {
1264 aResponseHeaders.Truncate();
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 }
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 }
1281 if (!mChannel) {
1282 return;
1283 }
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 }
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 }
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 }
1314 void
1315 nsXMLHttpRequest::GetResponseHeader(const nsACString& header,
1316 nsACString& _retval, ErrorResult& aRv)
1317 {
1318 _retval.SetIsVoid(true);
1320 nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
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 }
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 }
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 }
1348 nsCString value;
1349 if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
1350 !value.IsEmpty()) {
1351 _retval.Append(";charset=");
1352 _retval.Append(value);
1353 }
1354 }
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 }
1364 return;
1365 }
1367 // Check for dangerous headers
1368 if (!IsSafeHeader(header, httpChannel)) {
1369 return;
1370 }
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 }
1380 already_AddRefed<nsILoadGroup>
1381 nsXMLHttpRequest::GetLoadGroup() const
1382 {
1383 if (mState & XML_HTTP_REQUEST_BACKGROUND) {
1384 return nullptr;
1385 }
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 }
1396 return nullptr;
1397 }
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 }
1409 (*aDOMEvent)->InitEvent(NS_LITERAL_STRING(READYSTATE_STR),
1410 false, false);
1412 // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1413 (*aDOMEvent)->SetTrusted(true);
1415 return NS_OK;
1416 }
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");
1427 if (NS_FAILED(CheckInnerWindowCorrectness()) ||
1428 (!AllowUploadProgress() && aTarget == mUpload)) {
1429 return;
1430 }
1432 bool dispatchLoadend = aType.EqualsLiteral(LOAD_STR) ||
1433 aType.EqualsLiteral(ERROR_STR) ||
1434 aType.EqualsLiteral(TIMEOUT_STR) ||
1435 aType.EqualsLiteral(ABORT_STR);
1437 nsCOMPtr<nsIDOMEvent> event;
1438 nsresult rv = NS_NewDOMProgressEvent(getter_AddRefs(event), this,
1439 nullptr, nullptr);
1440 if (NS_FAILED(rv)) {
1441 return;
1442 }
1444 nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
1445 if (!progress) {
1446 return;
1447 }
1449 progress->InitProgressEvent(aType, false, false, aLengthComputable,
1450 aLoaded, (aTotal == UINT64_MAX) ? 0 : aTotal);
1452 event->SetTrusted(true);
1454 aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
1456 if (dispatchLoadend) {
1457 DispatchProgressEvent(aTarget, NS_LITERAL_STRING(LOADEND_STR),
1458 aLengthComputable, aLoaded, aTotal);
1459 }
1460 }
1462 already_AddRefed<nsIHttpChannel>
1463 nsXMLHttpRequest::GetCurrentHttpChannel()
1464 {
1465 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
1466 return httpChannel.forget();
1467 }
1469 already_AddRefed<nsIJARChannel>
1470 nsXMLHttpRequest::GetCurrentJARChannel()
1471 {
1472 nsCOMPtr<nsIJARChannel> appChannel = do_QueryInterface(mChannel);
1473 return appChannel.forget();
1474 }
1476 bool
1477 nsXMLHttpRequest::IsSystemXHR()
1478 {
1479 return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
1480 }
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 }
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 }
1505 // This is a cross-site request
1506 mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
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);
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 }
1522 return NS_OK;
1523 }
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 }
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());
1552 if (!async && !DontWarnAboutSyncXHR() && GetOwner() &&
1553 GetOwner()->GetExtantDoc()) {
1554 GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
1555 }
1557 Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC,
1558 async ? 0 : 1);
1560 NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
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 }
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 }
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 }
1607 nsresult rv;
1608 nsCOMPtr<nsIURI> uri;
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();
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 }
1624 // Unset any pre-existing aborted and timed-out states.
1625 mState &= ~XML_HTTP_REQUEST_ABORTED & ~XML_HTTP_REQUEST_TIMED_OUT;
1627 if (async) {
1628 mState |= XML_HTTP_REQUEST_ASYNC;
1629 } else {
1630 mState &= ~XML_HTTP_REQUEST_ASYNC;
1631 }
1633 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
1634 NS_ENSURE_SUCCESS(rv, rv);
1635 nsCOMPtr<nsIDocument> doc =
1636 nsContentUtils::GetDocumentFromScriptContext(sc);
1638 nsCOMPtr<nsIURI> baseURI;
1639 if (mBaseURI) {
1640 baseURI = mBaseURI;
1641 }
1642 else if (doc) {
1643 baseURI = doc->GetBaseURI();
1644 }
1646 rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, baseURI);
1647 if (NS_FAILED(rv)) return rv;
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 }
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 }
1679 // Clear our record of previously set headers so future header set
1680 // operations will merge/override correctly.
1681 mAlreadySetHeaders.Clear();
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();
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;
1707 mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
1708 XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
1710 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
1711 if (httpChannel) {
1712 rv = httpChannel->SetRequestMethod(method);
1713 NS_ENSURE_SUCCESS(rv, rv);
1715 // Set the initiator type
1716 nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
1717 if (timedChannel) {
1718 timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
1719 }
1720 }
1722 ChangeState(XML_HTTP_REQUEST_OPENED);
1724 return rv;
1725 }
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 }
1744 nsresult rv = NS_OK;
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));
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 }
1784 if (xmlHttpRequest->mState & XML_HTTP_REQUEST_PARSEBODY) {
1785 // Give the same data to the parser.
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);
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);
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 }
1808 if (NS_SUCCEEDED(rv)) {
1809 *writeCount = count;
1810 } else {
1811 *writeCount = 0;
1812 }
1814 return rv;
1815 }
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 }
1825 if (!file)
1826 return false;
1828 nsAutoCString contentType;
1829 mChannel->GetContentType(contentType);
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 }
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);
1847 NS_ABORT_IF_FALSE(mContext.get() == ctxt,"start context different from OnDataAvailable context");
1849 mProgressSinceLastProgressEvent = true;
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 }
1859 uint32_t totalRead;
1860 nsresult rv = inStr->ReadSegments(nsXMLHttpRequest::StreamReaderFunc,
1861 (void*)this, count, &totalRead);
1862 NS_ENSURE_SUCCESS(rv, rv);
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 }
1871 mLoadTransferred += totalRead;
1873 ChangeState(XML_HTTP_REQUEST_LOADING);
1875 MaybeDispatchProgressEvents(false);
1877 return NS_OK;
1878 }
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 }
1891 if (request != mChannel) {
1892 // Can this still happen?
1893 return NS_OK;
1894 }
1896 // Don't do anything if we have been aborted
1897 if (mState & XML_HTTP_REQUEST_UNSENT)
1898 return NS_OK;
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!");
1905 return NS_ERROR_UNEXPECTED;
1906 }
1908 // Don't do anything if we have timed out.
1909 if (mState & XML_HTTP_REQUEST_TIMED_OUT) {
1910 return NS_OK;
1911 }
1913 nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
1914 NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
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 }
1929 channel->SetOwner(documentPrincipal);
1931 nsresult status;
1932 request->GetStatus(&status);
1933 mErrorLoad = mErrorLoad || NS_FAILED(status);
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 }
1952 mContext = ctxt;
1953 mState |= XML_HTTP_REQUEST_PARSEBODY;
1954 ChangeState(XML_HTTP_REQUEST_HEADERS_RECEIVED);
1956 ResetResponse();
1958 if (!mOverrideMimeType.IsEmpty()) {
1959 channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
1960 }
1962 DetectCharset();
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 }
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 }
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);
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 }
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;
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 }
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);
2052 if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
2053 mResponseXML->ForceEnableXULXBL();
2054 }
2056 if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
2057 nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
2058 if (htmlDoc) {
2059 htmlDoc->DisableCookieAccess();
2060 }
2061 }
2063 nsCOMPtr<nsIStreamListener> listener;
2064 nsCOMPtr<nsILoadGroup> loadGroup;
2065 channel->GetLoadGroup(getter_AddRefs(loadGroup));
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);
2072 mXMLParserStreamListener = listener;
2073 rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
2074 NS_ENSURE_SUCCESS(rv, rv);
2075 }
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 }
2085 return NS_OK;
2086 }
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 }
2098 mWaitingForOnStopRequest = false;
2100 if (mRequestObserver) {
2101 NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
2102 mFirstStartRequestSeen = false;
2103 mRequestObserver->OnStopRequest(request, ctxt, status);
2104 }
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 }
2116 // Is this good enough here?
2117 if (mState & XML_HTTP_REQUEST_PARSEBODY && mXMLParserStreamListener) {
2118 mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
2119 }
2121 mXMLParserStreamListener = nullptr;
2122 mContext = nullptr;
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 }
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 }
2166 nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
2167 NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
2169 channel->SetNotificationCallbacks(nullptr);
2170 mNotificationCallbacks = nullptr;
2171 mChannelEventSink = nullptr;
2172 mProgressEventSink = nullptr;
2174 mState &= ~XML_HTTP_REQUEST_SYNCLOOPING;
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.
2180 mErrorLoad = true;
2181 mResponseXML = nullptr;
2182 }
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 }
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 }
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 }
2227 ChangeState(XML_HTTP_REQUEST_DONE, true);
2228 if (mTimeoutTimer) {
2229 mTimeoutTimer->Cancel();
2230 }
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 }
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 }
2254 NS_IMETHODIMP
2255 nsXMLHttpRequest::SendAsBinary(const nsAString &aBody)
2256 {
2257 ErrorResult rv;
2258 SendAsBinary(aBody, rv);
2259 return rv.ErrorCode();
2260 }
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 }
2272 if (GetOwner() && GetOwner()->GetExtantDoc()) {
2273 GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSendAsBinary);
2274 }
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';
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 }
2298 nsCOMPtr<nsIWritableVariant> variant = new nsVariant();
2300 aRv = variant->SetAsISupports(stream);
2301 if (aRv.Failed()) {
2302 return;
2303 }
2305 aRv = Send(variant);
2306 }
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 }
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);
2330 nsCOMPtr<nsIStorageStream> storStream;
2331 rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream));
2332 NS_ENSURE_SUCCESS(rv, rv);
2334 nsCOMPtr<nsIOutputStream> output;
2335 rv = storStream->GetOutputStream(0, getter_AddRefs(output));
2336 NS_ENSURE_SUCCESS(rv, rv);
2338 // Make sure to use the encoding we'll send
2339 rv = serializer->SerializeToStream(aDoc, output, aCharset);
2340 NS_ENSURE_SUCCESS(rv, rv);
2342 output->Close();
2344 uint32_t length;
2345 rv = storStream->GetLength(&length);
2346 NS_ENSURE_SUCCESS(rv, rv);
2347 *aContentLength = length;
2349 return storStream->NewInputStream(0, aResult);
2350 }
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");
2360 nsCString converted = NS_ConvertUTF16toUTF8(aString);
2361 *aContentLength = converted.Length();
2362 return NS_NewCStringInputStream(aResult, converted);
2363 }
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();
2373 nsresult rv = aStream->Available(aContentLength);
2374 NS_ENSURE_SUCCESS(rv, rv);
2376 NS_ADDREF(*aResult = aStream);
2378 return NS_OK;
2379 }
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 }
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();
2397 *aContentLength = aDataLength;
2398 const char* data = reinterpret_cast<const char*>(aData);
2400 nsCOMPtr<nsIInputStream> stream;
2401 nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), data, aDataLength,
2402 NS_ASSIGNMENT_COPY);
2403 NS_ENSURE_SUCCESS(rv, rv);
2405 stream.forget(aResult);
2407 return NS_OK;
2408 }
2410 static nsresult
2411 GetRequestBody(nsIVariant* aBody, nsIInputStream** aResult, uint64_t* aContentLength,
2412 nsACString& aContentType, nsACString& aCharset)
2413 {
2414 *aResult = nullptr;
2416 uint16_t dataType;
2417 nsresult rv = aBody->GetDataType(&dataType);
2418 NS_ENSURE_SUCCESS(rv, rv);
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);
2427 nsMemory::Free(iid);
2429 // document?
2430 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(supports);
2431 if (doc) {
2432 return GetRequestBody(doc, aResult, aContentLength, aContentType, aCharset);
2433 }
2435 // nsISupportsString?
2436 nsCOMPtr<nsISupportsString> wstr = do_QueryInterface(supports);
2437 if (wstr) {
2438 nsAutoString string;
2439 wstr->GetData(string);
2441 return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
2442 }
2444 // nsIInputStream?
2445 nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
2446 if (stream) {
2447 return GetRequestBody(stream, aResult, aContentLength, aContentType, aCharset);
2448 }
2450 // nsIXHRSendable?
2451 nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
2452 if (sendable) {
2453 return GetRequestBody(sendable, aResult, aContentLength, aContentType, aCharset);
2454 }
2456 // ArrayBuffer?
2457 AutoSafeJSContext cx;
2458 JS::Rooted<JS::Value> realVal(cx);
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;
2478 return NS_OK;
2479 }
2481 char16_t* data = nullptr;
2482 uint32_t len = 0;
2483 rv = aBody->GetAsWStringWithSize(&len, &data);
2484 NS_ENSURE_SUCCESS(rv, rv);
2486 nsString string;
2487 string.Adopt(data, len);
2489 return GetRequestBody(string, aResult, aContentLength, aContentType, aCharset);
2490 }
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 }
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);
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 }
2556 NS_NOTREACHED("Default cases exist for a reason");
2557 return NS_OK;
2558 }
2560 /* void send (in nsIVariant aBody); */
2561 NS_IMETHODIMP
2562 nsXMLHttpRequest::Send(nsIVariant *aBody)
2563 {
2564 return Send(aBody, Nullable<RequestBody>());
2565 }
2567 nsresult
2568 nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
2569 {
2570 NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
2572 nsresult rv = CheckInnerWindowCorrectness();
2573 NS_ENSURE_SUCCESS(rv, rv);
2575 // Return error if we're already processing a request
2576 if (XML_HTTP_REQUEST_SENT & mState) {
2577 return NS_ERROR_FAILURE;
2578 }
2580 // Make sure we've been opened
2581 if (!mChannel || !(XML_HTTP_REQUEST_OPENED & mState)) {
2582 return NS_ERROR_NOT_INITIALIZED;
2583 }
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 }
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.
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));
2608 if (httpChannel) {
2609 httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
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.
2624 nsCOMPtr<nsIURI> principalURI;
2625 mPrincipal->GetURI(getter_AddRefs(principalURI));
2627 nsIScriptContext* sc = GetContextForEventHandlers(&rv);
2628 NS_ENSURE_SUCCESS(rv, rv);
2629 nsCOMPtr<nsIDocument> doc =
2630 nsContentUtils::GetDocumentFromScriptContext(sc);
2632 nsCOMPtr<nsIURI> docCurURI;
2633 nsCOMPtr<nsIURI> docOrigURI;
2634 if (doc) {
2635 docCurURI = doc->GetDocumentURI();
2636 docOrigURI = doc->GetOriginalURI();
2637 }
2639 nsCOMPtr<nsIURI> referrerURI;
2641 if (principalURI && docCurURI && docOrigURI) {
2642 bool equal = false;
2643 principalURI->Equals(docOrigURI, &equal);
2644 if (equal) {
2645 referrerURI = docCurURI;
2646 }
2647 }
2649 if (!referrerURI)
2650 referrerURI = principalURI;
2652 httpChannel->SetReferrer(referrerURI);
2653 }
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 }
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")) {
2686 nsAutoCString charset;
2687 nsAutoCString defaultContentType;
2688 nsCOMPtr<nsIInputStream> postDataStream;
2690 rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream),
2691 &mUploadTotal, defaultContentType, charset);
2692 NS_ENSURE_SUCCESS(rv, rv);
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 }
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 }
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 }
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);
2750 postDataStream = bufferedStream;
2751 }
2753 mUploadComplete = false;
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 }
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);
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 }
2796 ResetResponse();
2798 rv = CheckChannelForCrossSiteRequest(mChannel);
2799 NS_ENSURE_SUCCESS(rv, rv);
2801 bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
2803 // Hook us up to listen to redirects and the like
2804 mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
2805 mChannel->SetNotificationCallbacks(this);
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);
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);
2819 // Disable Necko-internal response timeouts.
2820 internalHttpChannel->SetResponseTimeoutEnabled(false);
2821 }
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.
2838 listener = new nsStreamListenerWrapper(listener);
2839 }
2841 if (mIsAnon) {
2842 AddLoadFlags(mChannel, nsIRequest::LOAD_ANONYMOUS);
2843 }
2844 else {
2845 AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
2846 }
2848 NS_ASSERTION(listener != this,
2849 "Using an object as a listener that can't be exposed to JS");
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 }
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 }
2878 // We're about to send the request. Start our timeout.
2879 mRequestSentTime = PR_Now();
2880 StartTimeoutTimer();
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.
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 }
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 }
2905 // Either AsyncOpen was called, or CORS will open the channel later.
2906 mWaitingForOnStopRequest = true;
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;
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 }
2930 ChangeState(XML_HTTP_REQUEST_SENT);
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 }
2945 if (suspendedDoc) {
2946 suspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents,
2947 true);
2948 }
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 }
2970 if (!mChannel) {
2971 return NS_ERROR_FAILURE;
2972 }
2974 return rv;
2975 }
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.");
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 }
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);
3003 if (pending) {
3004 return NS_ERROR_IN_PROGRESS;
3005 }
3006 }
3008 if (!mChannel) // open() initializes mChannel, and open()
3009 return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
3011 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
3012 if (!httpChannel) {
3013 return NS_OK;
3014 }
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;
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 }
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 }
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 }
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 }
3083 if (!mAlreadySetHeaders.Contains(header)) {
3084 // Case 2 above
3085 mergeHeaders = false;
3086 }
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));
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 }
3106 /* attribute unsigned long timeout; */
3107 NS_IMETHODIMP
3108 nsXMLHttpRequest::GetTimeout(uint32_t *aTimeout)
3109 {
3110 *aTimeout = Timeout();
3111 return NS_OK;
3112 }
3114 NS_IMETHODIMP
3115 nsXMLHttpRequest::SetTimeout(uint32_t aTimeout)
3116 {
3117 ErrorResult rv;
3118 SetTimeout(aTimeout, rv);
3119 return rv.ErrorCode();
3120 }
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 }
3134 mTimeoutMilliseconds = aTimeout;
3135 if (mRequestSentTime) {
3136 StartTimeoutTimer();
3137 }
3138 }
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 }
3150 if (mTimeoutTimer) {
3151 mTimeoutTimer->Cancel();
3152 }
3154 if (!mTimeoutMilliseconds) {
3155 return;
3156 }
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 }
3170 /* readonly attribute unsigned short readyState; */
3171 NS_IMETHODIMP
3172 nsXMLHttpRequest::GetReadyState(uint16_t *aState)
3173 {
3174 *aState = ReadyState();
3175 return NS_OK;
3176 }
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 }
3198 /* void overrideMimeType(in DOMString mimetype); */
3199 NS_IMETHODIMP
3200 nsXMLHttpRequest::SlowOverrideMimeType(const nsAString& aMimeType)
3201 {
3202 OverrideMimeType(aMimeType);
3203 return NS_OK;
3204 }
3206 /* attribute boolean mozBackgroundRequest; */
3207 NS_IMETHODIMP
3208 nsXMLHttpRequest::GetMozBackgroundRequest(bool *_retval)
3209 {
3210 *_retval = MozBackgroundRequest();
3211 return NS_OK;
3212 }
3214 bool
3215 nsXMLHttpRequest::MozBackgroundRequest()
3216 {
3217 return !!(mState & XML_HTTP_REQUEST_BACKGROUND);
3218 }
3220 NS_IMETHODIMP
3221 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest)
3222 {
3223 nsresult rv = NS_OK;
3224 SetMozBackgroundRequest(aMozBackgroundRequest, rv);
3225 return rv;
3226 }
3228 void
3229 nsXMLHttpRequest::SetMozBackgroundRequest(bool aMozBackgroundRequest, nsresult& aRv)
3230 {
3231 if (!IsSystemXHR()) {
3232 aRv = NS_ERROR_DOM_SECURITY_ERR;
3233 return;
3234 }
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 }
3242 if (aMozBackgroundRequest) {
3243 mState |= XML_HTTP_REQUEST_BACKGROUND;
3244 } else {
3245 mState &= ~XML_HTTP_REQUEST_BACKGROUND;
3246 }
3247 }
3249 /* attribute boolean withCredentials; */
3250 NS_IMETHODIMP
3251 nsXMLHttpRequest::GetWithCredentials(bool *_retval)
3252 {
3253 *_retval = WithCredentials();
3254 return NS_OK;
3255 }
3257 bool
3258 nsXMLHttpRequest::WithCredentials()
3259 {
3260 return !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);
3261 }
3263 NS_IMETHODIMP
3264 nsXMLHttpRequest::SetWithCredentials(bool aWithCredentials)
3265 {
3266 ErrorResult rv;
3267 SetWithCredentials(aWithCredentials, rv);
3268 return rv.ErrorCode();
3269 }
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 }
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 }
3288 if (aWithCredentials) {
3289 mState |= XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
3290 } else {
3291 mState &= ~XML_HTTP_REQUEST_AC_WITH_CREDENTIALS;
3292 }
3293 }
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;
3306 if (mProgressNotifier &&
3307 !(aState & (XML_HTTP_REQUEST_HEADERS_RECEIVED | XML_HTTP_REQUEST_LOADING))) {
3308 mProgressTimerIsActive = false;
3309 mProgressNotifier->Cancel();
3310 }
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);
3322 DispatchDOMEvent(nullptr, event, nullptr, nullptr);
3323 }
3325 return rv;
3326 }
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 }
3340 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
3341 NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)
3343 // nsIAsyncVerifyRedirectCallback implementation
3344 NS_IMETHOD OnRedirectVerifyCallback(nsresult result)
3345 {
3346 mXHR->OnRedirectVerifyCallback(result);
3348 return NS_OK;
3349 }
3351 private:
3352 nsRefPtr<nsXMLHttpRequest> mXHR;
3353 };
3355 NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder, mXHR)
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
3362 NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
3363 NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)
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?");
3377 nsresult rv;
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 }
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 }
3395 // Prepare to receive callback
3396 mRedirectCallback = callback;
3397 mNewRedirectChannel = aNewChannel;
3399 if (mChannelEventSink) {
3400 nsRefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
3401 new AsyncVerifyRedirectCallbackForwarder(this);
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 }
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");
3422 if (NS_SUCCEEDED(result)) {
3423 mChannel = mNewRedirectChannel;
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 }
3439 mNewRedirectChannel = nullptr;
3441 mRedirectCallback->OnRedirectVerifyCallback(result);
3442 mRedirectCallback = nullptr;
3443 }
3445 /////////////////////////////////////////////////////
3446 // nsIProgressEventSink methods:
3447 //
3449 void
3450 nsXMLHttpRequest::MaybeDispatchProgressEvents(bool aFinalProgress)
3451 {
3452 if (aFinalProgress && mProgressTimerIsActive) {
3453 mProgressTimerIsActive = false;
3454 mProgressNotifier->Cancel();
3455 }
3457 if (mProgressTimerIsActive ||
3458 !mProgressSinceLastProgressEvent ||
3459 mErrorLoad ||
3460 !(mState & XML_HTTP_REQUEST_ASYNC)) {
3461 return;
3462 }
3464 if (!aFinalProgress) {
3465 StartProgressEventTimer();
3466 }
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 }
3494 mProgressSinceLastProgressEvent = false;
3495 }
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;
3518 MaybeDispatchProgressEvents(false);
3519 } else {
3520 mLoadLengthComputable = lengthComputable;
3521 mLoadTotal = lengthComputable ? aProgressMax : 0;
3523 // Don't dispatch progress events here. OnDataAvailable will take care
3524 // of that.
3525 }
3527 if (mProgressEventSink) {
3528 mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
3529 aProgressMax);
3530 }
3532 return NS_OK;
3533 }
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 }
3542 return NS_OK;
3543 }
3545 bool
3546 nsXMLHttpRequest::AllowUploadProgress()
3547 {
3548 return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
3549 (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
3550 }
3552 /////////////////////////////////////////////////////
3553 // nsIInterfaceRequestor methods:
3554 //
3555 NS_IMETHODIMP
3556 nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
3557 {
3558 nsresult rv;
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 }
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 }
3584 if (mState & XML_HTTP_REQUEST_BACKGROUND) {
3585 nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
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))) {
3598 nsCOMPtr<nsIURI> uri;
3599 rv = mChannel->GetURI(getter_AddRefs(uri));
3600 NS_ENSURE_SUCCESS(rv, rv);
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;
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 */
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 }
3625 // ... request username is null, and request password is null,
3626 if (showPrompt) {
3628 nsCString username;
3629 rv = uri->GetUsername(username);
3630 NS_ENSURE_SUCCESS(rv, rv);
3632 nsCString password;
3633 rv = uri->GetPassword(password);
3634 NS_ENSURE_SUCCESS(rv, rv);
3636 if (!username.IsEmpty() || !password.IsEmpty()) {
3637 showPrompt = false;
3638 }
3639 }
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;
3647 return prompt->QueryInterface(aIID, aResult);
3648 }
3650 nsCOMPtr<nsIPromptFactory> wwatch =
3651 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
3652 NS_ENSURE_SUCCESS(rv, rv);
3654 // Get the an auth prompter for our window so that the parenting
3655 // of the dialogs works as it should when using tabs.
3657 nsCOMPtr<nsIDOMWindow> window;
3658 if (GetOwner()) {
3659 window = GetOwner()->GetOuterWindow();
3660 }
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 }
3681 return QueryInterface(aIID, aResult);
3682 }
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 }
3692 nsXMLHttpRequestUpload*
3693 nsXMLHttpRequest::Upload()
3694 {
3695 if (!mUpload) {
3696 mUpload = new nsXMLHttpRequestUpload(this);
3697 }
3698 return mUpload;
3699 }
3701 NS_IMETHODIMP
3702 nsXMLHttpRequest::GetUpload(nsIXMLHttpRequestUpload** aUpload)
3703 {
3704 nsRefPtr<nsXMLHttpRequestUpload> upload = Upload();
3705 upload.forget(aUpload);
3706 return NS_OK;
3707 }
3709 bool
3710 nsXMLHttpRequest::MozAnon()
3711 {
3712 return mIsAnon;
3713 }
3715 NS_IMETHODIMP
3716 nsXMLHttpRequest::GetMozAnon(bool* aAnon)
3717 {
3718 *aAnon = MozAnon();
3719 return NS_OK;
3720 }
3722 bool
3723 nsXMLHttpRequest::MozSystem()
3724 {
3725 return IsSystemXHR();
3726 }
3728 NS_IMETHODIMP
3729 nsXMLHttpRequest::GetMozSystem(bool* aSystem)
3730 {
3731 *aSystem = MozSystem();
3732 return NS_OK;
3733 }
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 }
3744 CloseRequestWithError(NS_LITERAL_STRING(TIMEOUT_STR),
3745 XML_HTTP_REQUEST_TIMED_OUT);
3746 }
3748 NS_IMETHODIMP
3749 nsXMLHttpRequest::Notify(nsITimer* aTimer)
3750 {
3751 if (mProgressNotifier == aTimer) {
3752 HandleProgressTimerCallback();
3753 return NS_OK;
3754 }
3756 if (mTimeoutTimer == aTimer) {
3757 HandleTimeoutCallback();
3758 return NS_OK;
3759 }
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 }
3766 void
3767 nsXMLHttpRequest::HandleProgressTimerCallback()
3768 {
3769 mProgressTimerIsActive = false;
3770 MaybeDispatchProgressEvents(false);
3771 }
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 }
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 }
3797 NS_IMPL_ISUPPORTS(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
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 }
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
3822 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
3823 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
3825 // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
3826 // inheritance from nsISupports.
3827 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
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
3836 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
3837 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
3838 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
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 }
3852 return mXHR->GetInterface(aIID, aResult);
3853 }
3855 namespace mozilla {
3857 ArrayBufferBuilder::ArrayBufferBuilder()
3858 : mDataPtr(nullptr),
3859 mCapacity(0),
3860 mLength(0)
3861 {
3862 }
3864 ArrayBufferBuilder::~ArrayBufferBuilder()
3865 {
3866 reset();
3867 }
3869 void
3870 ArrayBufferBuilder::reset()
3871 {
3872 if (mDataPtr) {
3873 JS_free(nullptr, mDataPtr);
3874 }
3875 mDataPtr = nullptr;
3876 mCapacity = mLength = 0;
3877 }
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 }
3887 mDataPtr = newdata;
3888 mCapacity = aNewCap;
3889 if (mLength > aNewCap) {
3890 mLength = aNewCap;
3891 }
3893 return true;
3894 }
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 }
3909 // But make sure there's always enough to satisfy our request.
3910 if (newcap < mLength + aDataLen) {
3911 newcap = mLength + aDataLen;
3912 }
3914 // Did we overflow?
3915 if (newcap < mCapacity) {
3916 return false;
3917 }
3919 if (!setCapacity(newcap)) {
3920 return false;
3921 }
3922 }
3924 // Assert that the region isn't overlapping so we can memcpy.
3925 MOZ_ASSERT(!areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength,
3926 aDataLen));
3928 memcpy(mDataPtr + mLength, aNewData, aDataLen);
3929 mLength += aDataLen;
3931 return true;
3932 }
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 }
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 }
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;
3964 const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
3965 const uint8_t* min_end = end1 < end2 ? end1 : end2;
3967 return max_start < min_end;
3968 }
3970 } // namespace mozilla