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