Coverage Report

Created: 2018-09-25 14:53

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