Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "XMLHttpRequestMainThread.h"
8 :
9 : #include <algorithm>
10 : #ifndef XP_WIN
11 : #include <unistd.h>
12 : #endif
13 : #include "mozilla/ArrayUtils.h"
14 : #include "mozilla/CheckedInt.h"
15 : #include "mozilla/dom/BlobBinding.h"
16 : #include "mozilla/dom/BlobSet.h"
17 : #include "mozilla/dom/DocGroup.h"
18 : #include "mozilla/dom/DOMString.h"
19 : #include "mozilla/dom/File.h"
20 : #include "mozilla/dom/FileBinding.h"
21 : #include "mozilla/dom/FileCreatorHelper.h"
22 : #include "mozilla/dom/FetchUtil.h"
23 : #include "mozilla/dom/FormData.h"
24 : #include "mozilla/dom/MutableBlobStorage.h"
25 : #include "mozilla/dom/XMLDocument.h"
26 : #include "mozilla/dom/URLSearchParams.h"
27 : #include "mozilla/dom/PromiseNativeHandler.h"
28 : #include "mozilla/Encoding.h"
29 : #include "mozilla/EventDispatcher.h"
30 : #include "mozilla/EventListenerManager.h"
31 : #include "mozilla/EventStateManager.h"
32 : #include "mozilla/LoadInfo.h"
33 : #include "mozilla/LoadContext.h"
34 : #include "mozilla/MemoryReporting.h"
35 : #include "nsIDOMDocument.h"
36 : #include "mozilla/dom/ProgressEvent.h"
37 : #include "nsIJARChannel.h"
38 : #include "nsIJARURI.h"
39 : #include "nsLayoutCID.h"
40 : #include "nsReadableUtils.h"
41 :
42 : #include "nsIURI.h"
43 : #include "nsILoadGroup.h"
44 : #include "nsNetUtil.h"
45 : #include "nsStringStream.h"
46 : #include "nsIAuthPrompt.h"
47 : #include "nsIAuthPrompt2.h"
48 : #include "nsIOutputStream.h"
49 : #include "nsISupportsPrimitives.h"
50 : #include "nsISupportsPriority.h"
51 : #include "nsIInterfaceRequestorUtils.h"
52 : #include "nsStreamUtils.h"
53 : #include "nsThreadUtils.h"
54 : #include "nsIUploadChannel.h"
55 : #include "nsIUploadChannel2.h"
56 : #include "nsIDOMSerializer.h"
57 : #include "nsXPCOM.h"
58 : #include "nsIDOMEventListener.h"
59 : #include "nsIScriptSecurityManager.h"
60 : #include "nsIVariant.h"
61 : #include "nsVariant.h"
62 : #include "nsIScriptError.h"
63 : #include "nsIStreamConverterService.h"
64 : #include "nsICachingChannel.h"
65 : #include "nsContentUtils.h"
66 : #include "nsCycleCollectionParticipant.h"
67 : #include "nsError.h"
68 : #include "nsIHTMLDocument.h"
69 : #include "nsIStorageStream.h"
70 : #include "nsIPromptFactory.h"
71 : #include "nsIWindowWatcher.h"
72 : #include "nsIConsoleService.h"
73 : #include "nsIContentSecurityPolicy.h"
74 : #include "nsAsyncRedirectVerifyHelper.h"
75 : #include "nsStringBuffer.h"
76 : #include "nsIFileChannel.h"
77 : #include "mozilla/Telemetry.h"
78 : #include "jsfriendapi.h"
79 : #include "GeckoProfiler.h"
80 : #include "mozilla/dom/XMLHttpRequestBinding.h"
81 : #include "mozilla/Attributes.h"
82 : #include "MultipartBlobImpl.h"
83 : #include "nsIPermissionManager.h"
84 : #include "nsMimeTypes.h"
85 : #include "nsIHttpChannelInternal.h"
86 : #include "nsIClassOfService.h"
87 : #include "nsCharSeparatedTokenizer.h"
88 : #include "nsStreamListenerWrapper.h"
89 : #include "xpcjsid.h"
90 : #include "nsITimedChannel.h"
91 : #include "nsWrapperCacheInlines.h"
92 : #include "nsZipArchive.h"
93 : #include "mozilla/Preferences.h"
94 : #include "private/pprio.h"
95 : #include "XMLHttpRequestUpload.h"
96 :
97 : // Undefine the macro of CreateFile to avoid FileCreatorHelper#CreateFile being
98 : // replaced by FileCreatorHelper#CreateFileW.
99 : #ifdef CreateFile
100 : #undef CreateFile
101 : #endif
102 :
103 : using namespace mozilla::net;
104 :
105 : namespace mozilla {
106 : namespace dom {
107 :
108 : // Maximum size that we'll grow an ArrayBuffer instead of doubling,
109 : // once doubling reaches this threshold
110 : const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH = 32*1024*1024;
111 : // start at 32k to avoid lots of doubling right at the start
112 : const uint32_t XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE = 32*1024;
113 : // the maximum Content-Length that we'll preallocate. 1GB. Must fit
114 : // in an int32_t!
115 : const int32_t XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE = 1*1024*1024*1024LL;
116 :
117 : namespace {
118 3 : const nsLiteralString ProgressEventTypeStrings[] = {
119 : NS_LITERAL_STRING("loadstart"),
120 : NS_LITERAL_STRING("progress"),
121 : NS_LITERAL_STRING("error"),
122 : NS_LITERAL_STRING("abort"),
123 : NS_LITERAL_STRING("timeout"),
124 : NS_LITERAL_STRING("load"),
125 : NS_LITERAL_STRING("loadend")
126 3 : };
127 : static_assert(MOZ_ARRAY_LENGTH(ProgressEventTypeStrings) ==
128 : size_t(XMLHttpRequestMainThread::ProgressEventType::ENUM_MAX),
129 : "Mismatched lengths for ProgressEventTypeStrings and ProgressEventType enums");
130 :
131 3 : const nsString kLiteralString_readystatechange = NS_LITERAL_STRING("readystatechange");
132 3 : const nsString kLiteralString_xmlhttprequest = NS_LITERAL_STRING("xmlhttprequest");
133 3 : const nsString kLiteralString_DOMContentLoaded = NS_LITERAL_STRING("DOMContentLoaded");
134 : }
135 :
136 : // CIDs
137 : #define NS_BADCERTHANDLER_CONTRACTID \
138 : "@mozilla.org/content/xmlhttprequest-bad-cert-handler;1"
139 :
140 : #define NS_PROGRESS_EVENT_INTERVAL 50
141 : #define MAX_SYNC_TIMEOUT_WHEN_UNLOADING 10000 /* 10 secs */
142 :
143 0 : NS_IMPL_ISUPPORTS(nsXHRParseEndListener, nsIDOMEventListener)
144 :
145 0 : class nsResumeTimeoutsEvent : public Runnable
146 : {
147 : public:
148 0 : explicit nsResumeTimeoutsEvent(nsPIDOMWindowInner* aWindow)
149 0 : : Runnable("dom::nsResumeTimeoutsEvent")
150 0 : , mWindow(aWindow)
151 : {
152 0 : }
153 :
154 0 : NS_IMETHOD Run() override
155 : {
156 0 : mWindow->Resume();
157 0 : return NS_OK;
158 : }
159 :
160 : private:
161 : nsCOMPtr<nsPIDOMWindowInner> mWindow;
162 : };
163 :
164 :
165 : // This helper function adds the given load flags to the request's existing
166 : // load flags.
167 4 : static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
168 : {
169 : nsLoadFlags flags;
170 4 : request->GetLoadFlags(&flags);
171 4 : flags |= newFlags;
172 4 : request->SetLoadFlags(flags);
173 4 : }
174 :
175 : /////////////////////////////////////////////
176 : //
177 : //
178 : /////////////////////////////////////////////
179 :
180 : bool
181 : XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false;
182 :
183 3 : XMLHttpRequestMainThread::XMLHttpRequestMainThread()
184 : : mResponseBodyDecodedPos(0),
185 : mResponseCharset(nullptr),
186 : mResponseType(XMLHttpRequestResponseType::_empty),
187 : mRequestObserver(nullptr),
188 : mState(State::unsent),
189 : mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false),
190 : mFlagSyncLooping(false), mFlagBackgroundRequest(false),
191 : mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
192 : mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
193 : mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
194 : mProgressSinceLastProgressEvent(false),
195 : mRequestSentTime(0), mTimeoutMilliseconds(0),
196 : mErrorLoad(ErrorType::eOK), mErrorParsingXML(false),
197 : mWaitingForOnStopRequest(false),
198 : mProgressTimerIsActive(false),
199 : mIsHtml(false),
200 : mWarnAboutSyncHtml(false),
201 : mLoadTotal(-1),
202 : mIsSystem(false),
203 : mIsAnon(false),
204 : mFirstStartRequestSeen(false),
205 : mInLoadProgressEvent(false),
206 6 : mResultJSON(JS::UndefinedValue()),
207 : mResultArrayBuffer(nullptr),
208 : mIsMappedArrayBuffer(false),
209 : mXPCOMifier(nullptr),
210 9 : mEventDispatchingSuspended(false)
211 : {
212 3 : mozilla::HoldJSObjects(this);
213 3 : }
214 :
215 0 : XMLHttpRequestMainThread::~XMLHttpRequestMainThread()
216 : {
217 0 : mFlagDeleted = true;
218 :
219 0 : if ((mState == State::opened && mFlagSend) ||
220 0 : mState == State::loading) {
221 0 : Abort();
222 : }
223 :
224 0 : MOZ_ASSERT(!mFlagSyncLooping, "we rather crash than hang");
225 0 : mFlagSyncLooping = false;
226 :
227 0 : mResultJSON.setUndefined();
228 0 : mResultArrayBuffer = nullptr;
229 0 : mozilla::DropJSObjects(this);
230 0 : }
231 :
232 : /**
233 : * This Init method is called from the factory constructor.
234 : */
235 : nsresult
236 0 : XMLHttpRequestMainThread::Init()
237 : {
238 0 : nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
239 0 : nsCOMPtr<nsIPrincipal> subjectPrincipal;
240 0 : if (secMan) {
241 0 : secMan->GetSystemPrincipal(getter_AddRefs(subjectPrincipal));
242 : }
243 0 : NS_ENSURE_STATE(subjectPrincipal);
244 :
245 : // Instead of grabbing some random global from the context stack,
246 : // let's use the default one (junk scope) for now.
247 : // We should move away from this Init...
248 0 : Construct(subjectPrincipal, xpc::NativeGlobal(xpc::PrivilegedJunkScope()));
249 0 : return NS_OK;
250 : }
251 :
252 : /**
253 : * This Init method should only be called by C++ consumers.
254 : */
255 : NS_IMETHODIMP
256 0 : XMLHttpRequestMainThread::Init(nsIPrincipal* aPrincipal,
257 : nsIGlobalObject* aGlobalObject,
258 : nsIURI* aBaseURI,
259 : nsILoadGroup* aLoadGroup)
260 : {
261 0 : NS_ENSURE_ARG_POINTER(aPrincipal);
262 0 : Construct(aPrincipal, aGlobalObject, aBaseURI, aLoadGroup);
263 0 : return NS_OK;
264 : }
265 :
266 : void
267 0 : XMLHttpRequestMainThread::InitParameters(bool aAnon, bool aSystem)
268 : {
269 0 : if (!aAnon && !aSystem) {
270 0 : return;
271 : }
272 :
273 : // Check for permissions.
274 : // Chrome is always allowed access, so do the permission check only
275 : // for non-chrome pages.
276 0 : if (!IsSystemXHR() && aSystem) {
277 0 : nsIGlobalObject* global = GetOwnerGlobal();
278 0 : if (NS_WARN_IF(!global)) {
279 0 : SetParameters(aAnon, false);
280 0 : return;
281 : }
282 :
283 0 : nsIPrincipal* principal = global->PrincipalOrNull();
284 0 : if (NS_WARN_IF(!principal)) {
285 0 : SetParameters(aAnon, false);
286 0 : return;
287 : }
288 :
289 : nsCOMPtr<nsIPermissionManager> permMgr =
290 0 : services::GetPermissionManager();
291 0 : if (NS_WARN_IF(!permMgr)) {
292 0 : SetParameters(aAnon, false);
293 0 : return;
294 : }
295 :
296 : uint32_t permission;
297 : nsresult rv =
298 0 : permMgr->TestPermissionFromPrincipal(principal, "systemXHR", &permission);
299 0 : if (NS_FAILED(rv) || permission != nsIPermissionManager::ALLOW_ACTION) {
300 0 : SetParameters(aAnon, false);
301 0 : return;
302 : }
303 : }
304 :
305 0 : SetParameters(aAnon, aSystem);
306 : }
307 :
308 : void
309 7 : XMLHttpRequestMainThread::ResetResponse()
310 : {
311 7 : mResponseXML = nullptr;
312 7 : mResponseBody.Truncate();
313 7 : TruncateResponseText();
314 7 : mResponseBlob = nullptr;
315 7 : mBlobStorage = nullptr;
316 7 : mBlobSet = nullptr;
317 7 : mResultArrayBuffer = nullptr;
318 7 : mArrayBufferBuilder.reset();
319 7 : mResultJSON.setUndefined();
320 7 : mLoadTransferred = 0;
321 7 : mResponseBodyDecodedPos = 0;
322 7 : }
323 :
324 : void
325 0 : XMLHttpRequestMainThread::SetRequestObserver(nsIRequestObserver* aObserver)
326 : {
327 0 : mRequestObserver = aObserver;
328 0 : }
329 :
330 : NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestMainThread)
331 :
332 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestMainThread,
333 : XMLHttpRequestEventTarget)
334 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
335 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
336 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
337 :
338 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
339 :
340 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
341 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
342 :
343 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
344 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressEventSink)
345 :
346 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
347 3 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
348 :
349 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestMainThread,
350 : XMLHttpRequestEventTarget)
351 0 : tmp->mResultArrayBuffer = nullptr;
352 0 : tmp->mArrayBufferBuilder.reset();
353 0 : tmp->mResultJSON.setUndefined();
354 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
355 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
356 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
357 :
358 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
359 :
360 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
361 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)
362 :
363 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
364 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressEventSink)
365 :
366 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
367 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
368 :
369 7 : NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestMainThread,
370 : XMLHttpRequestEventTarget)
371 7 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
372 7 : NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultJSON)
373 7 : NS_IMPL_CYCLE_COLLECTION_TRACE_END
374 :
375 : bool
376 0 : XMLHttpRequestMainThread::IsCertainlyAliveForCC() const
377 : {
378 0 : return mWaitingForOnStopRequest;
379 : }
380 :
381 : // QueryInterface implementation for XMLHttpRequestMainThread
382 291 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XMLHttpRequestMainThread)
383 259 : NS_INTERFACE_MAP_ENTRY(nsIXMLHttpRequest)
384 259 : NS_INTERFACE_MAP_ENTRY(nsIJSXMLHttpRequest)
385 259 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
386 259 : NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
387 257 : NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
388 257 : NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
389 257 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
390 251 : NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
391 251 : NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
392 251 : NS_INTERFACE_MAP_ENTRY(nsINamed)
393 251 : NS_INTERFACE_MAP_ENTRY(nsISizeOfEventTarget)
394 251 : NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget)
395 :
396 335 : NS_IMPL_ADDREF_INHERITED(XMLHttpRequestMainThread, XMLHttpRequestEventTarget)
397 329 : NS_IMPL_RELEASE_INHERITED(XMLHttpRequestMainThread, XMLHttpRequestEventTarget)
398 :
399 0 : NS_IMPL_EVENT_HANDLER(XMLHttpRequestMainThread, readystatechange)
400 :
401 : void
402 0 : XMLHttpRequestMainThread::DisconnectFromOwner()
403 : {
404 0 : XMLHttpRequestEventTarget::DisconnectFromOwner();
405 0 : Abort();
406 0 : }
407 :
408 : size_t
409 0 : XMLHttpRequestMainThread::SizeOfEventTargetIncludingThis(
410 : MallocSizeOf aMallocSizeOf) const
411 : {
412 0 : size_t n = aMallocSizeOf(this);
413 0 : n += mResponseBody.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
414 :
415 : // Why is this safe? Because no-one else will report this string. The
416 : // other possible sharers of this string are as follows.
417 : //
418 : // - The JS engine could hold copies if the JS code holds references, e.g.
419 : // |var text = XHR.responseText|. However, those references will be via JS
420 : // external strings, for which the JS memory reporter does *not* report the
421 : // chars.
422 : //
423 : // - Binary extensions, but they're *extremely* unlikely to do any memory
424 : // reporting.
425 : //
426 0 : n += mResponseText.SizeOfThis(aMallocSizeOf);
427 :
428 0 : return n;
429 :
430 : // Measurement of the following members may be added later if DMD finds it is
431 : // worthwhile:
432 : // - lots
433 : }
434 :
435 : NS_IMETHODIMP
436 0 : XMLHttpRequestMainThread::GetChannel(nsIChannel **aChannel)
437 : {
438 0 : NS_ENSURE_ARG_POINTER(aChannel);
439 0 : NS_IF_ADDREF(*aChannel = mChannel);
440 :
441 0 : return NS_OK;
442 : }
443 :
444 0 : static void LogMessage(const char* aWarning, nsPIDOMWindowInner* aWindow,
445 : const char16_t** aParams=nullptr, uint32_t aParamCount=0)
446 : {
447 0 : nsCOMPtr<nsIDocument> doc;
448 0 : if (aWindow) {
449 0 : doc = aWindow->GetExtantDoc();
450 : }
451 0 : nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
452 0 : NS_LITERAL_CSTRING("DOM"), doc,
453 : nsContentUtils::eDOM_PROPERTIES,
454 0 : aWarning, aParams, aParamCount);
455 0 : }
456 :
457 : NS_IMETHODIMP
458 0 : XMLHttpRequestMainThread::GetResponseXML(nsIDOMDocument **aResponseXML)
459 : {
460 0 : ErrorResult rv;
461 0 : nsIDocument* responseXML = GetResponseXML(rv);
462 0 : if (rv.Failed()) {
463 0 : return rv.StealNSResult();
464 : }
465 :
466 0 : if (!responseXML) {
467 0 : *aResponseXML = nullptr;
468 0 : return NS_OK;
469 : }
470 :
471 0 : return CallQueryInterface(responseXML, aResponseXML);
472 : }
473 :
474 : nsIDocument*
475 0 : XMLHttpRequestMainThread::GetResponseXML(ErrorResult& aRv)
476 : {
477 0 : if (mResponseType != XMLHttpRequestResponseType::_empty &&
478 0 : mResponseType != XMLHttpRequestResponseType::Document) {
479 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSEXML);
480 0 : return nullptr;
481 : }
482 0 : if (mWarnAboutSyncHtml) {
483 0 : mWarnAboutSyncHtml = false;
484 0 : LogMessage("HTMLSyncXHRWarning", GetOwner());
485 : }
486 0 : if (mState != State::done) {
487 0 : return nullptr;
488 : }
489 0 : return mResponseXML;
490 : }
491 :
492 : /*
493 : * This piece copied from XMLDocument, we try to get the charset
494 : * from HTTP headers.
495 : */
496 : nsresult
497 2 : XMLHttpRequestMainThread::DetectCharset()
498 : {
499 2 : mResponseCharset = nullptr;
500 2 : mDecoder = nullptr;
501 :
502 4 : if (mResponseType != XMLHttpRequestResponseType::_empty &&
503 2 : mResponseType != XMLHttpRequestResponseType::Text &&
504 0 : mResponseType != XMLHttpRequestResponseType::Json &&
505 0 : mResponseType != XMLHttpRequestResponseType::Moz_chunked_text) {
506 0 : return NS_OK;
507 : }
508 :
509 4 : nsAutoCString charsetVal;
510 : const Encoding* encoding;
511 2 : bool ok = mChannel &&
512 4 : NS_SUCCEEDED(mChannel->GetContentCharset(charsetVal)) &&
513 4 : (encoding = Encoding::ForLabel(charsetVal));
514 2 : if (!ok) {
515 : // MS documentation states UTF-8 is default for responseText
516 2 : encoding = UTF_8_ENCODING;
517 : }
518 :
519 2 : if (mResponseType == XMLHttpRequestResponseType::Json &&
520 0 : encoding != UTF_8_ENCODING) {
521 : // The XHR spec says only UTF-8 is supported for responseType == "json"
522 0 : LogMessage("JSONCharsetWarning", GetOwner());
523 0 : encoding = UTF_8_ENCODING;
524 : }
525 :
526 2 : mResponseCharset = encoding;
527 2 : mDecoder = encoding->NewDecoderWithBOMRemoval();
528 :
529 2 : return NS_OK;
530 : }
531 :
532 : nsresult
533 3 : XMLHttpRequestMainThread::AppendToResponseText(const char * aSrcBuffer,
534 : uint32_t aSrcBufferLen)
535 : {
536 3 : NS_ENSURE_STATE(mDecoder);
537 :
538 : CheckedInt<size_t> destBufferLen =
539 3 : mDecoder->MaxUTF16BufferLength(aSrcBufferLen);
540 3 : if (!destBufferLen.isValid()) {
541 0 : return NS_ERROR_OUT_OF_MEMORY;
542 : }
543 :
544 3 : CheckedInt32 size = mResponseText.Length();
545 3 : size += destBufferLen.value();
546 3 : if (!size.isValid()) {
547 0 : return NS_ERROR_OUT_OF_MEMORY;
548 : }
549 :
550 6 : XMLHttpRequestStringWriterHelper helper(mResponseText);
551 :
552 3 : if (!helper.AddCapacity(destBufferLen.value())) {
553 0 : return NS_ERROR_OUT_OF_MEMORY;
554 : }
555 :
556 : // XXX there's no handling for incomplete byte sequences on EOF!
557 : uint32_t result;
558 : size_t read;
559 : size_t written;
560 : bool hadErrors;
561 6 : Tie(result, read, written, hadErrors) = mDecoder->DecodeToUTF16(
562 : AsBytes(MakeSpan(aSrcBuffer, aSrcBufferLen)),
563 : MakeSpan(helper.EndOfExistingData(), destBufferLen.value()),
564 3 : false);
565 3 : MOZ_ASSERT(result == kInputEmpty);
566 3 : MOZ_ASSERT(read == aSrcBufferLen);
567 3 : MOZ_ASSERT(written <= destBufferLen.value());
568 : Unused << hadErrors;
569 3 : helper.AddLength(written);
570 3 : return NS_OK;
571 : }
572 :
573 : NS_IMETHODIMP
574 0 : XMLHttpRequestMainThread::GetResponseText(nsAString& aResponseText)
575 : {
576 0 : ErrorResult rv;
577 0 : DOMString str;
578 0 : GetResponseText(str, rv);
579 0 : if (NS_WARN_IF(rv.Failed())) {
580 0 : return rv.StealNSResult();
581 : }
582 :
583 0 : str.ToString(aResponseText);
584 0 : return NS_OK;
585 : }
586 :
587 : void
588 0 : XMLHttpRequestMainThread::GetResponseText(DOMString& aResponseText,
589 : ErrorResult& aRv)
590 : {
591 0 : XMLHttpRequestStringSnapshot snapshot;
592 0 : GetResponseText(snapshot, aRv);
593 0 : if (aRv.Failed()) {
594 0 : return;
595 : }
596 :
597 0 : if (!snapshot.GetAsString(aResponseText)) {
598 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
599 0 : return;
600 : }
601 : }
602 :
603 : void
604 17 : XMLHttpRequestMainThread::GetResponseText(XMLHttpRequestStringSnapshot& aSnapshot,
605 : ErrorResult& aRv)
606 : {
607 17 : aSnapshot.Reset();
608 :
609 31 : if (mResponseType != XMLHttpRequestResponseType::_empty &&
610 14 : mResponseType != XMLHttpRequestResponseType::Text &&
611 0 : mResponseType != XMLHttpRequestResponseType::Moz_chunked_text) {
612 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_WRONG_RESPONSETYPE_FOR_RESPONSETEXT);
613 0 : return;
614 : }
615 :
616 17 : if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text &&
617 0 : !mInLoadProgressEvent) {
618 0 : aSnapshot.SetVoid();
619 0 : return;
620 : }
621 :
622 17 : if (mState != State::loading && mState != State::done) {
623 7 : return;
624 : }
625 :
626 : // We only decode text lazily if we're also parsing to a doc.
627 : // Also, if we've decoded all current data already, then no need to decode
628 : // more.
629 10 : if ((!mResponseXML && !mErrorParsingXML) ||
630 0 : mResponseBodyDecodedPos == mResponseBody.Length()) {
631 10 : mResponseText.CreateSnapshot(aSnapshot);
632 10 : return;
633 : }
634 :
635 0 : MatchCharsetAndDecoderToResponseDocument();
636 :
637 0 : NS_ASSERTION(mResponseBodyDecodedPos < mResponseBody.Length(),
638 : "Unexpected mResponseBodyDecodedPos");
639 0 : aRv = AppendToResponseText(mResponseBody.get() + mResponseBodyDecodedPos,
640 0 : mResponseBody.Length() - mResponseBodyDecodedPos);
641 0 : if (aRv.Failed()) {
642 0 : return;
643 : }
644 :
645 0 : mResponseBodyDecodedPos = mResponseBody.Length();
646 :
647 0 : if (mState == State::done) {
648 : // Free memory buffer which we no longer need
649 0 : mResponseBody.Truncate();
650 0 : mResponseBodyDecodedPos = 0;
651 : }
652 :
653 0 : mResponseText.CreateSnapshot(aSnapshot);
654 : }
655 :
656 : nsresult
657 0 : XMLHttpRequestMainThread::CreateResponseParsedJSON(JSContext* aCx)
658 : {
659 0 : if (!aCx) {
660 0 : return NS_ERROR_FAILURE;
661 : }
662 :
663 0 : nsAutoString string;
664 0 : if (!mResponseText.GetAsString(string)) {
665 0 : return NS_ERROR_OUT_OF_MEMORY;
666 : }
667 :
668 : // The Unicode converter has already zapped the BOM if there was one
669 0 : JS::Rooted<JS::Value> value(aCx);
670 0 : if (!JS_ParseJSON(aCx, string.BeginReading(), string.Length(), &value)) {
671 0 : return NS_ERROR_FAILURE;
672 : }
673 :
674 0 : mResultJSON = value;
675 0 : return NS_OK;
676 : }
677 :
678 : void
679 0 : XMLHttpRequestMainThread::CreatePartialBlob(ErrorResult& aRv)
680 : {
681 : // mBlobSet can be null if the request has been canceled
682 0 : if (!mBlobSet) {
683 0 : return;
684 : }
685 :
686 0 : nsAutoCString contentType;
687 0 : if (mState == State::done) {
688 0 : mChannel->GetContentType(contentType);
689 : }
690 :
691 0 : nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
692 : RefPtr<BlobImpl> blobImpl =
693 0 : MultipartBlobImpl::Create(Move(subImpls),
694 0 : NS_ConvertASCIItoUTF16(contentType),
695 0 : aRv);
696 0 : if (NS_WARN_IF(aRv.Failed())) {
697 0 : return;
698 : }
699 :
700 0 : mResponseBlob = Blob::Create(GetOwner(), blobImpl);
701 : }
702 :
703 17 : NS_IMETHODIMP XMLHttpRequestMainThread::GetResponseType(nsAString& aResponseType)
704 : {
705 17 : MOZ_ASSERT(mResponseType < XMLHttpRequestResponseType::EndGuard_);
706 : const EnumEntry& entry =
707 17 : XMLHttpRequestResponseTypeValues::strings[static_cast<uint32_t>(mResponseType)];
708 17 : aResponseType.AssignASCII(entry.value, entry.length);
709 17 : return NS_OK;
710 : }
711 :
712 0 : NS_IMETHODIMP XMLHttpRequestMainThread::SetResponseType(const nsAString& aResponseType)
713 : {
714 0 : uint32_t i = 0;
715 0 : for (const EnumEntry* entry = XMLHttpRequestResponseTypeValues::strings;
716 0 : entry->value; ++entry, ++i) {
717 0 : if (aResponseType.EqualsASCII(entry->value, entry->length)) {
718 0 : ErrorResult rv;
719 0 : SetResponseType(static_cast<XMLHttpRequestResponseType>(i), rv);
720 0 : return rv.StealNSResult();
721 : }
722 : }
723 :
724 0 : return NS_OK;
725 : }
726 :
727 : void
728 5 : XMLHttpRequestMainThread::SetResponseType(XMLHttpRequestResponseType aResponseType,
729 : ErrorResult& aRv)
730 : {
731 5 : if (mState == State::loading || mState == State::done) {
732 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
733 0 : return;
734 : }
735 :
736 : // sync request is not allowed setting responseType in window context
737 5 : if (HasOrHasHadOwner() && mState != State::unsent && mFlagSynchronous) {
738 0 : LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
739 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
740 0 : return;
741 : }
742 :
743 5 : if (mFlagSynchronous &&
744 0 : (aResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
745 : aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
746 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC);
747 0 : return;
748 : }
749 :
750 : // We want to get rid of this moz-only types. Bug 1335365.
751 5 : if (aResponseType == XMLHttpRequestResponseType::Moz_blob) {
752 0 : Telemetry::Accumulate(Telemetry::MOZ_BLOB_IN_XHR, 1);
753 5 : } else if (aResponseType == XMLHttpRequestResponseType::Moz_chunked_text) {
754 0 : Telemetry::Accumulate(Telemetry::MOZ_CHUNKED_TEXT_IN_XHR, 1);
755 5 : } else if (aResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
756 0 : Telemetry::Accumulate(Telemetry::MOZ_CHUNKED_ARRAYBUFFER_IN_XHR, 1);
757 : }
758 :
759 : // Set the responseType attribute's value to the given value.
760 5 : mResponseType = aResponseType;
761 : }
762 :
763 : NS_IMETHODIMP
764 0 : XMLHttpRequestMainThread::GetResponse(JSContext *aCx, JS::MutableHandle<JS::Value> aResult)
765 : {
766 0 : ErrorResult rv;
767 0 : GetResponse(aCx, aResult, rv);
768 0 : return rv.StealNSResult();
769 : }
770 :
771 : void
772 0 : XMLHttpRequestMainThread::GetResponse(JSContext* aCx,
773 : JS::MutableHandle<JS::Value> aResponse,
774 : ErrorResult& aRv)
775 : {
776 0 : switch (mResponseType) {
777 : case XMLHttpRequestResponseType::_empty:
778 : case XMLHttpRequestResponseType::Text:
779 : case XMLHttpRequestResponseType::Moz_chunked_text:
780 : {
781 0 : DOMString str;
782 0 : GetResponseText(str, aRv);
783 0 : if (aRv.Failed()) {
784 0 : return;
785 : }
786 0 : if (!xpc::StringToJsval(aCx, str, aResponse)) {
787 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
788 : }
789 0 : return;
790 : }
791 :
792 : case XMLHttpRequestResponseType::Arraybuffer:
793 : case XMLHttpRequestResponseType::Moz_chunked_arraybuffer:
794 : {
795 0 : if (!(mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
796 0 : mState == State::done) &&
797 0 : !(mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer &&
798 0 : mInLoadProgressEvent)) {
799 0 : aResponse.setNull();
800 0 : return;
801 : }
802 :
803 0 : if (!mResultArrayBuffer) {
804 0 : mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
805 0 : if (!mResultArrayBuffer) {
806 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
807 0 : return;
808 : }
809 : }
810 0 : aResponse.setObject(*mResultArrayBuffer);
811 0 : return;
812 : }
813 : case XMLHttpRequestResponseType::Blob:
814 : case XMLHttpRequestResponseType::Moz_blob:
815 : {
816 0 : if (mState != State::done) {
817 0 : if (mResponseType != XMLHttpRequestResponseType::Moz_blob) {
818 0 : aResponse.setNull();
819 0 : return;
820 : }
821 :
822 0 : if (!mResponseBlob) {
823 0 : CreatePartialBlob(aRv);
824 : }
825 : }
826 :
827 0 : if (!mResponseBlob) {
828 0 : aResponse.setNull();
829 0 : return;
830 : }
831 :
832 0 : GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse);
833 0 : return;
834 : }
835 : case XMLHttpRequestResponseType::Document:
836 : {
837 0 : if (!mResponseXML || mState != State::done) {
838 0 : aResponse.setNull();
839 0 : return;
840 : }
841 :
842 0 : aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
843 0 : return;
844 : }
845 : case XMLHttpRequestResponseType::Json:
846 : {
847 0 : if (mState != State::done) {
848 0 : aResponse.setNull();
849 0 : return;
850 : }
851 :
852 0 : if (mResultJSON.isUndefined()) {
853 0 : aRv = CreateResponseParsedJSON(aCx);
854 0 : TruncateResponseText();
855 0 : if (aRv.Failed()) {
856 : // Per spec, errors aren't propagated. null is returned instead.
857 0 : aRv = NS_OK;
858 : // It would be nice to log the error to the console. That's hard to
859 : // do without calling window.onerror as a side effect, though.
860 0 : JS_ClearPendingException(aCx);
861 0 : mResultJSON.setNull();
862 : }
863 : }
864 0 : aResponse.set(mResultJSON);
865 0 : return;
866 : }
867 : default:
868 0 : NS_ERROR("Should not happen");
869 : }
870 :
871 0 : aResponse.setNull();
872 : }
873 :
874 : bool
875 62 : XMLHttpRequestMainThread::IsCrossSiteCORSRequest() const
876 : {
877 62 : if (!mChannel) {
878 0 : return false;
879 : }
880 :
881 124 : nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
882 62 : MOZ_ASSERT(loadInfo);
883 :
884 62 : if (!loadInfo) {
885 0 : return false;
886 : }
887 :
888 62 : return loadInfo->GetTainting() == LoadTainting::CORS;
889 : }
890 :
891 : bool
892 54 : XMLHttpRequestMainThread::IsDeniedCrossSiteCORSRequest()
893 : {
894 54 : if (IsCrossSiteCORSRequest()) {
895 : nsresult rv;
896 0 : mChannel->GetStatus(&rv);
897 0 : if (NS_FAILED(rv)) {
898 0 : return true;
899 : }
900 : }
901 54 : return false;
902 : }
903 :
904 : void
905 17 : XMLHttpRequestMainThread::GetResponseURL(nsAString& aUrl)
906 : {
907 17 : aUrl.Truncate();
908 :
909 17 : uint16_t readyState = ReadyState();
910 17 : if ((readyState == UNSENT || readyState == OPENED) || !mChannel) {
911 10 : return;
912 : }
913 :
914 : // Make sure we don't leak responseURL information from denied cross-site
915 : // requests.
916 12 : if (IsDeniedCrossSiteCORSRequest()) {
917 0 : return;
918 : }
919 :
920 24 : nsCOMPtr<nsIURI> responseUrl;
921 12 : mChannel->GetURI(getter_AddRefs(responseUrl));
922 :
923 12 : if (!responseUrl) {
924 0 : return;
925 : }
926 :
927 24 : nsAutoCString temp;
928 12 : responseUrl->GetSpecIgnoringRef(temp);
929 12 : CopyUTF8toUTF16(temp, aUrl);
930 : }
931 :
932 : NS_IMETHODIMP
933 17 : XMLHttpRequestMainThread::GetStatus(uint32_t *aStatus)
934 : {
935 34 : ErrorResult rv;
936 17 : *aStatus = GetStatus(rv);
937 34 : return rv.StealNSResult();
938 : }
939 :
940 : uint32_t
941 17 : XMLHttpRequestMainThread::GetStatus(ErrorResult& aRv)
942 : {
943 : // Make sure we don't leak status information from denied cross-site
944 : // requests.
945 17 : if (IsDeniedCrossSiteCORSRequest()) {
946 0 : return 0;
947 : }
948 :
949 17 : uint16_t readyState = ReadyState();
950 17 : if (readyState == UNSENT || readyState == OPENED) {
951 5 : return 0;
952 : }
953 :
954 12 : if (mErrorLoad != ErrorType::eOK) {
955 : // Let's simulate the http protocol for jar/app requests:
956 0 : nsCOMPtr<nsIJARChannel> jarChannel = GetCurrentJARChannel();
957 0 : if (jarChannel) {
958 : nsresult status;
959 0 : mChannel->GetStatus(&status);
960 :
961 0 : if (status == NS_ERROR_FILE_NOT_FOUND) {
962 0 : return 404; // Not Found
963 : } else {
964 0 : return 500; // Internal Error
965 : }
966 : }
967 :
968 0 : return 0;
969 : }
970 :
971 24 : nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
972 12 : if (!httpChannel) {
973 : // Pretend like we got a 200 response, since our load was successful
974 12 : return 200;
975 : }
976 :
977 : uint32_t status;
978 0 : nsresult rv = httpChannel->GetResponseStatus(&status);
979 0 : if (NS_FAILED(rv)) {
980 0 : status = 0;
981 : }
982 :
983 0 : return status;
984 : }
985 :
986 : NS_IMETHODIMP
987 0 : XMLHttpRequestMainThread::GetStatusText(nsACString& aOut)
988 : {
989 0 : ErrorResult rv;
990 0 : GetStatusText(aOut, rv);
991 0 : return rv.StealNSResult();
992 : }
993 :
994 : void
995 17 : XMLHttpRequestMainThread::GetStatusText(nsACString& aStatusText,
996 : ErrorResult& aRv)
997 : {
998 : // Return an empty status text on all error loads.
999 17 : aStatusText.Truncate();
1000 :
1001 : // Make sure we don't leak status information from denied cross-site
1002 : // requests.
1003 17 : if (IsDeniedCrossSiteCORSRequest()) {
1004 5 : return;
1005 : }
1006 :
1007 : // Check the current XHR state to see if it is valid to obtain the statusText
1008 : // value. This check is to prevent the status text for redirects from being
1009 : // available before all the redirects have been followed and HTTP headers have
1010 : // been received.
1011 17 : uint16_t readyState = ReadyState();
1012 17 : if (readyState == UNSENT || readyState == OPENED) {
1013 5 : return;
1014 : }
1015 :
1016 12 : if (mErrorLoad != ErrorType::eOK) {
1017 0 : return;
1018 : }
1019 :
1020 24 : nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1021 12 : if (httpChannel) {
1022 0 : Unused << httpChannel->GetResponseStatusText(aStatusText);
1023 : } else {
1024 12 : aStatusText.AssignLiteral("OK");
1025 : }
1026 : }
1027 :
1028 : void
1029 3 : XMLHttpRequestMainThread::CloseRequest()
1030 : {
1031 3 : mWaitingForOnStopRequest = false;
1032 3 : if (mChannel) {
1033 0 : mChannel->Cancel(NS_BINDING_ABORTED);
1034 : }
1035 3 : if (mTimeoutTimer) {
1036 0 : mTimeoutTimer->Cancel();
1037 : }
1038 3 : }
1039 :
1040 : void
1041 0 : XMLHttpRequestMainThread::CloseRequestWithError(const ProgressEventType aType)
1042 : {
1043 0 : CloseRequest();
1044 :
1045 0 : ResetResponse();
1046 :
1047 : // If we're in the destructor, don't risk dispatching an event.
1048 0 : if (mFlagDeleted) {
1049 0 : mFlagSyncLooping = false;
1050 0 : return;
1051 : }
1052 :
1053 0 : if (mState != State::unsent &&
1054 0 : !(mState == State::opened && !mFlagSend) &&
1055 0 : mState != State::done) {
1056 0 : ChangeState(State::done, true);
1057 :
1058 0 : if (!mFlagSyncLooping) {
1059 0 : if (mUpload && !mUploadComplete) {
1060 0 : mUploadComplete = true;
1061 0 : DispatchProgressEvent(mUpload, aType, 0, -1);
1062 : }
1063 0 : DispatchProgressEvent(this, aType, 0, -1);
1064 : }
1065 : }
1066 :
1067 : // The ChangeState call above calls onreadystatechange handlers which
1068 : // if they load a new url will cause XMLHttpRequestMainThread::Open to clear
1069 : // the abort state bit. If this occurs we're not uninitialized (bug 361773).
1070 0 : if (mFlagAborted) {
1071 0 : ChangeState(State::unsent, false); // IE seems to do it
1072 : }
1073 :
1074 0 : mFlagSyncLooping = false;
1075 : }
1076 :
1077 : void
1078 0 : XMLHttpRequestMainThread::RequestErrorSteps(const ProgressEventType aEventType,
1079 : const nsresult aOptionalException,
1080 : ErrorResult& aRv)
1081 : {
1082 : // Step 1
1083 0 : mState = State::done;
1084 :
1085 0 : StopProgressEventTimer();
1086 :
1087 : // Step 2
1088 0 : mFlagSend = false;
1089 :
1090 : // Step 3
1091 0 : ResetResponse();
1092 :
1093 : // If we're in the destructor, don't risk dispatching an event.
1094 0 : if (mFlagDeleted) {
1095 0 : mFlagSyncLooping = false;
1096 0 : return;
1097 : }
1098 :
1099 : // Step 4
1100 0 : if (mFlagSynchronous && NS_FAILED(aOptionalException)) {
1101 0 : aRv.Throw(aOptionalException);
1102 0 : return;
1103 : }
1104 :
1105 : // Step 5
1106 0 : FireReadystatechangeEvent();
1107 :
1108 : // Step 6
1109 0 : if (mUpload) {
1110 :
1111 : // Step 6-1
1112 0 : if (!mUploadComplete) {
1113 0 : mUploadComplete = true;
1114 : }
1115 :
1116 : // Step 6-2
1117 0 : if (mFlagHadUploadListenersOnSend) {
1118 :
1119 : // Steps 6-3, 6-4 (loadend is fired for us)
1120 0 : DispatchProgressEvent(mUpload, aEventType, 0, -1);
1121 : }
1122 : }
1123 :
1124 : // Steps 7 and 8 (loadend is fired for us)
1125 0 : DispatchProgressEvent(this, aEventType, 0, -1);
1126 : }
1127 :
1128 : void
1129 0 : XMLHttpRequestMainThread::Abort(ErrorResult& aRv)
1130 : {
1131 0 : mFlagAborted = true;
1132 :
1133 : // Step 1
1134 0 : CloseRequest();
1135 :
1136 : // Step 2
1137 0 : if ((mState == State::opened && mFlagSend) ||
1138 0 : mState == State::headers_received ||
1139 0 : mState == State::loading) {
1140 0 : RequestErrorSteps(ProgressEventType::abort, NS_OK, aRv);
1141 : }
1142 :
1143 : // Step 3
1144 0 : if (mState == State::done) {
1145 0 : ChangeState(State::unsent, false); // no ReadystateChange event
1146 : }
1147 :
1148 0 : mFlagSyncLooping = false;
1149 0 : }
1150 :
1151 : NS_IMETHODIMP
1152 0 : XMLHttpRequestMainThread::SlowAbort()
1153 : {
1154 0 : Abort();
1155 0 : return NS_OK;
1156 : }
1157 :
1158 : /*Method that checks if it is safe to expose a header value to the client.
1159 : It is used to check what headers are exposed for CORS requests.*/
1160 : bool
1161 0 : XMLHttpRequestMainThread::IsSafeHeader(const nsACString& aHeader,
1162 : NotNull<nsIHttpChannel*> aHttpChannel) const
1163 : {
1164 : // See bug #380418. Hide "Set-Cookie" headers from non-chrome scripts.
1165 0 : if (!IsSystemXHR() && nsContentUtils::IsForbiddenResponseHeader(aHeader)) {
1166 0 : NS_WARNING("blocked access to response header");
1167 0 : return false;
1168 : }
1169 : // if this is not a CORS call all headers are safe
1170 0 : if (!IsCrossSiteCORSRequest()) {
1171 0 : return true;
1172 : }
1173 : // Check for dangerous headers
1174 : // Make sure we don't leak header information from denied cross-site
1175 : // requests.
1176 0 : if (mChannel) {
1177 : nsresult status;
1178 0 : mChannel->GetStatus(&status);
1179 0 : if (NS_FAILED(status)) {
1180 0 : return false;
1181 : }
1182 : }
1183 : const char* kCrossOriginSafeHeaders[] = {
1184 : "cache-control", "content-language", "content-type", "expires",
1185 : "last-modified", "pragma"
1186 0 : };
1187 0 : for (uint32_t i = 0; i < ArrayLength(kCrossOriginSafeHeaders); ++i) {
1188 0 : if (aHeader.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
1189 0 : return true;
1190 : }
1191 : }
1192 0 : nsAutoCString headerVal;
1193 : // The "Access-Control-Expose-Headers" header contains a comma separated
1194 : // list of method names.
1195 0 : Unused << aHttpChannel->
1196 0 : GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"),
1197 0 : headerVal);
1198 0 : nsCCharSeparatedTokenizer exposeTokens(headerVal, ',');
1199 0 : bool isSafe = false;
1200 0 : while (exposeTokens.hasMoreTokens()) {
1201 0 : const nsDependentCSubstring& token = exposeTokens.nextToken();
1202 0 : if (token.IsEmpty()) {
1203 0 : continue;
1204 : }
1205 0 : if (!NS_IsValidHTTPToken(token)) {
1206 0 : return false;
1207 : }
1208 0 : if (aHeader.Equals(token, nsCaseInsensitiveCStringComparator())) {
1209 0 : isSafe = true;
1210 : }
1211 : }
1212 0 : return isSafe;
1213 : }
1214 :
1215 : NS_IMETHODIMP
1216 0 : XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aOut)
1217 : {
1218 0 : ErrorResult rv;
1219 0 : GetAllResponseHeaders(aOut, rv);
1220 0 : return rv.StealNSResult();
1221 : }
1222 :
1223 : void
1224 0 : XMLHttpRequestMainThread::GetAllResponseHeaders(nsACString& aResponseHeaders,
1225 : ErrorResult& aRv)
1226 : {
1227 0 : aResponseHeaders.Truncate();
1228 :
1229 : // If the state is UNSENT or OPENED,
1230 : // return the empty string and terminate these steps.
1231 0 : if (mState == State::unsent || mState == State::opened) {
1232 0 : return;
1233 : }
1234 :
1235 0 : if (mErrorLoad != ErrorType::eOK) {
1236 0 : return;
1237 : }
1238 :
1239 0 : if (nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel()) {
1240 : RefPtr<nsHeaderVisitor> visitor =
1241 0 : new nsHeaderVisitor(*this, WrapNotNull(httpChannel));
1242 0 : if (NS_SUCCEEDED(httpChannel->VisitResponseHeaders(visitor))) {
1243 0 : aResponseHeaders = visitor->Headers();
1244 : }
1245 0 : return;
1246 : }
1247 :
1248 0 : if (!mChannel) {
1249 0 : return;
1250 : }
1251 :
1252 : // Even non-http channels supply content type.
1253 0 : nsAutoCString value;
1254 0 : if (NS_SUCCEEDED(mChannel->GetContentType(value))) {
1255 0 : aResponseHeaders.AppendLiteral("Content-Type: ");
1256 0 : aResponseHeaders.Append(value);
1257 0 : if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) && !value.IsEmpty()) {
1258 0 : aResponseHeaders.AppendLiteral(";charset=");
1259 0 : aResponseHeaders.Append(value);
1260 : }
1261 0 : aResponseHeaders.AppendLiteral("\r\n");
1262 : }
1263 :
1264 : // Don't provide Content-Length for data URIs
1265 0 : nsCOMPtr<nsIURI> uri;
1266 : bool isDataURI;
1267 0 : if (NS_FAILED(mChannel->GetURI(getter_AddRefs(uri))) ||
1268 0 : NS_FAILED(uri->SchemeIs("data", &isDataURI)) ||
1269 0 : !isDataURI) {
1270 : int64_t length;
1271 0 : if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
1272 0 : aResponseHeaders.AppendLiteral("Content-Length: ");
1273 0 : aResponseHeaders.AppendInt(length);
1274 0 : aResponseHeaders.AppendLiteral("\r\n");
1275 : }
1276 : }
1277 : }
1278 :
1279 : NS_IMETHODIMP
1280 0 : XMLHttpRequestMainThread::GetResponseHeader(const nsACString& aHeader,
1281 : nsACString& aResult)
1282 : {
1283 0 : ErrorResult rv;
1284 0 : GetResponseHeader(aHeader, aResult, rv);
1285 0 : return rv.StealNSResult();
1286 : }
1287 :
1288 : void
1289 0 : XMLHttpRequestMainThread::GetResponseHeader(const nsACString& header,
1290 : nsACString& _retval, ErrorResult& aRv)
1291 : {
1292 0 : _retval.SetIsVoid(true);
1293 :
1294 0 : nsCOMPtr<nsIHttpChannel> httpChannel = GetCurrentHttpChannel();
1295 :
1296 0 : if (!httpChannel) {
1297 : // If the state is UNSENT or OPENED,
1298 : // return null and terminate these steps.
1299 0 : if (mState == State::unsent || mState == State::opened) {
1300 0 : return;
1301 : }
1302 :
1303 : // Even non-http channels supply content type and content length.
1304 : // Remember we don't leak header information from denied cross-site
1305 : // requests.
1306 : nsresult status;
1307 0 : if (!mChannel ||
1308 0 : NS_FAILED(mChannel->GetStatus(&status)) ||
1309 0 : NS_FAILED(status)) {
1310 0 : return;
1311 : }
1312 :
1313 : // Content Type:
1314 0 : if (header.LowerCaseEqualsASCII("content-type")) {
1315 0 : if (NS_FAILED(mChannel->GetContentType(_retval))) {
1316 : // Means no content type
1317 0 : _retval.SetIsVoid(true);
1318 0 : return;
1319 : }
1320 :
1321 0 : nsCString value;
1322 0 : if (NS_SUCCEEDED(mChannel->GetContentCharset(value)) &&
1323 0 : !value.IsEmpty()) {
1324 0 : _retval.AppendLiteral(";charset=");
1325 0 : _retval.Append(value);
1326 : }
1327 : }
1328 :
1329 : // Content Length:
1330 0 : else if (header.LowerCaseEqualsASCII("content-length")) {
1331 : int64_t length;
1332 0 : if (NS_SUCCEEDED(mChannel->GetContentLength(&length))) {
1333 0 : _retval.AppendInt(length);
1334 : }
1335 : }
1336 :
1337 0 : return;
1338 : }
1339 :
1340 : // Check for dangerous headers
1341 0 : if (!IsSafeHeader(header, WrapNotNull(httpChannel))) {
1342 0 : return;
1343 : }
1344 :
1345 0 : aRv = httpChannel->GetResponseHeader(header, _retval);
1346 0 : if (aRv.ErrorCodeIs(NS_ERROR_NOT_AVAILABLE)) {
1347 : // Means no header
1348 0 : _retval.SetIsVoid(true);
1349 0 : aRv.SuppressException();
1350 : }
1351 : }
1352 :
1353 : already_AddRefed<nsILoadGroup>
1354 3 : XMLHttpRequestMainThread::GetLoadGroup() const
1355 : {
1356 3 : if (mFlagBackgroundRequest) {
1357 0 : return nullptr;
1358 : }
1359 :
1360 3 : if (mLoadGroup) {
1361 6 : nsCOMPtr<nsILoadGroup> ref = mLoadGroup;
1362 3 : return ref.forget();
1363 : }
1364 :
1365 0 : nsIDocument* doc = GetDocumentIfCurrent();
1366 0 : if (doc) {
1367 0 : return doc->GetDocumentLoadGroup();
1368 : }
1369 :
1370 0 : return nullptr;
1371 : }
1372 :
1373 : nsresult
1374 9 : XMLHttpRequestMainThread::FireReadystatechangeEvent()
1375 : {
1376 18 : RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
1377 9 : event->InitEvent(kLiteralString_readystatechange, false, false);
1378 : // We assume anyone who managed to call CreateReadystatechangeEvent is trusted
1379 9 : event->SetTrusted(true);
1380 9 : DispatchOrStoreEvent(this, event);
1381 18 : return NS_OK;
1382 : }
1383 :
1384 : void
1385 8 : XMLHttpRequestMainThread::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
1386 : const ProgressEventType aType,
1387 : int64_t aLoaded, int64_t aTotal)
1388 : {
1389 8 : NS_ASSERTION(aTarget, "null target");
1390 :
1391 16 : if (NS_FAILED(CheckInnerWindowCorrectness()) ||
1392 8 : (!AllowUploadProgress() && aTarget == mUpload)) {
1393 0 : return;
1394 : }
1395 :
1396 : // If blocked by CORS, zero-out the stats on progress events
1397 : // and never fire "progress" or "load" events at all.
1398 8 : if (IsDeniedCrossSiteCORSRequest()) {
1399 0 : if (aType == ProgressEventType::progress ||
1400 : aType == ProgressEventType::load) {
1401 0 : return;
1402 : }
1403 0 : aLoaded = 0;
1404 0 : aTotal = -1;
1405 : }
1406 :
1407 8 : if (aType == ProgressEventType::progress) {
1408 2 : mInLoadProgressEvent = true;
1409 : }
1410 :
1411 8 : ProgressEventInit init;
1412 8 : init.mBubbles = false;
1413 8 : init.mCancelable = false;
1414 8 : init.mLengthComputable = aTotal != -1; // XHR spec step 6.1
1415 8 : init.mLoaded = aLoaded;
1416 8 : init.mTotal = (aTotal == -1) ? 0 : aTotal;
1417 :
1418 8 : const nsAString& typeString = ProgressEventTypeStrings[(uint8_t)aType];
1419 : RefPtr<ProgressEvent> event =
1420 16 : ProgressEvent::Constructor(aTarget, typeString, init);
1421 8 : event->SetTrusted(true);
1422 :
1423 8 : DispatchOrStoreEvent(aTarget, event);
1424 :
1425 8 : if (aType == ProgressEventType::progress) {
1426 2 : mInLoadProgressEvent = false;
1427 :
1428 : // clear chunked responses after every progress event
1429 4 : if (mResponseType == XMLHttpRequestResponseType::Moz_chunked_text ||
1430 2 : mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
1431 0 : mResponseBody.Truncate();
1432 0 : TruncateResponseText();
1433 0 : mResultArrayBuffer = nullptr;
1434 0 : mArrayBufferBuilder.reset();
1435 : }
1436 : }
1437 :
1438 : // If we're sending a load, error, timeout or abort event, then
1439 : // also dispatch the subsequent loadend event.
1440 8 : if (aType == ProgressEventType::load || aType == ProgressEventType::error ||
1441 6 : aType == ProgressEventType::timeout || aType == ProgressEventType::abort) {
1442 2 : DispatchProgressEvent(aTarget, ProgressEventType::loadend, aLoaded, aTotal);
1443 : }
1444 : }
1445 :
1446 : void
1447 17 : XMLHttpRequestMainThread::DispatchOrStoreEvent(DOMEventTargetHelper* aTarget,
1448 : Event* aEvent)
1449 : {
1450 17 : MOZ_ASSERT(aTarget);
1451 17 : MOZ_ASSERT(aEvent);
1452 :
1453 17 : if (mEventDispatchingSuspended) {
1454 0 : PendingEvent* event = mPendingEvents.AppendElement();
1455 0 : event->mTarget = aTarget;
1456 0 : event->mEvent = aEvent;
1457 0 : return;
1458 : }
1459 :
1460 17 : aTarget->DispatchDOMEvent(nullptr, aEvent, nullptr, nullptr);
1461 : }
1462 :
1463 : void
1464 0 : XMLHttpRequestMainThread::SuspendEventDispatching()
1465 : {
1466 0 : MOZ_ASSERT(!mEventDispatchingSuspended);
1467 0 : mEventDispatchingSuspended = true;
1468 0 : }
1469 :
1470 : void
1471 0 : XMLHttpRequestMainThread::ResumeEventDispatching()
1472 : {
1473 0 : MOZ_ASSERT(mEventDispatchingSuspended);
1474 0 : mEventDispatchingSuspended = false;
1475 :
1476 0 : nsTArray<PendingEvent> pendingEvents;
1477 0 : pendingEvents.SwapElements(mPendingEvents);
1478 :
1479 0 : for (uint32_t i = 0; i < pendingEvents.Length(); ++i) {
1480 0 : pendingEvents[i].mTarget->
1481 0 : DispatchDOMEvent(nullptr, pendingEvents[i].mEvent, nullptr, nullptr);
1482 : }
1483 0 : }
1484 :
1485 : already_AddRefed<nsIHttpChannel>
1486 24 : XMLHttpRequestMainThread::GetCurrentHttpChannel()
1487 : {
1488 48 : nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
1489 48 : return httpChannel.forget();
1490 : }
1491 :
1492 : already_AddRefed<nsIJARChannel>
1493 0 : XMLHttpRequestMainThread::GetCurrentJARChannel()
1494 : {
1495 0 : nsCOMPtr<nsIJARChannel> appChannel = do_QueryInterface(mChannel);
1496 0 : return appChannel.forget();
1497 : }
1498 :
1499 : bool
1500 4 : XMLHttpRequestMainThread::IsSystemXHR() const
1501 : {
1502 4 : return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
1503 : }
1504 :
1505 : bool
1506 2 : XMLHttpRequestMainThread::InUploadPhase() const
1507 : {
1508 : // We're in the upload phase while our state is State::opened.
1509 2 : return mState == State::opened;
1510 : }
1511 :
1512 : NS_IMETHODIMP
1513 0 : XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsACString& aUrl,
1514 : bool aAsync, const nsAString& aUsername,
1515 : const nsAString& aPassword, uint8_t optional_argc)
1516 : {
1517 0 : return Open(aMethod, aUrl, optional_argc > 0 ? aAsync : true,
1518 0 : aUsername, aPassword);
1519 : }
1520 :
1521 : // This case is hit when the async parameter is outright omitted, which
1522 : // should set it to true (and the username and password to null).
1523 : void
1524 0 : XMLHttpRequestMainThread::Open(const nsACString& aMethod, const nsAString& aUrl,
1525 : ErrorResult& aRv)
1526 : {
1527 0 : Open(aMethod, aUrl, true, NullString(), NullString(), aRv);
1528 0 : }
1529 :
1530 : // This case is hit when the async parameter is specified, even if the
1531 : // JS value was "undefined" (which due to legacy reasons should be
1532 : // treated as true, which is how it will already be passed in here).
1533 : void
1534 3 : XMLHttpRequestMainThread::Open(const nsACString& aMethod,
1535 : const nsAString& aUrl,
1536 : bool aAsync,
1537 : const nsAString& aUsername,
1538 : const nsAString& aPassword,
1539 : ErrorResult& aRv)
1540 : {
1541 3 : nsresult rv = Open(aMethod, NS_ConvertUTF16toUTF8(aUrl), aAsync, aUsername, aPassword);
1542 3 : if (NS_FAILED(rv)) {
1543 0 : aRv.Throw(rv);
1544 : }
1545 3 : }
1546 :
1547 : nsresult
1548 3 : XMLHttpRequestMainThread::Open(const nsACString& aMethod,
1549 : const nsACString& aUrl,
1550 : bool aAsync,
1551 : const nsAString& aUsername,
1552 : const nsAString& aPassword) {
1553 : // Gecko-specific
1554 3 : if (!aAsync && !DontWarnAboutSyncXHR() && GetOwner() &&
1555 0 : GetOwner()->GetExtantDoc()) {
1556 0 : GetOwner()->GetExtantDoc()->WarnOnceAbout(nsIDocument::eSyncXMLHttpRequest);
1557 : }
1558 :
1559 3 : Telemetry::Accumulate(Telemetry::XMLHTTPREQUEST_ASYNC_OR_SYNC, aAsync ? 0 : 1);
1560 :
1561 : // Step 1
1562 6 : nsCOMPtr<nsIDocument> responsibleDocument = GetDocumentIfCurrent();
1563 3 : if (!responsibleDocument) {
1564 : // This could be because we're no longer current or because we're in some
1565 : // non-window context...
1566 3 : nsresult rv = CheckInnerWindowCorrectness();
1567 3 : if (NS_WARN_IF(NS_FAILED(rv))) {
1568 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
1569 : }
1570 : }
1571 3 : NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
1572 :
1573 : // Steps 2-4
1574 6 : nsAutoCString method;
1575 3 : nsresult rv = FetchUtil::GetValidRequestMethod(aMethod, method);
1576 3 : if (NS_WARN_IF(NS_FAILED(rv))) {
1577 0 : return rv;
1578 : }
1579 :
1580 : // Steps 5-6
1581 6 : nsCOMPtr<nsIURI> baseURI;
1582 3 : if (mBaseURI) {
1583 3 : baseURI = mBaseURI;
1584 0 : } else if (responsibleDocument) {
1585 0 : baseURI = responsibleDocument->GetBaseURI();
1586 : }
1587 6 : nsCOMPtr<nsIURI> parsedURL;
1588 3 : rv = NS_NewURI(getter_AddRefs(parsedURL), aUrl, nullptr, baseURI);
1589 3 : if (NS_FAILED(rv)) {
1590 0 : if (rv == NS_ERROR_MALFORMED_URI) {
1591 0 : return NS_ERROR_DOM_MALFORMED_URI;
1592 : }
1593 0 : return rv;
1594 : }
1595 3 : if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) {
1596 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
1597 : }
1598 :
1599 : // Step 7
1600 : // This is already handled by the other Open() method, which passes
1601 : // username and password in as NullStrings.
1602 :
1603 : // Step 8
1604 6 : nsAutoCString host;
1605 3 : parsedURL->GetHost(host);
1606 3 : if (!host.IsEmpty()) {
1607 6 : nsAutoCString userpass;
1608 3 : if (!aUsername.IsVoid()) {
1609 0 : CopyUTF16toUTF8(aUsername, userpass);
1610 : }
1611 3 : userpass.AppendLiteral(":");
1612 3 : if (!aPassword.IsVoid()) {
1613 0 : AppendUTF16toUTF8(aPassword, userpass);
1614 : }
1615 3 : parsedURL->SetUserPass(userpass);
1616 : }
1617 :
1618 : // Step 9
1619 3 : if (!aAsync && HasOrHasHadOwner() && (mTimeoutMilliseconds ||
1620 0 : mResponseType != XMLHttpRequestResponseType::_empty)) {
1621 0 : if (mTimeoutMilliseconds) {
1622 0 : LogMessage("TimeoutSyncXHRWarning", GetOwner());
1623 : }
1624 0 : if (mResponseType != XMLHttpRequestResponseType::_empty) {
1625 0 : LogMessage("ResponseTypeSyncXHRWarning", GetOwner());
1626 : }
1627 0 : return NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC;
1628 : }
1629 :
1630 : // Step 10
1631 3 : CloseRequest();
1632 :
1633 : // Step 11
1634 : // timeouts are handled without a flag
1635 3 : mFlagSend = false;
1636 3 : mRequestMethod.Assign(method);
1637 3 : mRequestURL = parsedURL;
1638 3 : mFlagSynchronous = !aAsync;
1639 3 : mAuthorRequestHeaders.Clear();
1640 3 : ResetResponse();
1641 :
1642 : // Gecko-specific
1643 3 : mFlagHadUploadListenersOnSend = false;
1644 3 : mFlagAborted = false;
1645 3 : mFlagTimedOut = false;
1646 :
1647 : // Per spec we should only create the channel on send(), but we have internal
1648 : // code that relies on the channel being created now, and that code is not
1649 : // always IsSystemXHR(). However, we're not supposed to throw channel-creation
1650 : // errors during open(), so we silently ignore those here.
1651 3 : CreateChannel();
1652 :
1653 : // Step 12
1654 3 : if (mState != State::opened) {
1655 3 : mState = State::opened;
1656 3 : FireReadystatechangeEvent();
1657 : }
1658 :
1659 3 : return NS_OK;
1660 : }
1661 :
1662 : void
1663 0 : XMLHttpRequestMainThread::SetOriginAttributes(const OriginAttributesDictionary& aAttrs)
1664 : {
1665 0 : MOZ_ASSERT((mState == State::opened) && !mFlagSend);
1666 :
1667 0 : OriginAttributes attrs(aAttrs);
1668 :
1669 0 : nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
1670 0 : MOZ_ASSERT(loadInfo);
1671 0 : if (loadInfo) {
1672 0 : loadInfo->SetOriginAttributes(attrs);
1673 : }
1674 0 : }
1675 :
1676 : void
1677 2 : XMLHttpRequestMainThread::PopulateNetworkInterfaceId()
1678 : {
1679 2 : if (mNetworkInterfaceId.IsEmpty()) {
1680 4 : return;
1681 : }
1682 0 : nsCOMPtr<nsIHttpChannelInternal> channel(do_QueryInterface(mChannel));
1683 0 : if (!channel) {
1684 0 : return;
1685 : }
1686 0 : DebugOnly<nsresult> rv = channel->SetNetworkInterfaceId(mNetworkInterfaceId);
1687 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
1688 : }
1689 :
1690 : /*
1691 : * "Copy" from a stream.
1692 : */
1693 : nsresult
1694 3 : XMLHttpRequestMainThread::StreamReaderFunc(nsIInputStream* in,
1695 : void* closure,
1696 : const char* fromRawSegment,
1697 : uint32_t toOffset,
1698 : uint32_t count,
1699 : uint32_t *writeCount)
1700 : {
1701 3 : XMLHttpRequestMainThread* xmlHttpRequest = static_cast<XMLHttpRequestMainThread*>(closure);
1702 3 : if (!xmlHttpRequest || !writeCount) {
1703 0 : NS_WARNING("XMLHttpRequest cannot read from stream: no closure or writeCount");
1704 0 : return NS_ERROR_FAILURE;
1705 : }
1706 :
1707 3 : nsresult rv = NS_OK;
1708 :
1709 3 : if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Blob) {
1710 0 : xmlHttpRequest->MaybeCreateBlobStorage();
1711 0 : rv = xmlHttpRequest->mBlobStorage->Append(fromRawSegment, count);
1712 3 : } else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_blob) {
1713 0 : if (!xmlHttpRequest->mBlobSet) {
1714 0 : xmlHttpRequest->mBlobSet = new BlobSet();
1715 : }
1716 0 : rv = xmlHttpRequest->mBlobSet->AppendVoidPtr(fromRawSegment, count);
1717 : // Clear the cache so that the blob size is updated.
1718 0 : xmlHttpRequest->mResponseBlob = nullptr;
1719 3 : } else if ((xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
1720 3 : !xmlHttpRequest->mIsMappedArrayBuffer) ||
1721 3 : xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer) {
1722 : // get the initial capacity to something reasonable to avoid a bunch of reallocs right
1723 : // at the start
1724 0 : if (xmlHttpRequest->mArrayBufferBuilder.capacity() == 0)
1725 0 : xmlHttpRequest->mArrayBufferBuilder.setCapacity(std::max(count, XML_HTTP_REQUEST_ARRAYBUFFER_MIN_SIZE));
1726 :
1727 0 : if (NS_WARN_IF(!xmlHttpRequest->mArrayBufferBuilder.append(
1728 : reinterpret_cast<const uint8_t*>(fromRawSegment),
1729 : count,
1730 : XML_HTTP_REQUEST_ARRAYBUFFER_MAX_GROWTH))) {
1731 0 : return NS_ERROR_OUT_OF_MEMORY;
1732 : }
1733 :
1734 3 : } else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::_empty &&
1735 0 : xmlHttpRequest->mResponseXML) {
1736 : // Copy for our own use
1737 0 : if (!xmlHttpRequest->mResponseBody.Append(fromRawSegment, count, fallible)) {
1738 0 : return NS_ERROR_OUT_OF_MEMORY;
1739 : }
1740 6 : } else if (xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::_empty ||
1741 3 : xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Text ||
1742 0 : xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Json ||
1743 0 : xmlHttpRequest->mResponseType == XMLHttpRequestResponseType::Moz_chunked_text) {
1744 3 : NS_ASSERTION(!xmlHttpRequest->mResponseXML,
1745 : "We shouldn't be parsing a doc here");
1746 3 : rv = xmlHttpRequest->AppendToResponseText(fromRawSegment, count);
1747 3 : if (NS_WARN_IF(NS_FAILED(rv))) {
1748 0 : return rv;
1749 : }
1750 : }
1751 :
1752 3 : if (xmlHttpRequest->mFlagParseBody) {
1753 : // Give the same data to the parser.
1754 :
1755 : // We need to wrap the data in a new lightweight stream and pass that
1756 : // to the parser, because calling ReadSegments() recursively on the same
1757 : // stream is not supported.
1758 0 : nsCOMPtr<nsIInputStream> copyStream;
1759 0 : rv = NS_NewByteInputStream(getter_AddRefs(copyStream), fromRawSegment, count);
1760 :
1761 0 : if (NS_SUCCEEDED(rv) && xmlHttpRequest->mXMLParserStreamListener) {
1762 0 : NS_ASSERTION(copyStream, "NS_NewByteInputStream lied");
1763 : nsresult parsingResult = xmlHttpRequest->mXMLParserStreamListener
1764 0 : ->OnDataAvailable(xmlHttpRequest->mChannel,
1765 : xmlHttpRequest->mContext,
1766 0 : copyStream, toOffset, count);
1767 :
1768 : // No use to continue parsing if we failed here, but we
1769 : // should still finish reading the stream
1770 0 : if (NS_FAILED(parsingResult)) {
1771 0 : xmlHttpRequest->mFlagParseBody = false;
1772 : }
1773 : }
1774 : }
1775 :
1776 3 : if (NS_SUCCEEDED(rv)) {
1777 3 : *writeCount = count;
1778 : } else {
1779 0 : *writeCount = 0;
1780 : }
1781 :
1782 3 : return rv;
1783 : }
1784 :
1785 : namespace {
1786 :
1787 : nsresult
1788 0 : GetLocalFileFromChannel(nsIRequest* aRequest, nsIFile** aFile)
1789 : {
1790 0 : MOZ_ASSERT(aRequest);
1791 0 : MOZ_ASSERT(aFile);
1792 :
1793 0 : *aFile = nullptr;
1794 :
1795 0 : nsCOMPtr<nsIFileChannel> fc = do_QueryInterface(aRequest);
1796 0 : if (!fc) {
1797 0 : return NS_OK;
1798 : }
1799 :
1800 0 : nsCOMPtr<nsIFile> file;
1801 0 : nsresult rv = fc->GetFile(getter_AddRefs(file));
1802 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1803 0 : return rv;
1804 : }
1805 :
1806 0 : file.forget(aFile);
1807 0 : return NS_OK;
1808 : }
1809 :
1810 : nsresult
1811 0 : DummyStreamReaderFunc(nsIInputStream* aInputStream,
1812 : void* aClosure,
1813 : const char* aFromRawSegment,
1814 : uint32_t aToOffset,
1815 : uint32_t aCount,
1816 : uint32_t* aWriteCount)
1817 : {
1818 0 : *aWriteCount = aCount;
1819 0 : return NS_OK;
1820 : }
1821 :
1822 : class FileCreationHandler final : public PromiseNativeHandler
1823 : {
1824 : public:
1825 : NS_DECL_ISUPPORTS
1826 :
1827 : static void
1828 0 : Create(Promise* aPromise, XMLHttpRequestMainThread* aXHR)
1829 : {
1830 0 : MOZ_ASSERT(aPromise);
1831 :
1832 0 : RefPtr<FileCreationHandler> handler = new FileCreationHandler(aXHR);
1833 0 : aPromise->AppendNativeHandler(handler);
1834 0 : }
1835 :
1836 : void
1837 0 : ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
1838 : {
1839 0 : if (NS_WARN_IF(!aValue.isObject())) {
1840 0 : mXHR->LocalFileToBlobCompleted(nullptr);
1841 0 : return;
1842 : }
1843 :
1844 0 : RefPtr<Blob> blob;
1845 0 : if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) {
1846 0 : mXHR->LocalFileToBlobCompleted(nullptr);
1847 0 : return;
1848 : }
1849 :
1850 0 : mXHR->LocalFileToBlobCompleted(blob);
1851 : }
1852 :
1853 : void
1854 0 : RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
1855 : {
1856 0 : mXHR->LocalFileToBlobCompleted(nullptr);
1857 0 : }
1858 :
1859 : private:
1860 0 : explicit FileCreationHandler(XMLHttpRequestMainThread* aXHR)
1861 0 : : mXHR(aXHR)
1862 : {
1863 0 : MOZ_ASSERT(aXHR);
1864 0 : }
1865 :
1866 0 : ~FileCreationHandler() = default;
1867 :
1868 : RefPtr<XMLHttpRequestMainThread> mXHR;
1869 : };
1870 :
1871 0 : NS_IMPL_ISUPPORTS0(FileCreationHandler)
1872 :
1873 : } // namespace
1874 :
1875 : void
1876 0 : XMLHttpRequestMainThread::LocalFileToBlobCompleted(Blob* aBlob)
1877 : {
1878 0 : MOZ_ASSERT(mState != State::done);
1879 :
1880 0 : mResponseBlob = aBlob;
1881 0 : mBlobStorage = nullptr;
1882 0 : mBlobSet = nullptr;
1883 0 : NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
1884 :
1885 0 : ChangeStateToDone();
1886 0 : }
1887 :
1888 : NS_IMETHODIMP
1889 2 : XMLHttpRequestMainThread::OnDataAvailable(nsIRequest *request,
1890 : nsISupports *ctxt,
1891 : nsIInputStream *inStr,
1892 : uint64_t sourceOffset,
1893 : uint32_t count)
1894 : {
1895 2 : NS_ENSURE_ARG_POINTER(inStr);
1896 :
1897 2 : MOZ_ASSERT(mContext.get() == ctxt,"start context different from OnDataAvailable context");
1898 :
1899 2 : mProgressSinceLastProgressEvent = true;
1900 2 : XMLHttpRequestBinding::ClearCachedResponseTextValue(this);
1901 :
1902 : nsresult rv;
1903 :
1904 4 : nsCOMPtr<nsIFile> localFile;
1905 4 : if ((mResponseType == XMLHttpRequestResponseType::Blob ||
1906 2 : mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
1907 0 : rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile));
1908 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1909 0 : return rv;
1910 : }
1911 :
1912 0 : if (localFile) {
1913 0 : mBlobStorage = nullptr;
1914 0 : mBlobSet = nullptr;
1915 0 : NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
1916 :
1917 : // The nsIStreamListener contract mandates us to read from the stream
1918 : // before returning.
1919 : uint32_t totalRead;
1920 : rv =
1921 0 : inStr->ReadSegments(DummyStreamReaderFunc, nullptr, count, &totalRead);
1922 0 : NS_ENSURE_SUCCESS(rv, rv);
1923 :
1924 0 : ChangeState(State::loading);
1925 :
1926 : // Cancel() must be called with an error. We use
1927 : // NS_ERROR_FILE_ALREADY_EXISTS to know that we've aborted the operation
1928 : // just because we can retrieve the File from the channel directly.
1929 0 : return request->Cancel(NS_ERROR_FILE_ALREADY_EXISTS);
1930 : }
1931 : }
1932 :
1933 : uint32_t totalRead;
1934 : rv = inStr->ReadSegments(XMLHttpRequestMainThread::StreamReaderFunc,
1935 2 : (void*)this, count, &totalRead);
1936 2 : NS_ENSURE_SUCCESS(rv, rv);
1937 :
1938 : // Fire the first progress event/loading state change
1939 2 : if (mState != State::loading) {
1940 2 : ChangeState(State::loading);
1941 2 : if (!mFlagSynchronous) {
1942 2 : DispatchProgressEvent(this, ProgressEventType::progress,
1943 2 : mLoadTransferred, mLoadTotal);
1944 : }
1945 2 : mProgressSinceLastProgressEvent = false;
1946 : }
1947 :
1948 2 : if (!mFlagSynchronous && !mProgressTimerIsActive) {
1949 0 : StartProgressEventTimer();
1950 : }
1951 :
1952 2 : return NS_OK;
1953 : }
1954 :
1955 : NS_IMETHODIMP
1956 2 : XMLHttpRequestMainThread::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
1957 : {
1958 4 : AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStartRequest", NETWORK);
1959 :
1960 2 : nsresult rv = NS_OK;
1961 2 : if (!mFirstStartRequestSeen && mRequestObserver) {
1962 0 : mFirstStartRequestSeen = true;
1963 0 : mRequestObserver->OnStartRequest(request, ctxt);
1964 : }
1965 :
1966 2 : if (request != mChannel) {
1967 : // Can this still happen?
1968 0 : return NS_OK;
1969 : }
1970 :
1971 : // Don't do anything if we have been aborted
1972 2 : if (mState == State::unsent) {
1973 0 : return NS_OK;
1974 : }
1975 :
1976 : /* Apparently, Abort() should set State::unsent. See bug 361773.
1977 : XHR2 spec says this is correct. */
1978 2 : if (mFlagAborted) {
1979 0 : NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
1980 :
1981 0 : return NS_ERROR_UNEXPECTED;
1982 : }
1983 :
1984 : // Don't do anything if we have timed out.
1985 2 : if (mFlagTimedOut) {
1986 0 : return NS_OK;
1987 : }
1988 :
1989 4 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
1990 2 : NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
1991 :
1992 : nsresult status;
1993 2 : request->GetStatus(&status);
1994 2 : if (mErrorLoad == ErrorType::eOK && NS_FAILED(status)) {
1995 0 : mErrorLoad = ErrorType::eRequest;
1996 : }
1997 :
1998 : // Upload phase is now over. If we were uploading anything,
1999 : // stop the timer and fire any final progress events.
2000 2 : if (mUpload && !mUploadComplete && mErrorLoad == ErrorType::eOK && !mFlagSynchronous) {
2001 0 : StopProgressEventTimer();
2002 :
2003 0 : mUploadTransferred = mUploadTotal;
2004 :
2005 0 : if (mProgressSinceLastProgressEvent) {
2006 0 : DispatchProgressEvent(mUpload, ProgressEventType::progress,
2007 0 : mUploadTransferred, mUploadTotal);
2008 0 : mProgressSinceLastProgressEvent = false;
2009 : }
2010 :
2011 0 : mUploadComplete = true;
2012 0 : DispatchProgressEvent(mUpload, ProgressEventType::load,
2013 0 : mUploadTotal, mUploadTotal);
2014 : }
2015 :
2016 2 : mContext = ctxt;
2017 2 : mFlagParseBody = true;
2018 2 : ChangeState(State::headers_received);
2019 :
2020 2 : ResetResponse();
2021 :
2022 2 : if (!mOverrideMimeType.IsEmpty()) {
2023 0 : channel->SetContentType(NS_ConvertUTF16toUTF8(mOverrideMimeType));
2024 : }
2025 :
2026 : // Fallback to 'application/octet-stream'
2027 4 : nsAutoCString type;
2028 2 : channel->GetContentType(type);
2029 2 : if (type.Equals(UNKNOWN_CONTENT_TYPE)) {
2030 0 : channel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
2031 : }
2032 :
2033 2 : DetectCharset();
2034 :
2035 : // Set up arraybuffer
2036 2 : if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
2037 0 : NS_SUCCEEDED(status)) {
2038 0 : if (mIsMappedArrayBuffer) {
2039 0 : nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(channel);
2040 0 : if (jarChannel) {
2041 0 : nsCOMPtr<nsIURI> uri;
2042 0 : rv = channel->GetURI(getter_AddRefs(uri));
2043 0 : if (NS_SUCCEEDED(rv)) {
2044 0 : nsAutoCString file;
2045 0 : nsAutoCString scheme;
2046 0 : uri->GetScheme(scheme);
2047 0 : if (scheme.LowerCaseEqualsLiteral("jar")) {
2048 0 : nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri);
2049 0 : if (jarURI) {
2050 0 : jarURI->GetJAREntry(file);
2051 : }
2052 : }
2053 0 : nsCOMPtr<nsIFile> jarFile;
2054 0 : jarChannel->GetJarFile(getter_AddRefs(jarFile));
2055 0 : if (!jarFile) {
2056 0 : mIsMappedArrayBuffer = false;
2057 : } else {
2058 0 : rv = mArrayBufferBuilder.mapToFileInPackage(file, jarFile);
2059 : // This can happen legitimately if there are compressed files
2060 : // in the jarFile. See bug #1357219. No need to warn on the error.
2061 0 : if (NS_FAILED(rv)) {
2062 0 : mIsMappedArrayBuffer = false;
2063 : } else {
2064 0 : channel->SetContentType(NS_LITERAL_CSTRING("application/mem-mapped"));
2065 : }
2066 : }
2067 : }
2068 : }
2069 : }
2070 : // If memory mapping failed, mIsMappedArrayBuffer would be set to false,
2071 : // and we want it fallback to the malloc way.
2072 0 : if (!mIsMappedArrayBuffer) {
2073 : int64_t contentLength;
2074 0 : rv = channel->GetContentLength(&contentLength);
2075 0 : if (NS_SUCCEEDED(rv) &&
2076 0 : contentLength > 0 &&
2077 0 : contentLength < XML_HTTP_REQUEST_MAX_CONTENT_LENGTH_PREALLOCATE) {
2078 0 : mArrayBufferBuilder.setCapacity(static_cast<int32_t>(contentLength));
2079 : }
2080 : }
2081 : }
2082 :
2083 : // Set up responseXML
2084 4 : bool parseBody = mResponseType == XMLHttpRequestResponseType::_empty ||
2085 4 : mResponseType == XMLHttpRequestResponseType::Document;
2086 4 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2087 2 : if (parseBody && httpChannel) {
2088 0 : nsAutoCString method;
2089 0 : rv = httpChannel->GetRequestMethod(method);
2090 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2091 0 : parseBody = !method.EqualsLiteral("HEAD");
2092 : }
2093 :
2094 2 : mIsHtml = false;
2095 2 : mWarnAboutSyncHtml = false;
2096 2 : if (parseBody && NS_SUCCEEDED(status)) {
2097 : // We can gain a huge performance win by not even trying to
2098 : // parse non-XML data. This also protects us from the situation
2099 : // where we have an XML document and sink, but HTML (or other)
2100 : // parser, which can produce unreliable results.
2101 0 : nsAutoCString type;
2102 0 : channel->GetContentType(type);
2103 :
2104 0 : if ((mResponseType == XMLHttpRequestResponseType::Document) &&
2105 0 : type.EqualsLiteral("text/html")) {
2106 : // HTML parsing is only supported for responseType == "document" to
2107 : // avoid running the parser and, worse, populating responseXML for
2108 : // legacy users of XHR who use responseType == "" for retrieving the
2109 : // responseText of text/html resources. This legacy case is so common
2110 : // that it's not useful to emit a warning about it.
2111 0 : if (mFlagSynchronous) {
2112 : // We don't make cool new features available in the bad synchronous
2113 : // mode. The synchronous mode is for legacy only.
2114 0 : mWarnAboutSyncHtml = true;
2115 0 : mFlagParseBody = false;
2116 : } else {
2117 0 : mIsHtml = true;
2118 : }
2119 0 : } else if (!(type.EqualsLiteral("text/xml") ||
2120 0 : type.EqualsLiteral("application/xml") ||
2121 0 : type.RFind("+xml", true, -1, 4) != kNotFound)) {
2122 : // Follow https://xhr.spec.whatwg.org/
2123 : // If final MIME type is not null, text/html, text/xml, application/xml,
2124 : // or does not end in +xml, return null.
2125 0 : mFlagParseBody = false;
2126 : }
2127 : } else {
2128 : // The request failed, so we shouldn't be parsing anyway
2129 2 : mFlagParseBody = false;
2130 : }
2131 :
2132 2 : if (mFlagParseBody) {
2133 0 : nsCOMPtr<nsIURI> baseURI, docURI;
2134 0 : rv = mChannel->GetURI(getter_AddRefs(docURI));
2135 0 : NS_ENSURE_SUCCESS(rv, rv);
2136 0 : baseURI = docURI;
2137 :
2138 0 : nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
2139 0 : nsCOMPtr<nsIURI> chromeXHRDocURI, chromeXHRDocBaseURI;
2140 0 : if (doc) {
2141 0 : chromeXHRDocURI = doc->GetDocumentURI();
2142 0 : chromeXHRDocBaseURI = doc->GetBaseURI();
2143 : } else {
2144 : // If we're no longer current, just kill the load, though it really should
2145 : // have been killed already.
2146 0 : if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) {
2147 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
2148 : }
2149 : }
2150 :
2151 : // Create an empty document from it.
2152 0 : const nsAString& emptyStr = EmptyString();
2153 0 : nsCOMPtr<nsIDOMDocument> responseDoc;
2154 0 : nsIGlobalObject* global = DOMEventTargetHelper::GetParentObject();
2155 :
2156 0 : nsCOMPtr<nsIPrincipal> requestingPrincipal;
2157 0 : rv = nsContentUtils::GetSecurityManager()->
2158 0 : GetChannelResultPrincipal(channel, getter_AddRefs(requestingPrincipal));
2159 0 : NS_ENSURE_SUCCESS(rv, rv);
2160 :
2161 0 : rv = NS_NewDOMDocument(getter_AddRefs(responseDoc),
2162 : emptyStr, emptyStr, nullptr, docURI,
2163 : baseURI, requestingPrincipal, true, global,
2164 0 : mIsHtml ? DocumentFlavorHTML :
2165 0 : DocumentFlavorLegacyGuess);
2166 0 : NS_ENSURE_SUCCESS(rv, rv);
2167 0 : mResponseXML = do_QueryInterface(responseDoc);
2168 0 : mResponseXML->SetChromeXHRDocURI(chromeXHRDocURI);
2169 0 : mResponseXML->SetChromeXHRDocBaseURI(chromeXHRDocBaseURI);
2170 :
2171 : // suppress parsing failure messages to console for statuses which
2172 : // can have empty bodies (see bug 884693).
2173 : uint32_t responseStatus;
2174 0 : if (NS_SUCCEEDED(GetStatus(&responseStatus)) &&
2175 0 : (responseStatus == 201 || responseStatus == 202 ||
2176 0 : responseStatus == 204 || responseStatus == 205 ||
2177 0 : responseStatus == 304)) {
2178 0 : mResponseXML->SetSuppressParserErrorConsoleMessages(true);
2179 : }
2180 :
2181 0 : if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
2182 0 : mResponseXML->ForceEnableXULXBL();
2183 : }
2184 :
2185 0 : nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
2186 0 : MOZ_ASSERT(loadInfo);
2187 0 : bool isCrossSite = false;
2188 0 : if (loadInfo) {
2189 0 : isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic;
2190 : }
2191 :
2192 0 : if (isCrossSite) {
2193 0 : nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
2194 0 : if (htmlDoc) {
2195 0 : htmlDoc->DisableCookieAccess();
2196 : }
2197 : }
2198 :
2199 0 : nsCOMPtr<nsIStreamListener> listener;
2200 0 : nsCOMPtr<nsILoadGroup> loadGroup;
2201 0 : channel->GetLoadGroup(getter_AddRefs(loadGroup));
2202 :
2203 : // suppress <parsererror> nodes on XML document parse failure, but only
2204 : // for non-privileged code (including Web Extensions). See bug 289714.
2205 0 : if (!IsSystemXHR()) {
2206 0 : mResponseXML->SetSuppressParserErrorElement(true);
2207 : }
2208 :
2209 0 : rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
2210 0 : nullptr, getter_AddRefs(listener),
2211 0 : !isCrossSite);
2212 0 : NS_ENSURE_SUCCESS(rv, rv);
2213 :
2214 : // the spec requires the response document.referrer to be the empty string
2215 0 : mResponseXML->SetReferrer(NS_LITERAL_CSTRING(""));
2216 :
2217 0 : mXMLParserStreamListener = listener;
2218 0 : rv = mXMLParserStreamListener->OnStartRequest(request, ctxt);
2219 0 : NS_ENSURE_SUCCESS(rv, rv);
2220 : }
2221 :
2222 : // Download phase beginning; start the progress event timer if necessary.
2223 2 : if (NS_SUCCEEDED(rv) && HasListenersFor(nsGkAtoms::onprogress)) {
2224 2 : StartProgressEventTimer();
2225 : }
2226 :
2227 2 : return NS_OK;
2228 : }
2229 :
2230 : NS_IMETHODIMP
2231 2 : XMLHttpRequestMainThread::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
2232 : {
2233 4 : AUTO_PROFILER_LABEL("XMLHttpRequestMainThread::OnStopRequest", NETWORK);
2234 :
2235 2 : if (request != mChannel) {
2236 : // Can this still happen?
2237 0 : return NS_OK;
2238 : }
2239 :
2240 2 : mWaitingForOnStopRequest = false;
2241 :
2242 2 : if (mRequestObserver) {
2243 0 : NS_ASSERTION(mFirstStartRequestSeen, "Inconsistent state!");
2244 0 : mFirstStartRequestSeen = false;
2245 0 : mRequestObserver->OnStopRequest(request, ctxt, status);
2246 : }
2247 :
2248 : // make sure to notify the listener if we were aborted
2249 : // XXX in fact, why don't we do the cleanup below in this case??
2250 : // State::unsent is for abort calls. See OnStartRequest above.
2251 2 : if (mState == State::unsent || mFlagTimedOut) {
2252 0 : if (mXMLParserStreamListener)
2253 0 : (void) mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
2254 0 : return NS_OK;
2255 : }
2256 :
2257 : // Is this good enough here?
2258 2 : if (mXMLParserStreamListener && mFlagParseBody) {
2259 0 : mXMLParserStreamListener->OnStopRequest(request, ctxt, status);
2260 : }
2261 :
2262 2 : mXMLParserStreamListener = nullptr;
2263 2 : mContext = nullptr;
2264 :
2265 2 : bool waitingForBlobCreation = false;
2266 :
2267 : // If we have this error, we have to deal with a file: URL + responseType =
2268 : // blob. We have this error because we canceled the channel. The status will
2269 : // be set to NS_OK.
2270 2 : if (status == NS_ERROR_FILE_ALREADY_EXISTS &&
2271 0 : (mResponseType == XMLHttpRequestResponseType::Blob ||
2272 0 : mResponseType == XMLHttpRequestResponseType::Moz_blob)) {
2273 0 : nsCOMPtr<nsIFile> file;
2274 0 : nsresult rv = GetLocalFileFromChannel(request, getter_AddRefs(file));
2275 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2276 0 : return rv;
2277 : }
2278 :
2279 0 : if (file) {
2280 0 : nsAutoCString contentType;
2281 0 : rv = mChannel->GetContentType(contentType);
2282 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2283 0 : return rv;
2284 : }
2285 :
2286 0 : ChromeFilePropertyBag bag;
2287 0 : bag.mType = NS_ConvertUTF8toUTF16(contentType);
2288 :
2289 0 : nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal();
2290 :
2291 0 : ErrorResult error;
2292 : RefPtr<Promise> promise =
2293 0 : FileCreatorHelper::CreateFile(global, file, bag, true, error);
2294 0 : if (NS_WARN_IF(error.Failed())) {
2295 0 : return error.StealNSResult();
2296 : }
2297 :
2298 0 : FileCreationHandler::Create(promise, this);
2299 0 : waitingForBlobCreation = true;
2300 0 : status = NS_OK;
2301 :
2302 0 : NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
2303 0 : NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
2304 : }
2305 : }
2306 :
2307 6 : if (NS_SUCCEEDED(status) &&
2308 4 : (mResponseType == XMLHttpRequestResponseType::Blob ||
2309 4 : mResponseType == XMLHttpRequestResponseType::Moz_blob) &&
2310 0 : !waitingForBlobCreation) {
2311 : // Smaller files may be written in cache map instead of separate files.
2312 : // Also, no-store response cannot be written in persistent cache.
2313 0 : nsAutoCString contentType;
2314 0 : mChannel->GetContentType(contentType);
2315 :
2316 0 : if (mResponseType == XMLHttpRequestResponseType::Blob) {
2317 : // mBlobStorage can be null if the channel is non-file non-cacheable
2318 : // and if the response length is zero.
2319 0 : MaybeCreateBlobStorage();
2320 0 : mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this);
2321 0 : waitingForBlobCreation = true;
2322 : } else {
2323 : // mBlobSet can be null if the channel is non-file non-cacheable
2324 : // and if the response length is zero.
2325 0 : if (!mBlobSet) {
2326 0 : mBlobSet = new BlobSet();
2327 : }
2328 :
2329 0 : ErrorResult error;
2330 0 : nsTArray<RefPtr<BlobImpl>> subImpls(mBlobSet->GetBlobImpls());
2331 : RefPtr<BlobImpl> blobImpl =
2332 0 : MultipartBlobImpl::Create(Move(subImpls),
2333 0 : NS_ConvertASCIItoUTF16(contentType),
2334 0 : error);
2335 0 : mBlobSet = nullptr;
2336 :
2337 0 : if (NS_WARN_IF(error.Failed())) {
2338 0 : return error.StealNSResult();
2339 : }
2340 :
2341 0 : mResponseBlob = Blob::Create(GetOwner(), blobImpl);
2342 : }
2343 :
2344 0 : NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
2345 0 : NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty");
2346 4 : } else if (NS_SUCCEEDED(status) &&
2347 2 : ((mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
2348 2 : !mIsMappedArrayBuffer) ||
2349 2 : mResponseType == XMLHttpRequestResponseType::Moz_chunked_arraybuffer)) {
2350 : // set the capacity down to the actual length, to realloc back
2351 : // down to the actual size
2352 0 : if (!mArrayBufferBuilder.setCapacity(mArrayBufferBuilder.length())) {
2353 : // this should never happen!
2354 0 : status = NS_ERROR_UNEXPECTED;
2355 : }
2356 : }
2357 :
2358 4 : nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
2359 2 : NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
2360 :
2361 2 : channel->SetNotificationCallbacks(nullptr);
2362 2 : mNotificationCallbacks = nullptr;
2363 2 : mChannelEventSink = nullptr;
2364 2 : mProgressEventSink = nullptr;
2365 :
2366 2 : mFlagSyncLooping = false;
2367 2 : mRequestSentTime = 0;
2368 :
2369 : // update our charset and decoder to match mResponseXML,
2370 : // before it is possibly nulled out
2371 2 : MatchCharsetAndDecoderToResponseDocument();
2372 :
2373 2 : if (NS_FAILED(status)) {
2374 : // This can happen if the server is unreachable. Other possible
2375 : // reasons are that the user leaves the page or hits the ESC key.
2376 :
2377 0 : mErrorLoad = ErrorType::eUnreachable;
2378 0 : mResponseXML = nullptr;
2379 : }
2380 :
2381 : // If we're uninitialized at this point, we encountered an error
2382 : // earlier and listeners have already been notified. Also we do
2383 : // not want to do this if we already completed.
2384 2 : if (mState == State::unsent || mState == State::done) {
2385 0 : return NS_OK;
2386 : }
2387 :
2388 2 : if (!mResponseXML) {
2389 2 : mFlagParseBody = false;
2390 :
2391 : //We postpone the 'done' until the creation of the Blob is completed.
2392 2 : if (!waitingForBlobCreation) {
2393 2 : ChangeStateToDone();
2394 : }
2395 :
2396 2 : return NS_OK;
2397 : }
2398 :
2399 0 : if (mIsHtml) {
2400 0 : NS_ASSERTION(!mFlagSyncLooping,
2401 : "We weren't supposed to support HTML parsing with XHR!");
2402 0 : nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(mResponseXML);
2403 : EventListenerManager* manager =
2404 0 : eventTarget->GetOrCreateListenerManager();
2405 0 : manager->AddEventListenerByType(new nsXHRParseEndListener(this),
2406 : kLiteralString_DOMContentLoaded,
2407 0 : TrustedEventsAtSystemGroupBubble());
2408 0 : return NS_OK;
2409 : } else {
2410 0 : mFlagParseBody = false;
2411 : }
2412 :
2413 : // We might have been sent non-XML data. If that was the case,
2414 : // we should null out the document member. The idea in this
2415 : // check here is that if there is no document element it is not
2416 : // an XML document. We might need a fancier check...
2417 0 : if (!mResponseXML->GetRootElement()) {
2418 0 : mErrorParsingXML = true;
2419 0 : mResponseXML = nullptr;
2420 : }
2421 0 : ChangeStateToDone();
2422 0 : return NS_OK;
2423 : }
2424 :
2425 : void
2426 0 : XMLHttpRequestMainThread::OnBodyParseEnd()
2427 : {
2428 0 : mFlagParseBody = false;
2429 0 : ChangeStateToDone();
2430 0 : }
2431 :
2432 : void
2433 2 : XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument()
2434 : {
2435 2 : if (mResponseXML && mResponseCharset != mResponseXML->GetDocumentCharacterSet()) {
2436 0 : mResponseCharset = mResponseXML->GetDocumentCharacterSet();
2437 0 : TruncateResponseText();
2438 0 : mResponseBodyDecodedPos = 0;
2439 0 : mDecoder = mResponseCharset->NewDecoderWithBOMRemoval();
2440 : }
2441 2 : }
2442 :
2443 : void
2444 2 : XMLHttpRequestMainThread::ChangeStateToDone()
2445 : {
2446 2 : StopProgressEventTimer();
2447 :
2448 2 : MOZ_ASSERT(!mFlagParseBody,
2449 : "ChangeStateToDone() called before async HTML parsing is done.");
2450 :
2451 2 : mFlagSend = false;
2452 :
2453 2 : if (mTimeoutTimer) {
2454 0 : mTimeoutTimer->Cancel();
2455 : }
2456 :
2457 : // Per spec, fire the last download progress event, if any,
2458 : // before readystatechange=4/done. (Note that 0-sized responses
2459 : // will have not sent a progress event yet, so one must be sent here).
2460 4 : if (!mFlagSynchronous &&
2461 4 : (!mLoadTransferred || mProgressSinceLastProgressEvent)) {
2462 0 : DispatchProgressEvent(this, ProgressEventType::progress,
2463 0 : mLoadTransferred, mLoadTotal);
2464 0 : mProgressSinceLastProgressEvent = false;
2465 : }
2466 :
2467 : // Per spec, fire readystatechange=4/done before final error events.
2468 2 : ChangeState(State::done, true);
2469 :
2470 : // Per spec, if we failed in the upload phase, fire a final error
2471 : // and loadend events for the upload after readystatechange=4/done.
2472 2 : if (!mFlagSynchronous && mUpload && !mUploadComplete) {
2473 0 : DispatchProgressEvent(mUpload, ProgressEventType::error, 0, -1);
2474 : }
2475 :
2476 : // Per spec, fire download's load/error and loadend events after
2477 : // readystatechange=4/done (and of course all upload events).
2478 2 : if (mErrorLoad != ErrorType::eOK) {
2479 0 : DispatchProgressEvent(this, ProgressEventType::error, 0, -1);
2480 : } else {
2481 2 : DispatchProgressEvent(this, ProgressEventType::load,
2482 2 : mLoadTransferred, mLoadTotal);
2483 : }
2484 :
2485 2 : if (mErrorLoad != ErrorType::eOK) {
2486 : // By nulling out channel here we make it so that Send() can test
2487 : // for that and throw. Also calling the various status
2488 : // methods/members will not throw.
2489 : // This matches what IE does.
2490 0 : mChannel = nullptr;
2491 : }
2492 2 : }
2493 :
2494 : nsresult
2495 3 : XMLHttpRequestMainThread::CreateChannel()
2496 : {
2497 : // When we are called from JS we can find the load group for the page,
2498 : // and add ourselves to it. This way any pending requests
2499 : // will be automatically aborted if the user leaves the page.
2500 6 : nsCOMPtr<nsILoadGroup> loadGroup = GetLoadGroup();
2501 :
2502 : nsSecurityFlags secFlags;
2503 : nsLoadFlags loadFlags = nsIRequest::LOAD_BACKGROUND |
2504 3 : nsIChannel::LOAD_CLASSIFY_URI;
2505 3 : if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
2506 : // When chrome is loading we want to make sure to sandbox any potential
2507 : // result document. We also want to allow cross-origin loads.
2508 3 : secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL |
2509 : nsILoadInfo::SEC_SANDBOXED;
2510 0 : } else if (IsSystemXHR()) {
2511 : // For pages that have appropriate permissions, we want to still allow
2512 : // cross-origin loads, but make sure that the any potential result
2513 : // documents get the same principal as the loader.
2514 0 : secFlags = nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
2515 : nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
2516 0 : loadFlags |= nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
2517 : } else {
2518 : // Otherwise use CORS. Again, make sure that potential result documents
2519 : // use the same principal as the loader.
2520 0 : secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
2521 : nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
2522 : }
2523 :
2524 3 : if (mIsAnon) {
2525 0 : secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
2526 : }
2527 :
2528 : // Use the responsibleDocument if we have it, except for dedicated workers
2529 : // where it will be the parent document, which is not the one we want to use.
2530 : nsresult rv;
2531 6 : nsCOMPtr<nsIDocument> responsibleDocument = GetDocumentIfCurrent();
2532 3 : if (responsibleDocument && responsibleDocument->NodePrincipal() == mPrincipal) {
2533 0 : rv = NS_NewChannel(getter_AddRefs(mChannel),
2534 : mRequestURL,
2535 : responsibleDocument,
2536 : secFlags,
2537 : nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
2538 : loadGroup,
2539 : nullptr, // aCallbacks
2540 0 : loadFlags);
2541 : } else {
2542 : // Otherwise use the principal.
2543 6 : rv = NS_NewChannel(getter_AddRefs(mChannel),
2544 : mRequestURL,
2545 : mPrincipal,
2546 : secFlags,
2547 : nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST,
2548 : loadGroup,
2549 : nullptr, // aCallbacks
2550 3 : loadFlags);
2551 : }
2552 3 : NS_ENSURE_SUCCESS(rv, rv);
2553 :
2554 6 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2555 3 : if (httpChannel) {
2556 0 : rv = httpChannel->SetRequestMethod(mRequestMethod);
2557 0 : NS_ENSURE_SUCCESS(rv, rv);
2558 :
2559 : // Set the initiator type
2560 0 : nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
2561 0 : if (timedChannel) {
2562 0 : timedChannel->SetInitiatorType(NS_LITERAL_STRING("xmlhttprequest"));
2563 : }
2564 : }
2565 :
2566 : // Using the provided principal as the triggeringPrincipal is fine, since we
2567 : // want to be able to access any of the origins that the principal has access
2568 : // to during the security checks, but we don't want a document to inherit an
2569 : // expanded principal, so in that case we need to select the principal in the
2570 : // expanded principal's whitelist that can load our URL as principalToInherit.
2571 6 : nsCOMPtr<nsIPrincipal> resultingDocumentPrincipal(mPrincipal);
2572 6 : nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(mPrincipal);
2573 3 : if (ep) {
2574 0 : nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist = nullptr;
2575 0 : ep->GetWhiteList(&whitelist);
2576 0 : if (!whitelist) {
2577 0 : return NS_ERROR_FAILURE;
2578 : }
2579 0 : MOZ_ASSERT(!(secFlags & nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS));
2580 0 : bool dataInherits = (secFlags &
2581 : (nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
2582 0 : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) != 0;
2583 0 : for (const auto& principal : *whitelist) {
2584 0 : if (NS_SUCCEEDED(principal->CheckMayLoad(mRequestURL, false, dataInherits))) {
2585 0 : resultingDocumentPrincipal = principal;
2586 0 : break;
2587 : }
2588 : }
2589 : }
2590 :
2591 6 : nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
2592 3 : if (loadInfo) {
2593 3 : rv = loadInfo->SetPrincipalToInherit(resultingDocumentPrincipal);
2594 3 : NS_ENSURE_SUCCESS(rv, rv);
2595 : }
2596 :
2597 3 : return NS_OK;
2598 : }
2599 :
2600 : void
2601 2 : XMLHttpRequestMainThread::MaybeLowerChannelPriority()
2602 : {
2603 2 : nsCOMPtr<nsIDocument> doc = GetDocumentIfCurrent();
2604 2 : if (!doc) {
2605 2 : return;
2606 : }
2607 :
2608 0 : AutoJSAPI jsapi;
2609 0 : if (!jsapi.Init(GetOwnerGlobal())) {
2610 0 : return;
2611 : }
2612 :
2613 0 : JSContext* cx = jsapi.cx();
2614 0 : nsAutoCString fileNameString;
2615 0 : if (!nsJSUtils::GetCallingLocation(cx, fileNameString)) {
2616 0 : return;
2617 : }
2618 :
2619 0 : if (!doc->IsScriptTracking(fileNameString)) {
2620 0 : return;
2621 : }
2622 :
2623 0 : nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(mChannel);
2624 0 : if (!p) {
2625 0 : return;
2626 : }
2627 :
2628 0 : p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
2629 : }
2630 :
2631 : nsresult
2632 2 : XMLHttpRequestMainThread::InitiateFetch(nsIInputStream* aUploadStream,
2633 : int64_t aUploadLength,
2634 : nsACString& aUploadContentType)
2635 : {
2636 : nsresult rv;
2637 :
2638 : // nsIRequest::LOAD_BACKGROUND prevents throbber from becoming active, which
2639 : // in turn keeps STOP button from becoming active. If the consumer passed in
2640 : // a progress event handler we must load with nsIRequest::LOAD_NORMAL or
2641 : // necko won't generate any progress notifications.
2642 4 : if (HasListenersFor(nsGkAtoms::onprogress) ||
2643 0 : (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress))) {
2644 : nsLoadFlags loadFlags;
2645 2 : mChannel->GetLoadFlags(&loadFlags);
2646 2 : loadFlags &= ~nsIRequest::LOAD_BACKGROUND;
2647 2 : loadFlags |= nsIRequest::LOAD_NORMAL;
2648 2 : mChannel->SetLoadFlags(loadFlags);
2649 : }
2650 :
2651 4 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2652 2 : if (httpChannel) {
2653 : // If the user hasn't overridden the Accept header, set it to */* per spec.
2654 0 : if (!mAuthorRequestHeaders.Has("accept")) {
2655 0 : mAuthorRequestHeaders.Set("accept", NS_LITERAL_CSTRING("*/*"));
2656 : }
2657 :
2658 0 : mAuthorRequestHeaders.ApplyToChannel(httpChannel);
2659 :
2660 0 : if (!IsSystemXHR()) {
2661 0 : nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
2662 0 : nsCOMPtr<nsIDocument> doc = owner ? owner->GetExtantDoc() : nullptr;
2663 0 : mozilla::net::ReferrerPolicy referrerPolicy = doc ?
2664 0 : doc->GetReferrerPolicy() : mozilla::net::RP_Unset;
2665 0 : nsContentUtils::SetFetchReferrerURIWithPolicy(mPrincipal, doc,
2666 0 : httpChannel, referrerPolicy);
2667 : }
2668 :
2669 : // Some extensions override the http protocol handler and provide their own
2670 : // implementation. The channels returned from that implementation don't
2671 : // always seem to implement the nsIUploadChannel2 interface, presumably
2672 : // because it's a new interface. Eventually we should remove this and simply
2673 : // require that http channels implement the new interface (see bug 529041).
2674 0 : nsCOMPtr<nsIUploadChannel2> uploadChannel2 = do_QueryInterface(httpChannel);
2675 0 : if (!uploadChannel2) {
2676 : nsCOMPtr<nsIConsoleService> consoleService =
2677 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
2678 0 : if (consoleService) {
2679 0 : consoleService->LogStringMessage(
2680 : u"Http channel implementation doesn't support nsIUploadChannel2. "
2681 : "An extension has supplied a non-functional http protocol handler. "
2682 0 : "This will break behavior and in future releases not work at all.");
2683 : }
2684 : }
2685 :
2686 0 : if (aUploadStream) {
2687 : // If necessary, wrap the stream in a buffered stream so as to guarantee
2688 : // support for our upload when calling ExplicitSetUploadStream.
2689 0 : nsCOMPtr<nsIInputStream> bufferedStream;
2690 0 : if (!NS_InputStreamIsBuffered(aUploadStream)) {
2691 0 : rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream),
2692 0 : aUploadStream, 4096);
2693 0 : NS_ENSURE_SUCCESS(rv, rv);
2694 :
2695 0 : aUploadStream = bufferedStream;
2696 : }
2697 :
2698 : // We want to use a newer version of the upload channel that won't
2699 : // ignore the necessary headers for an empty Content-Type.
2700 0 : nsCOMPtr<nsIUploadChannel2> uploadChannel2(do_QueryInterface(httpChannel));
2701 : // This assertion will fire if buggy extensions are installed
2702 0 : NS_ASSERTION(uploadChannel2, "http must support nsIUploadChannel2");
2703 0 : if (uploadChannel2) {
2704 0 : uploadChannel2->ExplicitSetUploadStream(aUploadStream,
2705 : aUploadContentType,
2706 : mUploadTotal, mRequestMethod,
2707 0 : false);
2708 : } else {
2709 : // The http channel doesn't support the new nsIUploadChannel2.
2710 : // Emulate it as best we can using nsIUploadChannel.
2711 0 : if (aUploadContentType.IsEmpty()) {
2712 0 : aUploadContentType.AssignLiteral("application/octet-stream");
2713 : }
2714 : nsCOMPtr<nsIUploadChannel> uploadChannel =
2715 0 : do_QueryInterface(httpChannel);
2716 0 : uploadChannel->SetUploadStream(aUploadStream, aUploadContentType,
2717 0 : mUploadTotal);
2718 : // Reset the method to its original value
2719 0 : rv = httpChannel->SetRequestMethod(mRequestMethod);
2720 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2721 : }
2722 : }
2723 : }
2724 :
2725 : // Due to the chrome-only XHR.channel API, we need a hacky way to set the
2726 : // SEC_COOKIES_INCLUDE *after* the channel has been has been created, since
2727 : // .withCredentials can be called after open() is called.
2728 : // Not doing this for privileged system XHRs since those don't use CORS.
2729 2 : if (!IsSystemXHR() && !mIsAnon && mFlagACwithCredentials) {
2730 0 : nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
2731 0 : if (loadInfo) {
2732 0 : static_cast<net::LoadInfo*>(loadInfo.get())->SetIncludeCookiesSecFlag();
2733 : }
2734 : }
2735 :
2736 : // We never let XHR be blocked by head CSS/JS loads to avoid potential
2737 : // deadlock where server generation of CSS/JS requires an XHR signal.
2738 4 : nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(mChannel));
2739 2 : if (cos) {
2740 0 : cos->AddClassFlags(nsIClassOfService::Unblocked);
2741 :
2742 : // Mark channel as urgent-start if the XHR is triggered by user input
2743 : // events.
2744 0 : if (EventStateManager::IsHandlingUserInput()) {
2745 0 : cos->AddClassFlags(nsIClassOfService::UrgentStart);
2746 : }
2747 : }
2748 :
2749 : // Disable Necko-internal response timeouts.
2750 : nsCOMPtr<nsIHttpChannelInternal>
2751 4 : internalHttpChannel(do_QueryInterface(mChannel));
2752 2 : if (internalHttpChannel) {
2753 0 : rv = internalHttpChannel->SetResponseTimeoutEnabled(false);
2754 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2755 : }
2756 :
2757 2 : if (!mIsAnon) {
2758 2 : AddLoadFlags(mChannel, nsIChannel::LOAD_EXPLICIT_CREDENTIALS);
2759 : }
2760 :
2761 : // Bypass the network cache in cases where it makes no sense:
2762 : // POST responses are always unique, and we provide no API that would
2763 : // allow our consumers to specify a "cache key" to access old POST
2764 : // responses, so they are not worth caching.
2765 2 : if (mRequestMethod.EqualsLiteral("POST")) {
2766 : AddLoadFlags(mChannel,
2767 : nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE |
2768 0 : nsIRequest::INHIBIT_CACHING);
2769 : } else {
2770 : // When we are sync loading, we need to bypass the local cache when it would
2771 : // otherwise block us waiting for exclusive access to the cache. If we don't
2772 : // do this, then we could dead lock in some cases (see bug 309424).
2773 : //
2774 : // Also don't block on the cache entry on async if it is busy - favoring parallelism
2775 : // over cache hit rate for xhr. This does not disable the cache everywhere -
2776 : // only in cases where more than one channel for the same URI is accessed
2777 : // simultanously.
2778 2 : AddLoadFlags(mChannel, nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY);
2779 : }
2780 :
2781 : // Since we expect XML data, set the type hint accordingly
2782 : // if the channel doesn't know any content type.
2783 : // This means that we always try to parse local files as XML
2784 : // ignoring return value, as this is not critical. Use text/xml as fallback
2785 : // MIME type.
2786 4 : nsAutoCString contentType;
2787 6 : if (NS_FAILED(mChannel->GetContentType(contentType)) ||
2788 4 : contentType.IsEmpty() ||
2789 2 : contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
2790 2 : mChannel->SetContentType(NS_LITERAL_CSTRING("text/xml"));
2791 : }
2792 :
2793 : // Set up the preflight if needed
2794 2 : if (!IsSystemXHR()) {
2795 0 : nsTArray<nsCString> CORSUnsafeHeaders;
2796 0 : mAuthorRequestHeaders.GetCORSUnsafeHeaders(CORSUnsafeHeaders);
2797 0 : nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
2798 0 : if (loadInfo) {
2799 0 : loadInfo->SetCorsPreflightInfo(CORSUnsafeHeaders,
2800 0 : mFlagHadUploadListenersOnSend);
2801 : }
2802 : }
2803 :
2804 : // Hook us up to listen to redirects and the like. Only do this very late
2805 : // since this creates a cycle between the channel and us. This cycle has
2806 : // to be manually broken if anything below fails.
2807 2 : mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
2808 2 : mChannel->SetNotificationCallbacks(this);
2809 :
2810 2 : if (internalHttpChannel) {
2811 0 : internalHttpChannel->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
2812 : }
2813 :
2814 : // Because of bug 682305, we can't let listener be the XHR object itself
2815 : // because JS wouldn't be able to use it. So create a listener around 'this'.
2816 : // Make sure to hold a strong reference so that we don't leak the wrapper.
2817 4 : nsCOMPtr<nsIStreamListener> listener = new net::nsStreamListenerWrapper(this);
2818 :
2819 : // Check if this XHR is created from a tracking script.
2820 : // If yes, lower the channel's priority.
2821 2 : if (nsContentUtils::IsLowerNetworkPriority()) {
2822 2 : MaybeLowerChannelPriority();
2823 : }
2824 :
2825 : // Start reading from the channel
2826 2 : rv = mChannel->AsyncOpen2(listener);
2827 2 : listener = nullptr;
2828 2 : if (NS_WARN_IF(NS_FAILED(rv))) {
2829 : // Drop our ref to the channel to avoid cycles. Also drop channel's
2830 : // ref to us to be extra safe.
2831 0 : mChannel->SetNotificationCallbacks(mNotificationCallbacks);
2832 0 : mChannel = nullptr;
2833 :
2834 0 : mErrorLoad = ErrorType::eChannelOpen;
2835 :
2836 : // Per spec, we throw on sync errors, but not async.
2837 0 : if (mFlagSynchronous) {
2838 0 : mState = State::done;
2839 0 : return NS_ERROR_DOM_NETWORK_ERR;
2840 : }
2841 : }
2842 :
2843 2 : return NS_OK;
2844 : }
2845 :
2846 : NS_IMETHODIMP
2847 2 : XMLHttpRequestMainThread::Send(nsIVariant* aVariant)
2848 : {
2849 2 : if (!aVariant) {
2850 0 : return SendInternal(nullptr);
2851 : }
2852 :
2853 : uint16_t dataType;
2854 2 : nsresult rv = aVariant->GetDataType(&dataType);
2855 2 : NS_ENSURE_SUCCESS(rv, rv);
2856 :
2857 4 : if (dataType == nsIDataType::VTYPE_INTERFACE ||
2858 2 : dataType == nsIDataType::VTYPE_INTERFACE_IS) {
2859 0 : nsCOMPtr<nsISupports> supports;
2860 : nsID *iid;
2861 0 : rv = aVariant->GetAsInterface(&iid, getter_AddRefs(supports));
2862 0 : NS_ENSURE_SUCCESS(rv, rv);
2863 :
2864 0 : free(iid);
2865 :
2866 : // document?
2867 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(supports);
2868 0 : if (doc) {
2869 0 : BodyExtractor<nsIDocument> body(doc);
2870 0 : return SendInternal(&body);
2871 : }
2872 :
2873 : // nsISupportsString?
2874 0 : nsCOMPtr<nsISupportsString> wstr = do_QueryInterface(supports);
2875 0 : if (wstr) {
2876 0 : nsAutoString string;
2877 0 : wstr->GetData(string);
2878 0 : BodyExtractor<const nsAString> body(&string);
2879 0 : return SendInternal(&body);
2880 : }
2881 :
2882 : // nsIInputStream?
2883 0 : nsCOMPtr<nsIInputStream> stream = do_QueryInterface(supports);
2884 0 : if (stream) {
2885 0 : BodyExtractor<nsIInputStream> body(stream);
2886 0 : return SendInternal(&body);
2887 : }
2888 :
2889 : // nsIXHRSendable?
2890 0 : nsCOMPtr<nsIXHRSendable> sendable = do_QueryInterface(supports);
2891 0 : if (sendable) {
2892 0 : BodyExtractor<nsIXHRSendable> body(sendable);
2893 0 : return SendInternal(&body);
2894 : }
2895 :
2896 : // ArrayBuffer?
2897 0 : JS::RootingContext* rootingCx = RootingCx();
2898 0 : JS::Rooted<JS::Value> realVal(rootingCx);
2899 :
2900 0 : nsresult rv = aVariant->GetAsJSVal(&realVal);
2901 0 : if (NS_SUCCEEDED(rv) && !realVal.isPrimitive()) {
2902 0 : JS::Rooted<JSObject*> obj(rootingCx, realVal.toObjectOrNull());
2903 0 : RootedTypedArray<ArrayBuffer> buf(rootingCx);
2904 0 : if (buf.Init(obj)) {
2905 0 : BodyExtractor<const ArrayBuffer> body(&buf);
2906 0 : return SendInternal(&body);
2907 : }
2908 0 : }
2909 4 : } else if (dataType == nsIDataType::VTYPE_VOID ||
2910 2 : dataType == nsIDataType::VTYPE_EMPTY) {
2911 0 : return SendInternal(nullptr);
2912 : }
2913 :
2914 2 : char16_t* data = nullptr;
2915 2 : uint32_t len = 0;
2916 2 : rv = aVariant->GetAsWStringWithSize(&len, &data);
2917 2 : NS_ENSURE_SUCCESS(rv, rv);
2918 :
2919 4 : nsString string;
2920 2 : string.Adopt(data, len);
2921 :
2922 2 : BodyExtractor<const nsAString> body(&string);
2923 2 : return SendInternal(&body);
2924 : }
2925 :
2926 : void
2927 0 : XMLHttpRequestMainThread::UnsuppressEventHandlingAndResume()
2928 : {
2929 0 : MOZ_ASSERT(NS_IsMainThread());
2930 0 : MOZ_ASSERT(mFlagSynchronous);
2931 :
2932 0 : if (mSuspendedDoc) {
2933 0 : mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(true);
2934 0 : mSuspendedDoc = nullptr;
2935 : }
2936 :
2937 0 : if (mResumeTimeoutRunnable) {
2938 0 : DispatchToMainThread(mResumeTimeoutRunnable.forget());
2939 0 : mResumeTimeoutRunnable = nullptr;
2940 : }
2941 0 : }
2942 :
2943 : nsresult
2944 2 : XMLHttpRequestMainThread::SendInternal(const BodyExtractorBase* aBody)
2945 : {
2946 2 : MOZ_ASSERT(NS_IsMainThread());
2947 :
2948 2 : NS_ENSURE_TRUE(mPrincipal, NS_ERROR_NOT_INITIALIZED);
2949 :
2950 : // Step 1
2951 2 : if (mState != State::opened) {
2952 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
2953 : }
2954 :
2955 : // Step 2
2956 2 : if (mFlagSend) {
2957 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
2958 : }
2959 :
2960 2 : nsresult rv = CheckInnerWindowCorrectness();
2961 2 : if (NS_FAILED(rv)) {
2962 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT;
2963 : }
2964 :
2965 : // If open() failed to create the channel, then throw a network error
2966 : // as per spec. We really should create the channel here in send(), but
2967 : // we have internal code relying on the channel being created in open().
2968 2 : if (!mChannel) {
2969 0 : return NS_ERROR_DOM_NETWORK_ERR;
2970 : }
2971 :
2972 2 : PopulateNetworkInterfaceId();
2973 :
2974 : // XXX We should probably send a warning to the JS console
2975 : // if there are no event listeners set and we are doing
2976 : // an asynchronous call.
2977 :
2978 2 : mUploadTransferred = 0;
2979 2 : mUploadTotal = 0;
2980 : // By default we don't have any upload, so mark upload complete.
2981 2 : mUploadComplete = true;
2982 2 : mErrorLoad = ErrorType::eOK;
2983 2 : mLoadTotal = -1;
2984 4 : nsCOMPtr<nsIInputStream> uploadStream;
2985 4 : nsAutoCString uploadContentType;
2986 4 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
2987 4 : if (aBody && httpChannel &&
2988 2 : !mRequestMethod.EqualsLiteral("GET") &&
2989 0 : !mRequestMethod.EqualsLiteral("HEAD")) {
2990 :
2991 0 : nsAutoCString charset;
2992 0 : nsAutoCString defaultContentType;
2993 : uint64_t size_u64;
2994 0 : rv = aBody->GetAsStream(getter_AddRefs(uploadStream),
2995 0 : &size_u64, defaultContentType, charset);
2996 0 : NS_ENSURE_SUCCESS(rv, rv);
2997 :
2998 : // make sure it fits within js MAX_SAFE_INTEGER
2999 0 : mUploadTotal =
3000 0 : net::InScriptableRange(size_u64) ? static_cast<int64_t>(size_u64) : -1;
3001 :
3002 0 : if (uploadStream) {
3003 : // If author set no Content-Type, use the default from GetAsStream().
3004 0 : mAuthorRequestHeaders.Get("content-type", uploadContentType);
3005 0 : if (uploadContentType.IsVoid()) {
3006 0 : uploadContentType = defaultContentType;
3007 : }
3008 :
3009 : // We don't want to set a charset for streams.
3010 0 : if (!charset.IsEmpty()) {
3011 : // Replace all case-insensitive matches of the charset in the
3012 : // content-type with the correct case.
3013 0 : RequestHeaders::CharsetIterator iter(uploadContentType);
3014 0 : const nsCaseInsensitiveCStringComparator cmp;
3015 0 : while (iter.Next()) {
3016 0 : if (!iter.Equals(charset, cmp)) {
3017 0 : iter.Replace(charset);
3018 : }
3019 : }
3020 : }
3021 :
3022 0 : mUploadComplete = false;
3023 : }
3024 : }
3025 :
3026 2 : ResetResponse();
3027 :
3028 : // Check if we should enable cross-origin upload listeners.
3029 2 : if (mUpload && mUpload->HasListeners()) {
3030 0 : mFlagHadUploadListenersOnSend = true;
3031 : }
3032 :
3033 2 : mIsMappedArrayBuffer = false;
3034 2 : if (mResponseType == XMLHttpRequestResponseType::Arraybuffer &&
3035 0 : IsMappedArrayBufferEnabled()) {
3036 0 : nsCOMPtr<nsIURI> uri;
3037 0 : nsAutoCString scheme;
3038 :
3039 0 : rv = mChannel->GetURI(getter_AddRefs(uri));
3040 0 : if (NS_SUCCEEDED(rv)) {
3041 0 : uri->GetScheme(scheme);
3042 0 : if (scheme.LowerCaseEqualsLiteral("jar")) {
3043 0 : mIsMappedArrayBuffer = true;
3044 : }
3045 : }
3046 : }
3047 :
3048 2 : rv = InitiateFetch(uploadStream, mUploadTotal, uploadContentType);
3049 2 : NS_ENSURE_SUCCESS(rv, rv);
3050 :
3051 : // Start our timeout
3052 2 : mRequestSentTime = PR_Now();
3053 2 : StartTimeoutTimer();
3054 :
3055 2 : mWaitingForOnStopRequest = true;
3056 :
3057 : // Step 8
3058 2 : mFlagSend = true;
3059 :
3060 : // If we're synchronous, spin an event loop here and wait
3061 2 : if (mFlagSynchronous) {
3062 0 : mFlagSyncLooping = true;
3063 :
3064 0 : if (GetOwner()) {
3065 0 : if (nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetOwner()->GetOuterWindow()->GetTop()) {
3066 0 : if (nsCOMPtr<nsPIDOMWindowInner> topInner = topWindow->GetCurrentInnerWindow()) {
3067 0 : mSuspendedDoc = topWindow->GetExtantDoc();
3068 0 : if (mSuspendedDoc) {
3069 0 : mSuspendedDoc->SuppressEventHandling();
3070 : }
3071 0 : topInner->Suspend();
3072 0 : mResumeTimeoutRunnable = new nsResumeTimeoutsEvent(topInner);
3073 : }
3074 : }
3075 : }
3076 :
3077 0 : SuspendEventDispatching();
3078 0 : StopProgressEventTimer();
3079 :
3080 0 : SyncTimeoutType syncTimeoutType = MaybeStartSyncTimeoutTimer();
3081 0 : if (syncTimeoutType == eErrorOrExpired) {
3082 0 : Abort();
3083 0 : rv = NS_ERROR_DOM_NETWORK_ERR;
3084 : }
3085 :
3086 0 : if (NS_SUCCEEDED(rv)) {
3087 0 : nsAutoSyncOperation sync(mSuspendedDoc);
3088 0 : if (!SpinEventLoopUntil([&]() { return !mFlagSyncLooping; })) {
3089 0 : rv = NS_ERROR_UNEXPECTED;
3090 : }
3091 :
3092 : // Time expired... We should throw.
3093 0 : if (syncTimeoutType == eTimerStarted && !mSyncTimeoutTimer) {
3094 0 : rv = NS_ERROR_DOM_NETWORK_ERR;
3095 : }
3096 :
3097 0 : CancelSyncTimeoutTimer();
3098 : }
3099 :
3100 0 : UnsuppressEventHandlingAndResume();
3101 0 : ResumeEventDispatching();
3102 : } else {
3103 : // Now that we've successfully opened the channel, we can change state. Note
3104 : // that this needs to come after the AsyncOpen() and rv check, because this
3105 : // can run script that would try to restart this request, and that could end
3106 : // up doing our AsyncOpen on a null channel if the reentered AsyncOpen fails.
3107 2 : StopProgressEventTimer();
3108 :
3109 : // Upload phase beginning; start the progress event timer if necessary.
3110 2 : if (mUpload && mUpload->HasListenersFor(nsGkAtoms::onprogress)) {
3111 0 : StartProgressEventTimer();
3112 : }
3113 : // Dispatch loadstart events
3114 2 : DispatchProgressEvent(this, ProgressEventType::loadstart, 0, -1);
3115 2 : if (mUpload && !mUploadComplete) {
3116 0 : DispatchProgressEvent(mUpload, ProgressEventType::loadstart,
3117 0 : 0, mUploadTotal);
3118 : }
3119 : }
3120 :
3121 2 : if (!mChannel) {
3122 : // Per spec, silently fail on async request failures; throw for sync.
3123 0 : if (mFlagSynchronous) {
3124 0 : mState = State::done;
3125 0 : return NS_ERROR_DOM_NETWORK_ERR;
3126 : } else {
3127 : // Defer the actual sending of async events just in case listeners
3128 : // are attached after the send() method is called.
3129 0 : return DispatchToMainThread(NewRunnableMethod<ProgressEventType>(
3130 : "dom::XMLHttpRequestMainThread::CloseRequestWithError",
3131 : this,
3132 : &XMLHttpRequestMainThread::CloseRequestWithError,
3133 0 : ProgressEventType::error));
3134 : }
3135 : }
3136 :
3137 2 : return rv;
3138 : }
3139 :
3140 : /* static */
3141 : bool
3142 0 : XMLHttpRequestMainThread::IsMappedArrayBufferEnabled()
3143 : {
3144 : static bool sMappedArrayBufferAdded = false;
3145 : static bool sIsMappedArrayBufferEnabled;
3146 :
3147 0 : if (!sMappedArrayBufferAdded) {
3148 : Preferences::AddBoolVarCache(&sIsMappedArrayBufferEnabled,
3149 : "dom.mapped_arraybuffer.enabled",
3150 0 : true);
3151 0 : sMappedArrayBufferAdded = true;
3152 : }
3153 :
3154 0 : return sIsMappedArrayBufferEnabled;
3155 : }
3156 :
3157 : /* static */
3158 : bool
3159 0 : XMLHttpRequestMainThread::IsLowercaseResponseHeader()
3160 : {
3161 : static bool sLowercaseResponseHeaderAdded = false;
3162 : static bool sIsLowercaseResponseHeaderEnabled;
3163 :
3164 0 : if (!sLowercaseResponseHeaderAdded) {
3165 : Preferences::AddBoolVarCache(&sIsLowercaseResponseHeaderEnabled,
3166 : "dom.xhr.lowercase_header.enabled",
3167 0 : false);
3168 0 : sLowercaseResponseHeaderAdded = true;
3169 : }
3170 :
3171 0 : return sIsLowercaseResponseHeaderEnabled;
3172 : }
3173 :
3174 : // http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html#dom-xmlhttprequest-setrequestheader
3175 : NS_IMETHODIMP
3176 0 : XMLHttpRequestMainThread::SetRequestHeader(const nsACString& aName,
3177 : const nsACString& aValue)
3178 : {
3179 : // Step 1
3180 0 : if (mState != State::opened) {
3181 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_BE_OPENED;
3182 : }
3183 :
3184 : // Step 2
3185 0 : if (mFlagSend) {
3186 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
3187 : }
3188 :
3189 : // Step 3
3190 0 : nsAutoCString value;
3191 0 : NS_TrimHTTPWhitespace(aValue, value);
3192 :
3193 : // Step 4
3194 0 : if (!NS_IsValidHTTPToken(aName) || !NS_IsReasonableHTTPHeaderValue(value)) {
3195 0 : return NS_ERROR_DOM_INVALID_HEADER_NAME;
3196 : }
3197 :
3198 : // Step 5
3199 0 : bool isPrivilegedCaller = IsSystemXHR();
3200 0 : bool isForbiddenHeader = nsContentUtils::IsForbiddenRequestHeader(aName);
3201 0 : if (!isPrivilegedCaller && isForbiddenHeader) {
3202 0 : NS_ConvertUTF8toUTF16 name(aName);
3203 0 : const char16_t* params[] = { name.get() };
3204 0 : LogMessage("ForbiddenHeaderWarning", GetOwner(), params, ArrayLength(params));
3205 0 : return NS_OK;
3206 : }
3207 :
3208 : // Step 6.1
3209 : // Skipping for now, as normalizing the case of header names may not be
3210 : // web-compatible. See bug 1285036.
3211 :
3212 : // Step 6.2-6.3
3213 : // Gecko-specific: invalid headers can be set by privileged
3214 : // callers, but will not merge.
3215 0 : if (isPrivilegedCaller && isForbiddenHeader) {
3216 0 : mAuthorRequestHeaders.Set(aName, value);
3217 : } else {
3218 0 : mAuthorRequestHeaders.MergeOrSet(aName, value);
3219 : }
3220 :
3221 0 : return NS_OK;
3222 : }
3223 :
3224 : NS_IMETHODIMP
3225 0 : XMLHttpRequestMainThread::GetTimeout(uint32_t *aTimeout)
3226 : {
3227 0 : *aTimeout = Timeout();
3228 0 : return NS_OK;
3229 : }
3230 :
3231 : NS_IMETHODIMP
3232 0 : XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout)
3233 : {
3234 0 : ErrorResult rv;
3235 0 : SetTimeout(aTimeout, rv);
3236 0 : return rv.StealNSResult();
3237 : }
3238 :
3239 : void
3240 0 : XMLHttpRequestMainThread::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
3241 : {
3242 0 : if (mFlagSynchronous && mState != State::unsent && HasOrHasHadOwner()) {
3243 : /* Timeout is not supported for synchronous requests with an owning window,
3244 : per XHR2 spec. */
3245 0 : LogMessage("TimeoutSyncXHRWarning", GetOwner());
3246 0 : aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC);
3247 0 : return;
3248 : }
3249 :
3250 0 : mTimeoutMilliseconds = aTimeout;
3251 0 : if (mRequestSentTime) {
3252 0 : StartTimeoutTimer();
3253 : }
3254 : }
3255 :
3256 : void
3257 2 : XMLHttpRequestMainThread::SetTimerEventTarget(nsITimer* aTimer)
3258 : {
3259 4 : if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
3260 0 : nsCOMPtr<nsIEventTarget> target = global->EventTargetFor(TaskCategory::Other);
3261 0 : aTimer->SetTarget(target);
3262 : }
3263 2 : }
3264 :
3265 : nsresult
3266 0 : XMLHttpRequestMainThread::DispatchToMainThread(already_AddRefed<nsIRunnable> aRunnable)
3267 : {
3268 0 : if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
3269 0 : nsCOMPtr<nsIEventTarget> target = global->EventTargetFor(TaskCategory::Other);
3270 0 : MOZ_ASSERT(target);
3271 :
3272 0 : return target->Dispatch(Move(aRunnable), NS_DISPATCH_NORMAL);
3273 : }
3274 :
3275 0 : return NS_DispatchToMainThread(Move(aRunnable));
3276 : }
3277 :
3278 : void
3279 2 : XMLHttpRequestMainThread::StartTimeoutTimer()
3280 : {
3281 2 : MOZ_ASSERT(mRequestSentTime,
3282 : "StartTimeoutTimer mustn't be called before the request was sent!");
3283 2 : if (mState == State::done) {
3284 : // do nothing!
3285 0 : return;
3286 : }
3287 :
3288 2 : if (mTimeoutTimer) {
3289 0 : mTimeoutTimer->Cancel();
3290 : }
3291 :
3292 2 : if (!mTimeoutMilliseconds) {
3293 2 : return;
3294 : }
3295 :
3296 0 : if (!mTimeoutTimer) {
3297 0 : mTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
3298 0 : SetTimerEventTarget(mTimeoutTimer);
3299 : }
3300 : uint32_t elapsed =
3301 0 : (uint32_t)((PR_Now() - mRequestSentTime) / PR_USEC_PER_MSEC);
3302 0 : mTimeoutTimer->InitWithCallback(
3303 : this,
3304 0 : mTimeoutMilliseconds > elapsed ? mTimeoutMilliseconds - elapsed : 0,
3305 : nsITimer::TYPE_ONE_SHOT
3306 0 : );
3307 : }
3308 :
3309 : NS_IMETHODIMP
3310 3 : XMLHttpRequestMainThread::GetReadyState(uint16_t *aState)
3311 : {
3312 3 : *aState = ReadyState();
3313 3 : return NS_OK;
3314 : }
3315 :
3316 : uint16_t
3317 71 : XMLHttpRequestMainThread::ReadyState() const
3318 : {
3319 : // Translate some of our internal states for external consumers
3320 71 : switch(mState) {
3321 : case State::unsent:
3322 0 : return UNSENT;
3323 : case State::opened:
3324 23 : return OPENED;
3325 : case State::headers_received:
3326 8 : return HEADERS_RECEIVED;
3327 : case State::loading:
3328 16 : return LOADING;
3329 : case State::done:
3330 24 : return DONE;
3331 : default:
3332 0 : MOZ_CRASH("Unknown state");
3333 : }
3334 : return 0;
3335 : }
3336 :
3337 0 : void XMLHttpRequestMainThread::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
3338 : {
3339 0 : if (mState == State::loading || mState == State::done) {
3340 0 : ResetResponse();
3341 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_LOADING_OR_DONE);
3342 0 : return;
3343 : }
3344 :
3345 0 : mOverrideMimeType = aMimeType;
3346 : }
3347 :
3348 : NS_IMETHODIMP
3349 0 : XMLHttpRequestMainThread::SlowOverrideMimeType(const nsAString& aMimeType)
3350 : {
3351 0 : ErrorResult aRv;
3352 0 : OverrideMimeType(aMimeType, aRv);
3353 0 : return aRv.StealNSResult();
3354 : }
3355 :
3356 : NS_IMETHODIMP
3357 0 : XMLHttpRequestMainThread::GetMozBackgroundRequest(bool *_retval)
3358 : {
3359 0 : *_retval = MozBackgroundRequest();
3360 0 : return NS_OK;
3361 : }
3362 :
3363 : bool
3364 0 : XMLHttpRequestMainThread::MozBackgroundRequest() const
3365 : {
3366 0 : return mFlagBackgroundRequest;
3367 : }
3368 :
3369 : NS_IMETHODIMP
3370 0 : XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest)
3371 : {
3372 0 : if (!IsSystemXHR()) {
3373 0 : return NS_ERROR_DOM_SECURITY_ERR;
3374 : }
3375 :
3376 0 : if (mState != State::unsent) {
3377 : // Can't change this while we're in the middle of something.
3378 0 : return NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING;
3379 : }
3380 :
3381 0 : mFlagBackgroundRequest = aMozBackgroundRequest;
3382 :
3383 0 : return NS_OK;
3384 : }
3385 :
3386 : void
3387 0 : XMLHttpRequestMainThread::SetMozBackgroundRequest(bool aMozBackgroundRequest,
3388 : ErrorResult& aRv)
3389 : {
3390 : // No errors for this webIDL method on main-thread.
3391 0 : SetMozBackgroundRequest(aMozBackgroundRequest);
3392 0 : }
3393 :
3394 : NS_IMETHODIMP
3395 0 : XMLHttpRequestMainThread::GetWithCredentials(bool *_retval)
3396 : {
3397 0 : *_retval = WithCredentials();
3398 0 : return NS_OK;
3399 : }
3400 :
3401 : bool
3402 0 : XMLHttpRequestMainThread::WithCredentials() const
3403 : {
3404 0 : return mFlagACwithCredentials;
3405 : }
3406 :
3407 : NS_IMETHODIMP
3408 0 : XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials)
3409 : {
3410 0 : ErrorResult rv;
3411 0 : SetWithCredentials(aWithCredentials, rv);
3412 0 : return rv.StealNSResult();
3413 : }
3414 :
3415 : void
3416 0 : XMLHttpRequestMainThread::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
3417 : {
3418 : // Return error if we're already processing a request. Note that we can't use
3419 : // ReadyState() here, because it can't differentiate between "opened" and
3420 : // "sent", so we use mState directly.
3421 :
3422 0 : if ((mState != State::unsent && mState != State::opened) ||
3423 0 : mFlagSend || mIsAnon) {
3424 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_MUST_NOT_BE_SENDING);
3425 0 : return;
3426 : }
3427 :
3428 0 : mFlagACwithCredentials = aWithCredentials;
3429 : }
3430 :
3431 : nsresult
3432 6 : XMLHttpRequestMainThread::ChangeState(State aState, bool aBroadcast)
3433 : {
3434 6 : mState = aState;
3435 6 : nsresult rv = NS_OK;
3436 :
3437 6 : if (aState != State::headers_received && aState != State::loading) {
3438 2 : StopProgressEventTimer();
3439 : }
3440 :
3441 :
3442 6 : if (aBroadcast && (!mFlagSynchronous ||
3443 0 : aState == State::opened ||
3444 : aState == State::done)) {
3445 6 : rv = FireReadystatechangeEvent();
3446 : }
3447 :
3448 6 : return rv;
3449 : }
3450 :
3451 : /////////////////////////////////////////////////////
3452 : // nsIChannelEventSink methods:
3453 : //
3454 : NS_IMETHODIMP
3455 0 : XMLHttpRequestMainThread::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
3456 : nsIChannel *aNewChannel,
3457 : uint32_t aFlags,
3458 : nsIAsyncVerifyRedirectCallback *callback)
3459 : {
3460 0 : NS_PRECONDITION(aNewChannel, "Redirect without a channel?");
3461 :
3462 : // Prepare to receive callback
3463 0 : mRedirectCallback = callback;
3464 0 : mNewRedirectChannel = aNewChannel;
3465 :
3466 0 : if (mChannelEventSink) {
3467 : nsCOMPtr<nsIAsyncVerifyRedirectCallback> fwd =
3468 0 : EnsureXPCOMifier();
3469 :
3470 0 : nsresult rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
3471 : aNewChannel,
3472 0 : aFlags, fwd);
3473 0 : if (NS_FAILED(rv)) {
3474 0 : mRedirectCallback = nullptr;
3475 0 : mNewRedirectChannel = nullptr;
3476 : }
3477 0 : return rv;
3478 : }
3479 0 : OnRedirectVerifyCallback(NS_OK);
3480 0 : return NS_OK;
3481 : }
3482 :
3483 : nsresult
3484 0 : XMLHttpRequestMainThread::OnRedirectVerifyCallback(nsresult result)
3485 : {
3486 0 : NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
3487 0 : NS_ASSERTION(mNewRedirectChannel, "mNewRedirectChannel not set in callback");
3488 :
3489 0 : if (NS_SUCCEEDED(result)) {
3490 0 : mChannel = mNewRedirectChannel;
3491 :
3492 0 : nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
3493 0 : if (httpChannel) {
3494 : // Ensure all original headers are duplicated for the new channel (bug #553888)
3495 0 : mAuthorRequestHeaders.ApplyToChannel(httpChannel);
3496 : }
3497 : } else {
3498 0 : mErrorLoad = ErrorType::eRedirect;
3499 : }
3500 :
3501 0 : mNewRedirectChannel = nullptr;
3502 :
3503 0 : mRedirectCallback->OnRedirectVerifyCallback(result);
3504 0 : mRedirectCallback = nullptr;
3505 :
3506 : // It's important that we return success here. If we return the result code
3507 : // that we were passed, JavaScript callers who cancel the redirect will wind
3508 : // up throwing an exception in the process.
3509 0 : return NS_OK;
3510 : }
3511 :
3512 : /////////////////////////////////////////////////////
3513 : // nsIProgressEventSink methods:
3514 : //
3515 :
3516 : NS_IMETHODIMP
3517 2 : XMLHttpRequestMainThread::OnProgress(nsIRequest *aRequest, nsISupports *aContext, int64_t aProgress, int64_t aProgressMax)
3518 : {
3519 : // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
3520 : // So, try to remove the headers, if possible.
3521 2 : bool lengthComputable = (aProgressMax != -1);
3522 2 : if (InUploadPhase()) {
3523 0 : int64_t loaded = aProgress;
3524 0 : if (lengthComputable) {
3525 0 : int64_t headerSize = aProgressMax - mUploadTotal;
3526 0 : loaded -= headerSize;
3527 : }
3528 0 : mUploadTransferred = loaded;
3529 0 : mProgressSinceLastProgressEvent = true;
3530 :
3531 0 : if (!mFlagSynchronous && !mProgressTimerIsActive) {
3532 0 : StartProgressEventTimer();
3533 : }
3534 : } else {
3535 2 : mLoadTotal = aProgressMax;
3536 2 : mLoadTransferred = aProgress;
3537 : // OnDataAvailable() handles mProgressSinceLastProgressEvent
3538 : // for the download phase.
3539 : }
3540 :
3541 2 : if (mProgressEventSink) {
3542 0 : mProgressEventSink->OnProgress(aRequest, aContext, aProgress,
3543 0 : aProgressMax);
3544 : }
3545 :
3546 2 : return NS_OK;
3547 : }
3548 :
3549 : NS_IMETHODIMP
3550 0 : XMLHttpRequestMainThread::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatus, const char16_t *aStatusArg)
3551 : {
3552 0 : if (mProgressEventSink) {
3553 0 : mProgressEventSink->OnStatus(aRequest, aContext, aStatus, aStatusArg);
3554 : }
3555 :
3556 0 : return NS_OK;
3557 : }
3558 :
3559 : bool
3560 8 : XMLHttpRequestMainThread::AllowUploadProgress()
3561 : {
3562 8 : return !IsCrossSiteCORSRequest() ||
3563 8 : mFlagHadUploadListenersOnSend;
3564 : }
3565 :
3566 : /////////////////////////////////////////////////////
3567 : // nsIInterfaceRequestor methods:
3568 : //
3569 : NS_IMETHODIMP
3570 6 : XMLHttpRequestMainThread::GetInterface(const nsIID & aIID, void **aResult)
3571 : {
3572 : nsresult rv;
3573 :
3574 : // Make sure to return ourselves for the channel event sink interface and
3575 : // progress event sink interface, no matter what. We can forward these to
3576 : // mNotificationCallbacks if it wants to get notifications for them. But we
3577 : // need to see these notifications for proper functioning.
3578 6 : if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) {
3579 0 : mChannelEventSink = do_GetInterface(mNotificationCallbacks);
3580 0 : *aResult = static_cast<nsIChannelEventSink*>(EnsureXPCOMifier().take());
3581 0 : return NS_OK;
3582 6 : } else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) {
3583 2 : mProgressEventSink = do_GetInterface(mNotificationCallbacks);
3584 2 : *aResult = static_cast<nsIProgressEventSink*>(EnsureXPCOMifier().take());
3585 2 : return NS_OK;
3586 : }
3587 :
3588 : // Now give mNotificationCallbacks (if non-null) a chance to return the
3589 : // desired interface.
3590 4 : if (mNotificationCallbacks) {
3591 0 : rv = mNotificationCallbacks->GetInterface(aIID, aResult);
3592 0 : if (NS_SUCCEEDED(rv)) {
3593 0 : NS_ASSERTION(*aResult, "Lying nsIInterfaceRequestor implementation!");
3594 0 : return rv;
3595 : }
3596 : }
3597 :
3598 4 : if (mFlagBackgroundRequest) {
3599 0 : nsCOMPtr<nsIInterfaceRequestor> badCertHandler(do_CreateInstance(NS_BADCERTHANDLER_CONTRACTID, &rv));
3600 :
3601 : // Ignore failure to get component, we may not have all its dependencies
3602 : // available
3603 0 : if (NS_SUCCEEDED(rv)) {
3604 0 : rv = badCertHandler->GetInterface(aIID, aResult);
3605 0 : if (NS_SUCCEEDED(rv))
3606 0 : return rv;
3607 : }
3608 : }
3609 8 : else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
3610 4 : aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
3611 : nsCOMPtr<nsIPromptFactory> wwatch =
3612 0 : do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
3613 0 : NS_ENSURE_SUCCESS(rv, rv);
3614 :
3615 : // Get the an auth prompter for our window so that the parenting
3616 : // of the dialogs works as it should when using tabs.
3617 :
3618 0 : nsCOMPtr<nsPIDOMWindowOuter> window;
3619 0 : if (GetOwner()) {
3620 0 : window = GetOwner()->GetOuterWindow();
3621 : }
3622 :
3623 0 : return wwatch->GetPrompt(window, aIID,
3624 0 : reinterpret_cast<void**>(aResult));
3625 : }
3626 : // Now check for the various XHR non-DOM interfaces, except
3627 : // nsIProgressEventSink and nsIChannelEventSink which we already
3628 : // handled above.
3629 4 : else if (aIID.Equals(NS_GET_IID(nsIStreamListener))) {
3630 0 : *aResult = static_cast<nsIStreamListener*>(EnsureXPCOMifier().take());
3631 0 : return NS_OK;
3632 : }
3633 4 : else if (aIID.Equals(NS_GET_IID(nsIRequestObserver))) {
3634 0 : *aResult = static_cast<nsIRequestObserver*>(EnsureXPCOMifier().take());
3635 0 : return NS_OK;
3636 : }
3637 4 : else if (aIID.Equals(NS_GET_IID(nsITimerCallback))) {
3638 0 : *aResult = static_cast<nsITimerCallback*>(EnsureXPCOMifier().take());
3639 0 : return NS_OK;
3640 : }
3641 :
3642 4 : return QueryInterface(aIID, aResult);
3643 : }
3644 :
3645 : void
3646 0 : XMLHttpRequestMainThread::GetInterface(JSContext* aCx, nsIJSID* aIID,
3647 : JS::MutableHandle<JS::Value> aRetval,
3648 : ErrorResult& aRv)
3649 : {
3650 0 : dom::GetInterface(aCx, this, aIID, aRetval, aRv);
3651 0 : }
3652 :
3653 : XMLHttpRequestUpload*
3654 3 : XMLHttpRequestMainThread::GetUpload(ErrorResult& aRv)
3655 : {
3656 3 : if (!mUpload) {
3657 3 : mUpload = new XMLHttpRequestUpload(this);
3658 : }
3659 3 : return mUpload;
3660 : }
3661 :
3662 : NS_IMETHODIMP
3663 0 : XMLHttpRequestMainThread::GetUpload(nsIXMLHttpRequestUpload** aUpload)
3664 : {
3665 0 : ErrorResult rv;
3666 0 : RefPtr<XMLHttpRequestUpload> upload = GetUpload(rv);
3667 0 : upload.forget(aUpload);
3668 0 : return rv.StealNSResult();
3669 : }
3670 :
3671 : bool
3672 0 : XMLHttpRequestMainThread::MozAnon() const
3673 : {
3674 0 : return mIsAnon;
3675 : }
3676 :
3677 : NS_IMETHODIMP
3678 0 : XMLHttpRequestMainThread::GetMozAnon(bool* aAnon)
3679 : {
3680 0 : *aAnon = MozAnon();
3681 0 : return NS_OK;
3682 : }
3683 :
3684 : bool
3685 0 : XMLHttpRequestMainThread::MozSystem() const
3686 : {
3687 0 : return IsSystemXHR();
3688 : }
3689 :
3690 : NS_IMETHODIMP
3691 0 : XMLHttpRequestMainThread::GetMozSystem(bool* aSystem)
3692 : {
3693 0 : *aSystem = MozSystem();
3694 0 : return NS_OK;
3695 : }
3696 :
3697 : void
3698 0 : XMLHttpRequestMainThread::HandleTimeoutCallback()
3699 : {
3700 0 : if (mState == State::done) {
3701 0 : NS_NOTREACHED("XMLHttpRequestMainThread::HandleTimeoutCallback with completed request");
3702 : // do nothing!
3703 0 : return;
3704 : }
3705 :
3706 0 : mFlagTimedOut = true;
3707 0 : CloseRequestWithError(ProgressEventType::timeout);
3708 : }
3709 :
3710 : NS_IMETHODIMP
3711 0 : XMLHttpRequestMainThread::Notify(nsITimer* aTimer)
3712 : {
3713 0 : if (mProgressNotifier == aTimer) {
3714 0 : HandleProgressTimerCallback();
3715 0 : return NS_OK;
3716 : }
3717 :
3718 0 : if (mTimeoutTimer == aTimer) {
3719 0 : HandleTimeoutCallback();
3720 0 : return NS_OK;
3721 : }
3722 :
3723 0 : if (mSyncTimeoutTimer == aTimer) {
3724 0 : HandleSyncTimeoutTimer();
3725 0 : return NS_OK;
3726 : }
3727 :
3728 : // Just in case some JS user wants to QI to nsITimerCallback and play with us...
3729 0 : NS_WARNING("Unexpected timer!");
3730 0 : return NS_ERROR_INVALID_POINTER;
3731 : }
3732 :
3733 : void
3734 0 : XMLHttpRequestMainThread::HandleProgressTimerCallback()
3735 : {
3736 : // Don't fire the progress event if mLoadTotal is 0, see XHR spec step 6.1
3737 0 : if (!mLoadTotal && mLoadTransferred) {
3738 0 : return;
3739 : }
3740 :
3741 0 : mProgressTimerIsActive = false;
3742 :
3743 0 : if (!mProgressSinceLastProgressEvent || mErrorLoad != ErrorType::eOK) {
3744 0 : return;
3745 : }
3746 :
3747 0 : if (InUploadPhase()) {
3748 0 : if (mUpload && !mUploadComplete) {
3749 0 : DispatchProgressEvent(mUpload, ProgressEventType::progress,
3750 0 : mUploadTransferred, mUploadTotal);
3751 : }
3752 : } else {
3753 0 : FireReadystatechangeEvent();
3754 0 : DispatchProgressEvent(this, ProgressEventType::progress,
3755 0 : mLoadTransferred, mLoadTotal);
3756 : }
3757 :
3758 0 : mProgressSinceLastProgressEvent = false;
3759 :
3760 0 : StartProgressEventTimer();
3761 : }
3762 :
3763 : void
3764 6 : XMLHttpRequestMainThread::StopProgressEventTimer()
3765 : {
3766 6 : if (mProgressNotifier) {
3767 4 : mProgressTimerIsActive = false;
3768 4 : mProgressNotifier->Cancel();
3769 : }
3770 6 : }
3771 :
3772 : void
3773 2 : XMLHttpRequestMainThread::StartProgressEventTimer()
3774 : {
3775 2 : if (!mProgressNotifier) {
3776 2 : mProgressNotifier = do_CreateInstance(NS_TIMER_CONTRACTID);
3777 2 : SetTimerEventTarget(mProgressNotifier);
3778 : }
3779 2 : if (mProgressNotifier) {
3780 2 : mProgressTimerIsActive = true;
3781 2 : mProgressNotifier->Cancel();
3782 4 : mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
3783 4 : nsITimer::TYPE_ONE_SHOT);
3784 : }
3785 2 : }
3786 :
3787 : XMLHttpRequestMainThread::SyncTimeoutType
3788 0 : XMLHttpRequestMainThread::MaybeStartSyncTimeoutTimer()
3789 : {
3790 0 : MOZ_ASSERT(mFlagSynchronous);
3791 :
3792 0 : nsIDocument* doc = GetDocumentIfCurrent();
3793 0 : if (!doc || !doc->GetPageUnloadingEventTimeStamp()) {
3794 0 : return eNoTimerNeeded;
3795 : }
3796 :
3797 : // If we are in a beforeunload or a unload event, we must force a timeout.
3798 0 : TimeDuration diff = (TimeStamp::NowLoRes() - doc->GetPageUnloadingEventTimeStamp());
3799 0 : if (diff.ToMilliseconds() > MAX_SYNC_TIMEOUT_WHEN_UNLOADING) {
3800 0 : return eErrorOrExpired;
3801 : }
3802 :
3803 0 : mSyncTimeoutTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
3804 0 : SetTimerEventTarget(mSyncTimeoutTimer);
3805 0 : if (!mSyncTimeoutTimer) {
3806 0 : return eErrorOrExpired;
3807 : }
3808 :
3809 0 : uint32_t timeout = MAX_SYNC_TIMEOUT_WHEN_UNLOADING - diff.ToMilliseconds();
3810 0 : nsresult rv = mSyncTimeoutTimer->InitWithCallback(this, timeout,
3811 0 : nsITimer::TYPE_ONE_SHOT);
3812 0 : return NS_FAILED(rv) ? eErrorOrExpired : eTimerStarted;
3813 : }
3814 :
3815 : void
3816 0 : XMLHttpRequestMainThread::HandleSyncTimeoutTimer()
3817 : {
3818 0 : MOZ_ASSERT(mSyncTimeoutTimer);
3819 0 : MOZ_ASSERT(mFlagSyncLooping);
3820 :
3821 0 : CancelSyncTimeoutTimer();
3822 0 : Abort();
3823 0 : }
3824 :
3825 : void
3826 0 : XMLHttpRequestMainThread::CancelSyncTimeoutTimer()
3827 : {
3828 0 : if (mSyncTimeoutTimer) {
3829 0 : mSyncTimeoutTimer->Cancel();
3830 0 : mSyncTimeoutTimer = nullptr;
3831 : }
3832 0 : }
3833 :
3834 : already_AddRefed<nsXMLHttpRequestXPCOMifier>
3835 2 : XMLHttpRequestMainThread::EnsureXPCOMifier()
3836 : {
3837 2 : if (!mXPCOMifier) {
3838 2 : mXPCOMifier = new nsXMLHttpRequestXPCOMifier(this);
3839 : }
3840 4 : RefPtr<nsXMLHttpRequestXPCOMifier> newRef(mXPCOMifier);
3841 4 : return newRef.forget();
3842 : }
3843 :
3844 : bool
3845 0 : XMLHttpRequestMainThread::ShouldBlockAuthPrompt()
3846 : {
3847 : // Verify that it's ok to prompt for credentials here, per spec
3848 : // http://xhr.spec.whatwg.org/#the-send%28%29-method
3849 :
3850 0 : if (mAuthorRequestHeaders.Has("authorization")) {
3851 0 : return true;
3852 : }
3853 :
3854 0 : nsCOMPtr<nsIURI> uri;
3855 0 : nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
3856 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3857 0 : return false;
3858 : }
3859 :
3860 : // Also skip if a username and/or password is provided in the URI.
3861 0 : nsCString username;
3862 0 : rv = uri->GetUsername(username);
3863 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3864 0 : return false;
3865 : }
3866 :
3867 0 : nsCString password;
3868 0 : rv = uri->GetPassword(password);
3869 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3870 0 : return false;
3871 : }
3872 :
3873 0 : if (!username.IsEmpty() || !password.IsEmpty()) {
3874 0 : return true;
3875 : }
3876 :
3877 0 : return false;
3878 : }
3879 :
3880 : void
3881 7 : XMLHttpRequestMainThread::TruncateResponseText()
3882 : {
3883 7 : mResponseText.Truncate();
3884 7 : XMLHttpRequestBinding::ClearCachedResponseTextValue(this);
3885 7 : }
3886 :
3887 0 : NS_IMPL_ISUPPORTS(XMLHttpRequestMainThread::nsHeaderVisitor, nsIHttpHeaderVisitor)
3888 :
3889 0 : NS_IMETHODIMP XMLHttpRequestMainThread::
3890 : nsHeaderVisitor::VisitHeader(const nsACString &header, const nsACString &value)
3891 : {
3892 0 : if (mXHR.IsSafeHeader(header, mHttpChannel)) {
3893 0 : if (!IsLowercaseResponseHeader()) {
3894 0 : if(!mHeaderList.InsertElementSorted(HeaderEntry(header, value),
3895 : fallible)) {
3896 0 : return NS_ERROR_OUT_OF_MEMORY;
3897 : }
3898 0 : return NS_OK;
3899 : }
3900 :
3901 0 : nsAutoCString lowerHeader(header);
3902 0 : ToLowerCase(lowerHeader);
3903 0 : if (!mHeaderList.InsertElementSorted(HeaderEntry(lowerHeader, value),
3904 : fallible)) {
3905 0 : return NS_ERROR_OUT_OF_MEMORY;
3906 : }
3907 : }
3908 0 : return NS_OK;
3909 : }
3910 :
3911 : void
3912 0 : XMLHttpRequestMainThread::MaybeCreateBlobStorage()
3913 : {
3914 0 : MOZ_ASSERT(mResponseType == XMLHttpRequestResponseType::Blob);
3915 :
3916 0 : if (mBlobStorage) {
3917 0 : return;
3918 : }
3919 :
3920 : MutableBlobStorage::MutableBlobStorageType storageType =
3921 0 : BasePrincipal::Cast(mPrincipal)->PrivateBrowsingId() == 0
3922 0 : ? MutableBlobStorage::eCouldBeInTemporaryFile
3923 0 : : MutableBlobStorage::eOnlyInMemory;
3924 :
3925 0 : nsCOMPtr<nsIEventTarget> eventTarget;
3926 0 : if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) {
3927 0 : eventTarget = global->EventTargetFor(TaskCategory::Other);
3928 : }
3929 :
3930 0 : mBlobStorage = new MutableBlobStorage(storageType, eventTarget);
3931 : }
3932 :
3933 : void
3934 0 : XMLHttpRequestMainThread::BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
3935 : Blob* aBlob, nsresult aRv)
3936 : {
3937 : // Ok, the state is changed...
3938 0 : if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) {
3939 0 : return;
3940 : }
3941 :
3942 0 : MOZ_ASSERT(mState != State::done);
3943 :
3944 0 : mResponseBlob = aBlob;
3945 0 : mBlobStorage = nullptr;
3946 :
3947 0 : ChangeStateToDone();
3948 : }
3949 :
3950 : NS_IMETHODIMP
3951 0 : XMLHttpRequestMainThread::GetName(nsACString& aName)
3952 : {
3953 0 : aName.AssignLiteral("XMLHttpRequest");
3954 0 : return NS_OK;
3955 : }
3956 :
3957 : NS_IMETHODIMP
3958 0 : XMLHttpRequestMainThread::SetName(const char* aName)
3959 : {
3960 0 : return NS_ERROR_NOT_IMPLEMENTED;
3961 : }
3962 :
3963 : // nsXMLHttpRequestXPCOMifier implementation
3964 12 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
3965 2 : NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
3966 2 : NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
3967 2 : NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
3968 2 : NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
3969 2 : NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
3970 0 : NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
3971 0 : NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
3972 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
3973 0 : NS_INTERFACE_MAP_END
3974 :
3975 4 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXMLHttpRequestXPCOMifier)
3976 8 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXMLHttpRequestXPCOMifier)
3977 :
3978 : // Can't NS_IMPL_CYCLE_COLLECTION( because mXHR has ambiguous
3979 : // inheritance from nsISupports.
3980 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXMLHttpRequestXPCOMifier)
3981 :
3982 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXMLHttpRequestXPCOMifier)
3983 0 : if (tmp->mXHR) {
3984 0 : tmp->mXHR->mXPCOMifier = nullptr;
3985 : }
3986 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mXHR)
3987 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
3988 :
3989 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXMLHttpRequestXPCOMifier)
3990 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXHR)
3991 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
3992 :
3993 : NS_IMETHODIMP
3994 0 : nsXMLHttpRequestXPCOMifier::GetInterface(const nsIID & aIID, void **aResult)
3995 : {
3996 : // Return ourselves for the things we implement (except
3997 : // nsIInterfaceRequestor) and the XHR for the rest.
3998 0 : if (!aIID.Equals(NS_GET_IID(nsIInterfaceRequestor))) {
3999 0 : nsresult rv = QueryInterface(aIID, aResult);
4000 0 : if (NS_SUCCEEDED(rv)) {
4001 0 : return rv;
4002 : }
4003 : }
4004 :
4005 0 : return mXHR->GetInterface(aIID, aResult);
4006 : }
4007 :
4008 3 : ArrayBufferBuilder::ArrayBufferBuilder()
4009 : : mDataPtr(nullptr),
4010 : mCapacity(0),
4011 : mLength(0),
4012 3 : mMapPtr(nullptr)
4013 : {
4014 3 : }
4015 :
4016 0 : ArrayBufferBuilder::~ArrayBufferBuilder()
4017 : {
4018 0 : reset();
4019 0 : }
4020 :
4021 : void
4022 7 : ArrayBufferBuilder::reset()
4023 : {
4024 7 : if (mDataPtr) {
4025 0 : JS_free(nullptr, mDataPtr);
4026 : }
4027 :
4028 7 : if (mMapPtr) {
4029 0 : JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
4030 0 : mMapPtr = nullptr;
4031 : }
4032 :
4033 7 : mDataPtr = nullptr;
4034 7 : mCapacity = mLength = 0;
4035 7 : }
4036 :
4037 : bool
4038 0 : ArrayBufferBuilder::setCapacity(uint32_t aNewCap)
4039 : {
4040 0 : MOZ_ASSERT(!mMapPtr);
4041 :
4042 : // To ensure that realloc won't free mDataPtr, use a size of 1
4043 : // instead of 0.
4044 0 : uint8_t* newdata = (uint8_t *) js_realloc(mDataPtr, aNewCap ? aNewCap : 1);
4045 :
4046 0 : if (!newdata) {
4047 0 : return false;
4048 : }
4049 :
4050 0 : if (aNewCap > mCapacity) {
4051 0 : memset(newdata + mCapacity, 0, aNewCap - mCapacity);
4052 : }
4053 :
4054 0 : mDataPtr = newdata;
4055 0 : mCapacity = aNewCap;
4056 0 : if (mLength > aNewCap) {
4057 0 : mLength = aNewCap;
4058 : }
4059 :
4060 0 : return true;
4061 : }
4062 :
4063 : bool
4064 0 : ArrayBufferBuilder::append(const uint8_t *aNewData, uint32_t aDataLen,
4065 : uint32_t aMaxGrowth)
4066 : {
4067 0 : MOZ_ASSERT(!mMapPtr);
4068 :
4069 0 : CheckedUint32 neededCapacity = mLength;
4070 0 : neededCapacity += aDataLen;
4071 0 : if (!neededCapacity.isValid()) {
4072 0 : return false;
4073 : }
4074 0 : if (mLength + aDataLen > mCapacity) {
4075 0 : CheckedUint32 newcap = mCapacity;
4076 : // Double while under aMaxGrowth or if not specified.
4077 0 : if (!aMaxGrowth || mCapacity < aMaxGrowth) {
4078 0 : newcap *= 2;
4079 : } else {
4080 0 : newcap += aMaxGrowth;
4081 : }
4082 :
4083 0 : if (!newcap.isValid()) {
4084 0 : return false;
4085 : }
4086 :
4087 : // But make sure there's always enough to satisfy our request.
4088 0 : if (newcap.value() < neededCapacity.value()) {
4089 0 : newcap = neededCapacity;
4090 : }
4091 :
4092 0 : if (!setCapacity(newcap.value())) {
4093 0 : return false;
4094 : }
4095 : }
4096 :
4097 : // Assert that the region isn't overlapping so we can memcpy.
4098 0 : MOZ_ASSERT(!areOverlappingRegions(aNewData, aDataLen, mDataPtr + mLength,
4099 : aDataLen));
4100 :
4101 0 : memcpy(mDataPtr + mLength, aNewData, aDataLen);
4102 0 : mLength += aDataLen;
4103 :
4104 0 : return true;
4105 : }
4106 :
4107 : JSObject*
4108 0 : ArrayBufferBuilder::getArrayBuffer(JSContext* aCx)
4109 : {
4110 0 : if (mMapPtr) {
4111 0 : JSObject* obj = JS_NewMappedArrayBufferWithContents(aCx, mLength, mMapPtr);
4112 0 : if (!obj) {
4113 0 : JS_ReleaseMappedArrayBufferContents(mMapPtr, mLength);
4114 : }
4115 0 : mMapPtr = nullptr;
4116 :
4117 : // The memory-mapped contents will be released when the ArrayBuffer becomes
4118 : // detached or is GC'd.
4119 0 : return obj;
4120 : }
4121 :
4122 : // we need to check for mLength == 0, because nothing may have been
4123 : // added
4124 0 : if (mCapacity > mLength || mLength == 0) {
4125 0 : if (!setCapacity(mLength)) {
4126 0 : return nullptr;
4127 : }
4128 : }
4129 :
4130 0 : JSObject* obj = JS_NewArrayBufferWithContents(aCx, mLength, mDataPtr);
4131 0 : mLength = mCapacity = 0;
4132 0 : if (!obj) {
4133 0 : js_free(mDataPtr);
4134 : }
4135 0 : mDataPtr = nullptr;
4136 0 : return obj;
4137 : }
4138 :
4139 : nsresult
4140 0 : ArrayBufferBuilder::mapToFileInPackage(const nsCString& aFile,
4141 : nsIFile* aJarFile)
4142 : {
4143 : nsresult rv;
4144 :
4145 : // Open Jar file to get related attributes of target file.
4146 0 : RefPtr<nsZipArchive> zip = new nsZipArchive();
4147 0 : rv = zip->OpenArchive(aJarFile);
4148 0 : if (NS_FAILED(rv)) {
4149 0 : return rv;
4150 : }
4151 0 : nsZipItem* zipItem = zip->GetItem(aFile.get());
4152 0 : if (!zipItem) {
4153 0 : return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
4154 : }
4155 :
4156 : // If file was added to the package as stored(uncompressed), map to the
4157 : // offset of file in zip package.
4158 0 : if (!zipItem->Compression()) {
4159 0 : uint32_t offset = zip->GetDataOffset(zipItem);
4160 0 : uint32_t size = zipItem->RealSize();
4161 0 : mozilla::AutoFDClose pr_fd;
4162 0 : rv = aJarFile->OpenNSPRFileDesc(PR_RDONLY, 0, &pr_fd.rwget());
4163 0 : if (NS_FAILED(rv)) {
4164 0 : return rv;
4165 : }
4166 0 : mMapPtr = JS_CreateMappedArrayBufferContents(PR_FileDesc2NativeHandle(pr_fd),
4167 : offset, size);
4168 0 : if (mMapPtr) {
4169 0 : mLength = size;
4170 0 : return NS_OK;
4171 : }
4172 : }
4173 0 : return NS_ERROR_FAILURE;
4174 : }
4175 :
4176 : /* static */ bool
4177 0 : ArrayBufferBuilder::areOverlappingRegions(const uint8_t* aStart1,
4178 : uint32_t aLength1,
4179 : const uint8_t* aStart2,
4180 : uint32_t aLength2)
4181 : {
4182 0 : const uint8_t* end1 = aStart1 + aLength1;
4183 0 : const uint8_t* end2 = aStart2 + aLength2;
4184 :
4185 0 : const uint8_t* max_start = aStart1 > aStart2 ? aStart1 : aStart2;
4186 0 : const uint8_t* min_end = end1 < end2 ? end1 : end2;
4187 :
4188 0 : return max_start < min_end;
4189 : }
4190 :
4191 : RequestHeaders::RequestHeader*
4192 0 : RequestHeaders::Find(const nsACString& aName)
4193 : {
4194 0 : const nsCaseInsensitiveCStringComparator ignoreCase;
4195 0 : for (RequestHeaders::RequestHeader& header : mHeaders) {
4196 0 : if (header.mName.Equals(aName, ignoreCase)) {
4197 0 : return &header;
4198 : }
4199 : }
4200 0 : return nullptr;
4201 : }
4202 :
4203 : bool
4204 0 : RequestHeaders::Has(const char* aName)
4205 : {
4206 0 : return Has(nsDependentCString(aName));
4207 : }
4208 :
4209 : bool
4210 0 : RequestHeaders::Has(const nsACString& aName)
4211 : {
4212 0 : return !!Find(aName);
4213 : }
4214 :
4215 : void
4216 0 : RequestHeaders::Get(const char* aName, nsACString& aValue)
4217 : {
4218 0 : Get(nsDependentCString(aName), aValue);
4219 0 : }
4220 :
4221 : void
4222 0 : RequestHeaders::Get(const nsACString& aName, nsACString& aValue)
4223 : {
4224 0 : RequestHeader* header = Find(aName);
4225 0 : if (header) {
4226 0 : aValue = header->mValue;
4227 : } else {
4228 0 : aValue.SetIsVoid(true);
4229 : }
4230 0 : }
4231 :
4232 : void
4233 0 : RequestHeaders::Set(const char* aName, const nsACString& aValue)
4234 : {
4235 0 : Set(nsDependentCString(aName), aValue);
4236 0 : }
4237 :
4238 : void
4239 0 : RequestHeaders::Set(const nsACString& aName, const nsACString& aValue)
4240 : {
4241 0 : RequestHeader* header = Find(aName);
4242 0 : if (header) {
4243 0 : header->mValue.Assign(aValue);
4244 : } else {
4245 : RequestHeader newHeader = {
4246 : nsCString(aName), nsCString(aValue)
4247 0 : };
4248 0 : mHeaders.AppendElement(newHeader);
4249 : }
4250 0 : }
4251 :
4252 : void
4253 0 : RequestHeaders::MergeOrSet(const char* aName, const nsACString& aValue)
4254 : {
4255 0 : MergeOrSet(nsDependentCString(aName), aValue);
4256 0 : }
4257 :
4258 : void
4259 0 : RequestHeaders::MergeOrSet(const nsACString& aName, const nsACString& aValue)
4260 : {
4261 0 : RequestHeader* header = Find(aName);
4262 0 : if (header) {
4263 0 : header->mValue.AppendLiteral(", ");
4264 0 : header->mValue.Append(aValue);
4265 : } else {
4266 : RequestHeader newHeader = {
4267 : nsCString(aName), nsCString(aValue)
4268 0 : };
4269 0 : mHeaders.AppendElement(newHeader);
4270 : }
4271 0 : }
4272 :
4273 : void
4274 3 : RequestHeaders::Clear()
4275 : {
4276 3 : mHeaders.Clear();
4277 3 : }
4278 :
4279 : void
4280 0 : RequestHeaders::ApplyToChannel(nsIHttpChannel* aHttpChannel) const
4281 : {
4282 0 : for (const RequestHeader& header : mHeaders) {
4283 0 : if (header.mValue.IsEmpty()) {
4284 : DebugOnly<nsresult> rv =
4285 0 : aHttpChannel->SetEmptyRequestHeader(header.mName);
4286 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
4287 : } else {
4288 : DebugOnly<nsresult> rv =
4289 0 : aHttpChannel->SetRequestHeader(header.mName, header.mValue, false);
4290 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
4291 : }
4292 : }
4293 0 : }
4294 :
4295 : void
4296 0 : RequestHeaders::GetCORSUnsafeHeaders(nsTArray<nsCString>& aArray) const
4297 : {
4298 : static const char *kCrossOriginSafeHeaders[] = {
4299 : "accept", "accept-language", "content-language", "content-type",
4300 : "last-event-id"
4301 : };
4302 : const uint32_t kCrossOriginSafeHeadersLength =
4303 0 : ArrayLength(kCrossOriginSafeHeaders);
4304 0 : for (const RequestHeader& header : mHeaders) {
4305 0 : bool safe = false;
4306 0 : for (uint32_t i = 0; i < kCrossOriginSafeHeadersLength; ++i) {
4307 0 : if (header.mName.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
4308 0 : safe = true;
4309 0 : break;
4310 : }
4311 : }
4312 0 : if (!safe) {
4313 0 : aArray.AppendElement(header.mName);
4314 : }
4315 : }
4316 0 : }
4317 :
4318 0 : RequestHeaders::CharsetIterator::CharsetIterator(nsACString& aSource) :
4319 : mValid(false),
4320 : mCurPos(-1),
4321 : mCurLen(-1),
4322 0 : mCutoff(aSource.Length()),
4323 0 : mSource(aSource)
4324 : {
4325 0 : }
4326 :
4327 : bool
4328 0 : RequestHeaders::CharsetIterator::Equals(const nsACString& aOther,
4329 : const nsCStringComparator& aCmp) const
4330 : {
4331 0 : if (mValid) {
4332 0 : return Substring(mSource, mCurPos, mCurLen).Equals(aOther, aCmp);
4333 : } else {
4334 0 : return false;
4335 : }
4336 : }
4337 :
4338 : void
4339 0 : RequestHeaders::CharsetIterator::Replace(const nsACString& aReplacement)
4340 : {
4341 0 : if (mValid) {
4342 0 : mSource.Replace(mCurPos, mCurLen, aReplacement);
4343 0 : mCurLen = aReplacement.Length();
4344 : }
4345 0 : }
4346 :
4347 : bool
4348 0 : RequestHeaders::CharsetIterator::Next()
4349 : {
4350 : int32_t start, end;
4351 0 : nsAutoCString charset;
4352 :
4353 : // Look for another charset declaration in the string, limiting the
4354 : // search to only the characters before the parts we've already searched
4355 : // (before mCutoff), so that we don't find the same charset twice.
4356 0 : NS_ExtractCharsetFromContentType(Substring(mSource, 0, mCutoff),
4357 0 : charset, &mValid, &start, &end);
4358 :
4359 0 : if (!mValid) {
4360 0 : return false;
4361 : }
4362 :
4363 : // Everything after the = sign is the part of the charset we want.
4364 0 : mCurPos = mSource.FindChar('=', start) + 1;
4365 0 : mCurLen = end - mCurPos;
4366 :
4367 : // Special case: the extracted charset is quoted with single quotes.
4368 : // For the purpose of preserving what was set we want to handle them
4369 : // as delimiters (although they aren't really).
4370 0 : if (charset.Length() >= 2 &&
4371 0 : charset.First() == '\'' &&
4372 0 : charset.Last() == '\'') {
4373 0 : ++mCurPos;
4374 0 : mCurLen -= 2;
4375 : }
4376 :
4377 0 : mCutoff = start;
4378 :
4379 0 : return true;
4380 : }
4381 :
4382 : } // dom namespace
4383 : } // mozilla namespace
|