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