XMLHttpRequestWorker.cpp (73202B)
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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "XMLHttpRequestWorker.h" 8 9 #include "GeckoProfiler.h" 10 #include "XMLHttpRequestMainThread.h" 11 #include "XMLHttpRequestUpload.h" 12 #include "js/ArrayBuffer.h" // JS::Is{,Detached}ArrayBufferObject 13 #include "js/GCPolicyAPI.h" 14 #include "js/JSON.h" 15 #include "js/RootingAPI.h" // JS::{Handle,Heap,PersistentRooted} 16 #include "js/TracingAPI.h" 17 #include "js/Value.h" // JS::{Undefined,}Value 18 #include "jsfriendapi.h" 19 #include "mozilla/HoldDropJSObjects.h" 20 #include "mozilla/UniquePtr.h" 21 #include "mozilla/dom/Event.h" 22 #include "mozilla/dom/Exceptions.h" 23 #include "mozilla/dom/File.h" 24 #include "mozilla/dom/FormData.h" 25 #include "mozilla/dom/ProgressEvent.h" 26 #include "mozilla/dom/SerializedStackHolder.h" 27 #include "mozilla/dom/StreamBlobImpl.h" 28 #include "mozilla/dom/StructuredCloneHolder.h" 29 #include "mozilla/dom/URLSearchParams.h" 30 #include "mozilla/dom/WorkerRef.h" 31 #include "mozilla/dom/WorkerRunnable.h" 32 #include "mozilla/dom/WorkerScope.h" 33 #include "mozilla/dom/XMLHttpRequestBinding.h" 34 #include "nsComponentManagerUtils.h" 35 #include "nsContentUtils.h" 36 #include "nsIDOMEventListener.h" 37 #include "nsJSUtils.h" 38 #include "nsThreadUtils.h" 39 40 extern mozilla::LazyLogModule gXMLHttpRequestLog; 41 42 namespace mozilla::dom { 43 44 using EventType = XMLHttpRequest::EventType; 45 using Events = XMLHttpRequest::Events; 46 47 /** 48 * XMLHttpRequest in workers 49 * 50 * XHR in workers is implemented by proxying calls/events/etc between the 51 * worker thread and an XMLHttpRequest on the main thread. The glue 52 * object here is the Proxy, which lives on both threads. All other objects 53 * live on either the main thread (the XMLHttpRequest) or the worker thread 54 * (the worker and XHR private objects). 55 * 56 * The main thread XHR is always operated in async mode, even for sync XHR 57 * in workers. Calls made on the worker thread are proxied to the main thread 58 * synchronously (meaning the worker thread is blocked until the call 59 * returns). Each proxied call spins up a sync queue, which captures any 60 * synchronously dispatched events and ensures that they run synchronously 61 * on the worker as well. Asynchronously dispatched events are posted to the 62 * worker thread to run asynchronously. Some of the XHR state is mirrored on 63 * the worker thread to avoid needing a cross-thread call on every property 64 * access. 65 * 66 * The XHR private is stored in the private slot of the XHR JSObject on the 67 * worker thread. It is destroyed when that JSObject is GCd. The private 68 * roots its JSObject while network activity is in progress. It also 69 * adds itself as a feature to the worker to give itself a chance to clean up 70 * if the worker goes away during an XHR call. It is important that the 71 * rooting and feature registration (collectively called pinning) happens at 72 * the proper times. If we pin for too long we can cause memory leaks or even 73 * shutdown hangs. If we don't pin for long enough we introduce a GC hazard. 74 * 75 * The XHR is pinned from the time Send is called to roughly the time loadend 76 * is received. There are some complications involved with Abort and XHR 77 * reuse. We maintain a counter on the main thread of how many times Send was 78 * called on this XHR, and we decrement the counter every time we receive a 79 * loadend event. When the counter reaches zero we dispatch a runnable to the 80 * worker thread to unpin the XHR. We only decrement the counter if the 81 * dispatch was successful, because the worker may no longer be accepting 82 * regular runnables. In the event that we reach Proxy::Teardown and there 83 * the outstanding Send count is still non-zero, we dispatch a control 84 * runnable which is guaranteed to run. 85 * 86 * NB: Some of this could probably be simplified now that we have the 87 * inner/outer channel ids. 88 */ 89 90 class Proxy final : public nsIDOMEventListener { 91 public: 92 // Read on multiple threads. 93 RefPtr<ThreadSafeWorkerRef> mWorkerRef; 94 const ClientInfo mClientInfo; 95 const Maybe<ServiceWorkerDescriptor> mController; 96 97 // Only ever dereferenced and/or checked on the worker thread. Cleared 98 // explicitly on the worker thread inside XMLHttpRequestWorker::ReleaseProxy. 99 WeakPtr<XMLHttpRequestWorker> mXMLHttpRequestPrivate; 100 101 // XHR Params: 102 bool mMozAnon; 103 bool mMozSystem; 104 105 // Only touched on the main thread. 106 RefPtr<XMLHttpRequestMainThread> mXHR; 107 RefPtr<XMLHttpRequestUpload> mXHRUpload; 108 nsCOMPtr<nsIEventTarget> mSyncLoopTarget; 109 nsCOMPtr<nsIEventTarget> mSyncEventResponseTarget; 110 uint32_t mInnerEventStreamId; 111 uint32_t mInnerChannelId; 112 uint32_t mOutstandingSendCount; 113 114 // Only touched on the worker thread. 115 uint32_t mOuterChannelId; 116 uint32_t mOpenCount; 117 uint64_t mLastLoaded; 118 uint64_t mLastTotal; 119 uint64_t mLastUploadLoaded; 120 uint64_t mLastUploadTotal; 121 nsresult mLastErrorDetailAtLoadend; 122 bool mIsSyncXHR; 123 bool mLastLengthComputable; 124 bool mLastUploadLengthComputable; 125 bool mSeenUploadLoadStart; 126 bool mSeenUploadLoadEnd; 127 128 // Only touched on the main thread. 129 bool mUploadEventListenersAttached; 130 bool mMainThreadSeenLoadStart; 131 bool mInOpen; 132 133 public: 134 Proxy(XMLHttpRequestWorker* aXHRPrivate, const ClientInfo& aClientInfo, 135 const Maybe<ServiceWorkerDescriptor>& aController, bool aMozAnon, 136 bool aMozSystem) 137 : mClientInfo(aClientInfo), 138 mController(aController), 139 mXMLHttpRequestPrivate(aXHRPrivate), 140 mMozAnon(aMozAnon), 141 mMozSystem(aMozSystem), 142 mInnerEventStreamId(aXHRPrivate->EventStreamId()), 143 mInnerChannelId(0), 144 mOutstandingSendCount(0), 145 mOuterChannelId(0), 146 mOpenCount(0), 147 mLastLoaded(0), 148 mLastTotal(0), 149 mLastUploadLoaded(0), 150 mLastUploadTotal(0), 151 mLastErrorDetailAtLoadend(NS_OK), 152 mIsSyncXHR(false), 153 mLastLengthComputable(false), 154 mLastUploadLengthComputable(false), 155 mSeenUploadLoadStart(false), 156 mSeenUploadLoadEnd(false), 157 mUploadEventListenersAttached(false), 158 mMainThreadSeenLoadStart(false), 159 mInOpen(false) {} 160 161 NS_DECL_THREADSAFE_ISUPPORTS 162 NS_DECL_NSIDOMEVENTLISTENER 163 164 // This method is called in OpenRunnable::MainThreadRunInternal(). The 165 // OpenRunnable has to provide a valid WorkerPrivate for the Proxy's 166 // initialization since OpenRunnable is a WorkerMainThreadRunnable, which 167 // holds a ThreadSafeWorkerRef and blocks Worker's shutdown until the 168 // execution returns back to the worker thread. 169 bool Init(WorkerPrivate* aWorkerPrivate); 170 171 void Teardown(); 172 173 bool AddRemoveEventListeners(bool aUpload, bool aAdd); 174 175 void Reset() { 176 AssertIsOnMainThread(); 177 178 if (mUploadEventListenersAttached) { 179 AddRemoveEventListeners(true, false); 180 } 181 } 182 183 already_AddRefed<nsIEventTarget> GetEventTarget() { 184 AssertIsOnMainThread(); 185 186 nsCOMPtr<nsIEventTarget> target = 187 mSyncEventResponseTarget ? mSyncEventResponseTarget : mSyncLoopTarget; 188 return target.forget(); 189 } 190 191 WorkerPrivate* Private() const { 192 if (mWorkerRef) { 193 return mWorkerRef->Private(); 194 } 195 return nullptr; 196 } 197 198 #ifdef DEBUG 199 void DebugStoreWorkerRef(RefPtr<ThreadSafeWorkerRef>& aWorkerRef) { 200 MOZ_ASSERT(!NS_IsMainThread()); 201 MutexAutoLock lock(mXHR->mTSWorkerRefMutex); 202 mXHR->mTSWorkerRef = aWorkerRef; 203 } 204 205 void DebugForgetWorkerRef() { 206 MOZ_ASSERT(!NS_IsMainThread()); 207 MutexAutoLock lock(mXHR->mTSWorkerRefMutex); 208 mXHR->mTSWorkerRef = nullptr; 209 } 210 #endif 211 212 private: 213 ~Proxy() { 214 MOZ_ASSERT(!mXHR); 215 MOZ_ASSERT(!mXHRUpload); 216 MOZ_ASSERT(!mOutstandingSendCount); 217 } 218 }; 219 220 class WorkerThreadProxySyncRunnable : public WorkerMainThreadRunnable { 221 protected: 222 RefPtr<Proxy> mProxy; 223 224 private: 225 // mErrorCode is set on the main thread by MainThreadRun and it's used at the 226 // end of the Dispatch() to return the error code. 227 nsresult mErrorCode; 228 229 public: 230 WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy) 231 : WorkerMainThreadRunnable(aWorkerPrivate, "XHR"_ns), 232 mProxy(aProxy), 233 mErrorCode(NS_OK) { 234 MOZ_ASSERT(aWorkerPrivate); 235 MOZ_ASSERT(aProxy); 236 aWorkerPrivate->AssertIsOnWorkerThread(); 237 } 238 239 void Dispatch(WorkerPrivate* aWorkerPrivate, WorkerStatus aFailStatus, 240 ErrorResult& aRv) { 241 MOZ_ASSERT(aWorkerPrivate); 242 aWorkerPrivate->AssertIsOnWorkerThread(); 243 244 WorkerMainThreadRunnable::Dispatch(aWorkerPrivate, aFailStatus, aRv); 245 if (NS_WARN_IF(aRv.Failed())) { 246 return; 247 } 248 249 if (NS_FAILED(mErrorCode)) { 250 aRv.Throw(mErrorCode); 251 } 252 } 253 254 protected: 255 virtual ~WorkerThreadProxySyncRunnable() = default; 256 257 virtual void RunOnMainThread(ErrorResult& aRv) = 0; 258 259 private: 260 virtual bool MainThreadRun() override; 261 }; 262 263 class SendRunnable final : public WorkerThreadProxySyncRunnable { 264 RefPtr<BlobImpl> mBlobImpl; 265 // WorkerMainThreadRunnable has a member mSyncLoopTarget to perform the 266 // synchronous dispatch. The mSyncLoopTarget will be released after 267 // WorkerMainThreadRunnable::Dispatch(). 268 // However, to perform sync XHR, an additional sync loop is needed to wait 269 // for the sync XHR response. This is because XMLHttpRequestMainThread 270 // performs xhr in async way, and it causes the response to not be 271 // available before SendRunnable returns back to the worker thread. 272 // This is the event target to the additional sync loop. 273 nsCOMPtr<nsIEventTarget> mSyncXHRSyncLoopTarget; 274 bool mHasUploadListeners; 275 276 public: 277 SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 278 BlobImpl* aBlobImpl) 279 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), 280 mBlobImpl(aBlobImpl), 281 mHasUploadListeners(false) {} 282 283 void SetHaveUploadListeners(bool aHasUploadListeners) { 284 mHasUploadListeners = aHasUploadListeners; 285 } 286 287 void SetSyncXHRSyncLoopTarget(nsIEventTarget* aSyncXHRSyncLoopTarget) { 288 mSyncXHRSyncLoopTarget = aSyncXHRSyncLoopTarget; 289 } 290 291 private: 292 ~SendRunnable() = default; 293 294 virtual void RunOnMainThread(ErrorResult& aRv) override; 295 }; 296 297 namespace { 298 299 class MainThreadProxyRunnable : public MainThreadWorkerSyncRunnable { 300 protected: 301 RefPtr<Proxy> mProxy; 302 303 MainThreadProxyRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 304 const char* aName = "MainThreadProxyRunnable") 305 : MainThreadWorkerSyncRunnable(aProxy->GetEventTarget(), aName), 306 mProxy(aProxy) { 307 MOZ_ASSERT(aProxy); 308 } 309 310 virtual ~MainThreadProxyRunnable() = default; 311 }; 312 313 class AsyncTeardownRunnable final : public Runnable { 314 RefPtr<Proxy> mProxy; 315 316 public: 317 explicit AsyncTeardownRunnable(Proxy* aProxy) 318 : Runnable("dom::AsyncTeardownRunnable"), mProxy(aProxy) { 319 MOZ_ASSERT(aProxy); 320 } 321 322 private: 323 ~AsyncTeardownRunnable() = default; 324 325 NS_IMETHOD 326 Run() override { 327 AssertIsOnMainThread(); 328 329 mProxy->Teardown(); 330 mProxy = nullptr; 331 332 return NS_OK; 333 } 334 }; 335 336 class LoadStartDetectionRunnable final : public Runnable, 337 public nsIDOMEventListener { 338 RefPtr<Proxy> mProxy; 339 RefPtr<XMLHttpRequest> mXHR; 340 uint32_t mChannelId; 341 bool mReceivedLoadStart; 342 343 class ProxyCompleteRunnable final : public MainThreadProxyRunnable { 344 uint32_t mChannelId; 345 346 public: 347 ProxyCompleteRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 348 uint32_t aChannelId) 349 : MainThreadProxyRunnable(aWorkerPrivate, aProxy, 350 "ProxyCompleteRunnable"), 351 mChannelId(aChannelId) {} 352 353 private: 354 ~ProxyCompleteRunnable() = default; 355 356 virtual bool WorkerRun(JSContext* aCx, 357 WorkerPrivate* aWorkerPrivate) override { 358 if (mChannelId != mProxy->mOuterChannelId) { 359 // Threads raced, this event is now obsolete. 360 return true; 361 } 362 363 if (mSyncLoopTarget) { 364 aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, NS_OK); 365 } 366 367 XMLHttpRequestWorker* xhrw = mProxy->mXMLHttpRequestPrivate.get(); 368 if (xhrw && xhrw->SendInProgress()) { 369 xhrw->Unpin(); 370 } 371 372 return true; 373 } 374 375 nsresult Cancel() override { return Run(); } 376 }; 377 378 public: 379 explicit LoadStartDetectionRunnable(Proxy* aProxy) 380 : Runnable("dom::LoadStartDetectionRunnable"), 381 mProxy(aProxy), 382 mXHR(aProxy->mXHR), 383 mChannelId(mProxy->mInnerChannelId), 384 mReceivedLoadStart(false) { 385 AssertIsOnMainThread(); 386 } 387 388 NS_DECL_ISUPPORTS_INHERITED 389 NS_DECL_NSIRUNNABLE 390 NS_DECL_NSIDOMEVENTLISTENER 391 392 bool RegisterAndDispatch() { 393 AssertIsOnMainThread(); 394 395 if (NS_FAILED( 396 mXHR->AddEventListener(Events::loadstart, this, false, false))) { 397 NS_WARNING("Failed to add event listener!"); 398 return false; 399 } 400 401 MOZ_ASSERT_DEBUG_OR_FUZZING(mProxy && mProxy->Private()); 402 403 return NS_SUCCEEDED(mProxy->Private()->DispatchToMainThread(this)); 404 } 405 406 private: 407 ~LoadStartDetectionRunnable() { AssertIsOnMainThread(); } 408 }; 409 410 class EventRunnable final : public MainThreadProxyRunnable { 411 const EventType& mType; 412 UniquePtr<XMLHttpRequestWorker::ResponseData> mResponseData; 413 nsCString mResponseURL; 414 nsCString mStatusText; 415 uint64_t mLoaded; 416 uint64_t mTotal; 417 uint32_t mEventStreamId; 418 uint32_t mStatus; 419 uint16_t mReadyState; 420 bool mUploadEvent; 421 bool mProgressEvent; 422 bool mLengthComputable; 423 nsresult mStatusResult; 424 nsresult mErrorDetail; 425 // mScopeObj is used in PreDispatch only. We init it in our constructor, and 426 // reset() in PreDispatch, to ensure that it's not still linked into the 427 // runtime once we go off-thread. 428 JS::PersistentRooted<JSObject*> mScopeObj; 429 430 public: 431 EventRunnable(Proxy* aProxy, bool aUploadEvent, const EventType& aType, 432 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal, 433 JS::Handle<JSObject*> aScopeObj) 434 : MainThreadProxyRunnable(aProxy->Private(), aProxy, "EventRunnable"), 435 mType(aType), 436 mResponseData(new XMLHttpRequestWorker::ResponseData()), 437 mLoaded(aLoaded), 438 mTotal(aTotal), 439 mEventStreamId(aProxy->mInnerEventStreamId), 440 mStatus(0), 441 mReadyState(0), 442 mUploadEvent(aUploadEvent), 443 mProgressEvent(true), 444 mLengthComputable(aLengthComputable), 445 mStatusResult(NS_OK), 446 mErrorDetail(NS_OK), 447 mScopeObj(RootingCx(), aScopeObj) {} 448 449 EventRunnable(Proxy* aProxy, bool aUploadEvent, const EventType& aType, 450 JS::Handle<JSObject*> aScopeObj) 451 : MainThreadProxyRunnable(aProxy->Private(), aProxy, "EventRunnable"), 452 mType(aType), 453 mResponseData(new XMLHttpRequestWorker::ResponseData()), 454 mLoaded(0), 455 mTotal(0), 456 mEventStreamId(aProxy->mInnerEventStreamId), 457 mStatus(0), 458 mReadyState(0), 459 mUploadEvent(aUploadEvent), 460 mProgressEvent(false), 461 mLengthComputable(0), 462 mStatusResult(NS_OK), 463 mErrorDetail(NS_OK), 464 mScopeObj(RootingCx(), aScopeObj) {} 465 466 private: 467 ~EventRunnable() = default; 468 469 bool PreDispatch(WorkerPrivate* /* unused */) final; 470 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override; 471 }; 472 473 class SyncTeardownRunnable final : public WorkerThreadProxySyncRunnable { 474 public: 475 SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy) 476 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy) {} 477 478 private: 479 ~SyncTeardownRunnable() = default; 480 481 virtual void RunOnMainThread(ErrorResult& aRv) override { 482 mProxy->Teardown(); 483 MOZ_ASSERT(!mProxy->mSyncLoopTarget); 484 } 485 }; 486 487 class SetBackgroundRequestRunnable final 488 : public WorkerThreadProxySyncRunnable { 489 bool mValue; 490 491 public: 492 SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 493 bool aValue) 494 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue) {} 495 496 private: 497 ~SetBackgroundRequestRunnable() = default; 498 499 virtual void RunOnMainThread(ErrorResult& aRv) override { 500 // XXXedgar, do we intend to ignore the errors? 501 mProxy->mXHR->SetMozBackgroundRequest(mValue, aRv); 502 } 503 }; 504 505 class SetWithCredentialsRunnable final : public WorkerThreadProxySyncRunnable { 506 bool mValue; 507 508 public: 509 SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 510 bool aValue) 511 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue) {} 512 513 private: 514 ~SetWithCredentialsRunnable() = default; 515 516 virtual void RunOnMainThread(ErrorResult& aRv) override { 517 mProxy->mXHR->SetWithCredentials(mValue, aRv); 518 } 519 }; 520 521 class SetResponseTypeRunnable final : public WorkerThreadProxySyncRunnable { 522 XMLHttpRequestResponseType mResponseType; 523 524 public: 525 SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 526 XMLHttpRequestResponseType aResponseType) 527 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), 528 mResponseType(aResponseType) {} 529 530 XMLHttpRequestResponseType ResponseType() { return mResponseType; } 531 532 private: 533 ~SetResponseTypeRunnable() = default; 534 535 virtual void RunOnMainThread(ErrorResult& aRv) override { 536 mProxy->mXHR->SetResponseTypeRaw(mResponseType); 537 mResponseType = mProxy->mXHR->ResponseType(); 538 } 539 }; 540 541 class SetTimeoutRunnable final : public WorkerThreadProxySyncRunnable { 542 uint32_t mTimeout; 543 544 public: 545 SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 546 uint32_t aTimeout) 547 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), 548 mTimeout(aTimeout) {} 549 550 private: 551 ~SetTimeoutRunnable() = default; 552 553 virtual void RunOnMainThread(ErrorResult& aRv) override { 554 mProxy->mXHR->SetTimeout(mTimeout, aRv); 555 } 556 }; 557 558 class AbortRunnable final : public WorkerThreadProxySyncRunnable { 559 public: 560 AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy) 561 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy) {} 562 563 private: 564 ~AbortRunnable() = default; 565 566 virtual void RunOnMainThread(ErrorResult& aRv) override; 567 }; 568 569 class GetAllResponseHeadersRunnable final 570 : public WorkerThreadProxySyncRunnable { 571 nsCString& mResponseHeaders; 572 573 public: 574 GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 575 nsCString& aResponseHeaders) 576 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), 577 mResponseHeaders(aResponseHeaders) {} 578 579 private: 580 ~GetAllResponseHeadersRunnable() = default; 581 582 virtual void RunOnMainThread(ErrorResult& aRv) override { 583 mProxy->mXHR->GetAllResponseHeaders(mResponseHeaders, aRv); 584 } 585 }; 586 587 class GetResponseHeaderRunnable final : public WorkerThreadProxySyncRunnable { 588 const nsCString mHeader; 589 nsCString& mValue; 590 591 public: 592 GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 593 const nsACString& aHeader, nsCString& aValue) 594 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), 595 mHeader(aHeader), 596 mValue(aValue) {} 597 598 private: 599 ~GetResponseHeaderRunnable() = default; 600 601 virtual void RunOnMainThread(ErrorResult& aRv) override { 602 mProxy->mXHR->GetResponseHeader(mHeader, mValue, aRv); 603 } 604 }; 605 606 class OpenRunnable final : public WorkerThreadProxySyncRunnable { 607 nsCString mMethod; 608 nsCString mURL; 609 Optional<nsACString> mUser; 610 nsCString mUserStr; 611 Optional<nsACString> mPassword; 612 nsCString mPasswordStr; 613 bool mBackgroundRequest; 614 bool mWithCredentials; 615 uint32_t mTimeout; 616 XMLHttpRequestResponseType mResponseType; 617 const nsString mMimeTypeOverride; 618 619 // Remember the worker thread's stack when the XHR was opened, so that it can 620 // be passed on to the net monitor. 621 UniquePtr<SerializedStackHolder> mOriginStack; 622 623 // Remember the worker thread's stack when the XHR was opened for profiling 624 // purposes. 625 UniquePtr<ProfileChunkedBuffer> mSource; 626 627 public: 628 OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 629 const nsACString& aMethod, const nsACString& aURL, 630 const Optional<nsACString>& aUser, 631 const Optional<nsACString>& aPassword, bool aBackgroundRequest, 632 bool aWithCredentials, uint32_t aTimeout, 633 XMLHttpRequestResponseType aResponseType, 634 const nsString& aMimeTypeOverride, 635 UniquePtr<SerializedStackHolder> aOriginStack, 636 UniquePtr<ProfileChunkedBuffer> aSource = nullptr) 637 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), 638 mMethod(aMethod), 639 mURL(aURL), 640 mBackgroundRequest(aBackgroundRequest), 641 mWithCredentials(aWithCredentials), 642 mTimeout(aTimeout), 643 mResponseType(aResponseType), 644 mMimeTypeOverride(aMimeTypeOverride), 645 mOriginStack(std::move(aOriginStack)), 646 mSource(std::move(aSource)) { 647 if (aUser.WasPassed()) { 648 mUserStr = aUser.Value(); 649 mUser = &mUserStr; 650 } 651 if (aPassword.WasPassed()) { 652 mPasswordStr = aPassword.Value(); 653 mPassword = &mPasswordStr; 654 } 655 } 656 657 private: 658 ~OpenRunnable() = default; 659 660 virtual void RunOnMainThread(ErrorResult& aRv) override { 661 MOZ_ASSERT_IF(mProxy->mWorkerRef, 662 mProxy->mWorkerRef->Private() == mWorkerRef->Private()); 663 664 // mProxy wants a valid ThreadSafeWorkerRef for the duration of our call, 665 // but mProxy->mWorkerRef may be null if a send is not currently active, 666 // so save the existing value for the duration of the call. 667 RefPtr<ThreadSafeWorkerRef> oldWorker = std::move(mProxy->mWorkerRef); 668 669 // WorkerMainThreadRunnable::mWorkerRef must not be nullptr here, since 670 // when get here, it means this WorkerMainThreadRunnable had already be 671 // dispatched successfully and the execution is on the main thread. 672 MOZ_ASSERT_DEBUG_OR_FUZZING(mWorkerRef); 673 674 // Set mProxy->mWorkerRef as OpenRunnable::mWorkerRef which is from 675 // WorkerMainThreadRunnable during the runnable execution. 676 // Let OpenRunnable keep a reference for dispatching 677 // MainThreadStopSyncRunnable back to the Worker thread after the main 678 // thread execution completes. 679 mProxy->mWorkerRef = mWorkerRef; 680 681 MainThreadRunInternal(aRv); 682 683 // Restore the previous activated WorkerRef for send. 684 mProxy->mWorkerRef = std::move(oldWorker); 685 } 686 687 void MainThreadRunInternal(ErrorResult& aRv); 688 }; 689 690 class SetRequestHeaderRunnable final : public WorkerThreadProxySyncRunnable { 691 nsCString mHeader; 692 nsCString mValue; 693 694 public: 695 SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 696 const nsACString& aHeader, const nsACString& aValue) 697 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), 698 mHeader(aHeader), 699 mValue(aValue) {} 700 701 private: 702 ~SetRequestHeaderRunnable() = default; 703 704 virtual void RunOnMainThread(ErrorResult& aRv) override { 705 mProxy->mXHR->SetRequestHeader(mHeader, mValue, aRv); 706 } 707 }; 708 709 class OverrideMimeTypeRunnable final : public WorkerThreadProxySyncRunnable { 710 nsString mMimeType; 711 712 public: 713 OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy, 714 const nsAString& aMimeType) 715 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), 716 mMimeType(aMimeType) {} 717 718 private: 719 ~OverrideMimeTypeRunnable() = default; 720 721 virtual void RunOnMainThread(ErrorResult& aRv) override { 722 mProxy->mXHR->OverrideMimeType(mMimeType, aRv); 723 } 724 }; 725 726 class AutoUnpinXHR { 727 XMLHttpRequestWorker* mXMLHttpRequestPrivate; 728 729 public: 730 explicit AutoUnpinXHR(XMLHttpRequestWorker* aXMLHttpRequestPrivate) 731 : mXMLHttpRequestPrivate(aXMLHttpRequestPrivate) { 732 MOZ_ASSERT(aXMLHttpRequestPrivate); 733 } 734 735 ~AutoUnpinXHR() { 736 if (mXMLHttpRequestPrivate) { 737 mXMLHttpRequestPrivate->Unpin(); 738 } 739 } 740 741 void Clear() { mXMLHttpRequestPrivate = nullptr; } 742 }; 743 744 } // namespace 745 746 bool Proxy::Init(WorkerPrivate* aWorkerPrivate) { 747 AssertIsOnMainThread(); 748 MOZ_ASSERT(aWorkerPrivate); 749 750 if (mXHR) { 751 return true; 752 } 753 754 nsPIDOMWindowInner* ownerWindow = aWorkerPrivate->GetWindow(); 755 if (ownerWindow && !ownerWindow->IsCurrentInnerWindow()) { 756 NS_WARNING("Window has navigated, cannot create XHR here."); 757 return false; 758 } 759 760 mXHR = new XMLHttpRequestMainThread(ownerWindow ? ownerWindow->AsGlobal() 761 : nullptr); 762 mXHR->Construct(aWorkerPrivate->GetPrincipal(), 763 aWorkerPrivate->CookieJarSettings(), true, 764 aWorkerPrivate->GetBaseURI(), aWorkerPrivate->GetLoadGroup(), 765 aWorkerPrivate->GetPerformanceStorage(), 766 aWorkerPrivate->CSPEventListener()); 767 768 mXHR->SetParameters(mMozAnon, mMozSystem); 769 mXHR->SetClientInfoAndController(mClientInfo, mController); 770 771 ErrorResult rv; 772 mXHRUpload = mXHR->GetUpload(rv); 773 if (NS_WARN_IF(rv.Failed())) { 774 mXHR = nullptr; 775 return false; 776 } 777 778 if (!AddRemoveEventListeners(false, true)) { 779 mXHR = nullptr; 780 mXHRUpload = nullptr; 781 return false; 782 } 783 784 return true; 785 } 786 787 void Proxy::Teardown() { 788 AssertIsOnMainThread(); 789 790 if (mXHR) { 791 Reset(); 792 793 // NB: We are intentionally dropping events coming from xhr.abort on the 794 // floor. 795 AddRemoveEventListeners(false, false); 796 797 ErrorResult rv; 798 mXHR->Abort(rv); 799 if (NS_WARN_IF(rv.Failed())) { 800 rv.SuppressException(); 801 } 802 803 if (mOutstandingSendCount) { 804 if (mSyncLoopTarget) { 805 // We have an unclosed sync loop. Fix that now. 806 RefPtr<MainThreadStopSyncLoopRunnable> runnable = 807 new MainThreadStopSyncLoopRunnable(std::move(mSyncLoopTarget), 808 NS_ERROR_FAILURE); 809 MOZ_ALWAYS_TRUE(runnable->Dispatch(mWorkerRef->Private())); 810 } 811 812 mOutstandingSendCount = 0; 813 } 814 815 mWorkerRef = nullptr; 816 mXHRUpload = nullptr; 817 mXHR = nullptr; 818 } 819 820 MOZ_ASSERT(!mWorkerRef); 821 MOZ_ASSERT(!mSyncLoopTarget); 822 // If there are rare edge cases left that violate our invariants 823 // just ensure that they won't harm us too much. 824 mWorkerRef = nullptr; 825 mSyncLoopTarget = nullptr; 826 } 827 828 bool Proxy::AddRemoveEventListeners(bool aUpload, bool aAdd) { 829 AssertIsOnMainThread(); 830 831 NS_ASSERTION(!aUpload || (mUploadEventListenersAttached && !aAdd) || 832 (!mUploadEventListenersAttached && aAdd), 833 "Messed up logic for upload listeners!"); 834 835 RefPtr<DOMEventTargetHelper> targetHelper = 836 aUpload ? static_cast<XMLHttpRequestUpload*>(mXHRUpload.get()) 837 : static_cast<XMLHttpRequestEventTarget*>(mXHR.get()); 838 MOZ_ASSERT(targetHelper, "This should never fail!"); 839 840 for (const EventType* type : Events::All) { 841 if (aUpload && *type == Events::readystatechange) { 842 continue; 843 } 844 if (aAdd) { 845 if (NS_FAILED(targetHelper->AddEventListener(*type, this, false))) { 846 return false; 847 } 848 } else { 849 targetHelper->RemoveEventListener(*type, this, false); 850 } 851 } 852 853 if (aUpload) { 854 mUploadEventListenersAttached = aAdd; 855 } 856 857 return true; 858 } 859 860 NS_IMPL_ISUPPORTS(Proxy, nsIDOMEventListener) 861 862 NS_IMETHODIMP 863 Proxy::HandleEvent(Event* aEvent) { 864 AssertIsOnMainThread(); 865 866 // EventRunnable::WorkerRun will bail out if mXMLHttpRequestWorker is null, 867 // so we do not need to prevent the dispatch from the main thread such that 868 // we do not need to touch it off-worker-thread. 869 if (!mWorkerRef) { 870 NS_ERROR("Shouldn't get here!"); 871 return NS_OK; 872 } 873 874 nsAutoString _type; 875 aEvent->GetType(_type); 876 const EventType* typePtr = Events::Find(_type); 877 MOZ_DIAGNOSTIC_ASSERT(typePtr, "Shouldn't get non-XMLHttpRequest events"); 878 const EventType& type = *typePtr; 879 880 bool isUploadTarget = mXHR != aEvent->GetTarget(); 881 ProgressEvent* progressEvent = aEvent->AsProgressEvent(); 882 883 if (mInOpen && type == Events::readystatechange) { 884 if (mXHR->ReadyState() == 1) { 885 mInnerEventStreamId++; 886 } 887 } 888 889 { 890 AutoJSAPI jsapi; 891 JSObject* junkScope = xpc::UnprivilegedJunkScope(fallible); 892 if (!junkScope || !jsapi.Init(junkScope)) { 893 return NS_ERROR_FAILURE; 894 } 895 JSContext* cx = jsapi.cx(); 896 897 JS::Rooted<JS::Value> value(cx); 898 if (!GetOrCreateDOMReflectorNoWrap(cx, mXHR, &value)) { 899 return NS_ERROR_FAILURE; 900 } 901 902 JS::Rooted<JSObject*> scope(cx, &value.toObject()); 903 904 RefPtr<EventRunnable> runnable; 905 if (progressEvent) { 906 if (!mIsSyncXHR || type != Events::progress) { 907 runnable = new EventRunnable( 908 this, isUploadTarget, type, progressEvent->LengthComputable(), 909 progressEvent->Loaded(), progressEvent->Total(), scope); 910 } 911 } else { 912 runnable = new EventRunnable(this, isUploadTarget, type, scope); 913 } 914 915 if (runnable) { 916 runnable->Dispatch(mWorkerRef->Private()); 917 } 918 } 919 920 if (!isUploadTarget) { 921 if (type == Events::loadstart) { 922 mMainThreadSeenLoadStart = true; 923 } else if (mMainThreadSeenLoadStart && type == Events::loadend) { 924 mMainThreadSeenLoadStart = false; 925 926 RefPtr<LoadStartDetectionRunnable> runnable = 927 new LoadStartDetectionRunnable(this); 928 if (!runnable->RegisterAndDispatch()) { 929 NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!"); 930 } 931 } 932 } 933 934 return NS_OK; 935 } 936 937 NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable, Runnable, 938 nsIDOMEventListener) 939 940 NS_IMETHODIMP 941 LoadStartDetectionRunnable::Run() { 942 AssertIsOnMainThread(); 943 944 mXHR->RemoveEventListener(Events::loadstart, this, false); 945 946 if (!mReceivedLoadStart) { 947 if (mProxy->mOutstandingSendCount > 1) { 948 mProxy->mOutstandingSendCount--; 949 } else if (mProxy->mOutstandingSendCount == 1) { 950 mProxy->Reset(); 951 952 RefPtr<ProxyCompleteRunnable> runnable = 953 new ProxyCompleteRunnable(mProxy->Private(), mProxy, mChannelId); 954 if (runnable->Dispatch(mProxy->Private())) { 955 mProxy->mWorkerRef = nullptr; 956 mProxy->mSyncLoopTarget = nullptr; 957 mProxy->mOutstandingSendCount--; 958 } 959 } 960 } 961 962 mProxy = nullptr; 963 mXHR = nullptr; 964 return NS_OK; 965 } 966 967 NS_IMETHODIMP 968 LoadStartDetectionRunnable::HandleEvent(Event* aEvent) { 969 AssertIsOnMainThread(); 970 971 #ifdef DEBUG 972 { 973 nsAutoString type; 974 aEvent->GetType(type); 975 MOZ_ASSERT(type == Events::loadstart); 976 } 977 #endif 978 979 mReceivedLoadStart = true; 980 return NS_OK; 981 } 982 983 bool EventRunnable::PreDispatch(WorkerPrivate* /* unused */) { 984 AssertIsOnMainThread(); 985 986 AutoJSAPI jsapi; 987 DebugOnly<bool> ok = jsapi.Init(xpc::NativeGlobal(mScopeObj)); 988 MOZ_ASSERT(ok); 989 JSContext* cx = jsapi.cx(); 990 // Now keep the mScopeObj alive for the duration 991 JS::Rooted<JSObject*> scopeObj(cx, mScopeObj); 992 // And reset mScopeObj now, before we have a chance to run its destructor on 993 // some background thread. 994 mScopeObj.reset(); 995 996 RefPtr<XMLHttpRequestMainThread>& xhr = mProxy->mXHR; 997 MOZ_ASSERT(xhr); 998 999 ErrorResult rv; 1000 1001 XMLHttpRequestResponseType type = xhr->ResponseType(); 1002 1003 // We want to take the result data only if this is available. 1004 if (mType == Events::readystatechange) { 1005 switch (type) { 1006 case XMLHttpRequestResponseType::_empty: 1007 case XMLHttpRequestResponseType::Text: { 1008 xhr->GetResponseText(mResponseData->mResponseText, rv); 1009 mResponseData->mResponseResult = rv.StealNSResult(); 1010 break; 1011 } 1012 1013 case XMLHttpRequestResponseType::Blob: { 1014 mResponseData->mResponseBlobImpl = xhr->GetResponseBlobImpl(); 1015 break; 1016 } 1017 1018 case XMLHttpRequestResponseType::Arraybuffer: { 1019 mResponseData->mResponseArrayBufferBuilder = 1020 xhr->GetResponseArrayBufferBuilder(); 1021 break; 1022 } 1023 1024 case XMLHttpRequestResponseType::Json: { 1025 mResponseData->mResponseResult = 1026 xhr->GetResponseTextForJSON(mResponseData->mResponseJSON); 1027 break; 1028 } 1029 1030 default: 1031 MOZ_ASSERT_UNREACHABLE("Invalid response type"); 1032 return false; 1033 } 1034 } 1035 1036 mStatus = xhr->GetStatus(rv); 1037 mStatusResult = rv.StealNSResult(); 1038 1039 mErrorDetail = xhr->ErrorDetail(); 1040 1041 xhr->GetStatusText(mStatusText, rv); 1042 MOZ_ASSERT(!rv.Failed()); 1043 1044 mReadyState = xhr->ReadyState(); 1045 1046 xhr->GetResponseURL(mResponseURL); 1047 1048 return true; 1049 } 1050 1051 bool EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { 1052 if (!mProxy->mXMLHttpRequestPrivate) { 1053 // Object was finalized, bail. 1054 return true; 1055 } 1056 1057 if (mEventStreamId != mProxy->mXMLHttpRequestPrivate->EventStreamId()) { 1058 // Threads raced, this event is now obsolete. 1059 return true; 1060 } 1061 1062 if (mType == Events::loadend) { 1063 mProxy->mLastErrorDetailAtLoadend = mErrorDetail; 1064 } 1065 1066 bool isLoadStart = mType == Events::loadstart; 1067 if (mUploadEvent) { 1068 if (isLoadStart) { 1069 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 1070 ("Saw upload.loadstart event on main thread")); 1071 mProxy->mSeenUploadLoadStart = true; 1072 } else if (mType == Events::loadend) { 1073 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 1074 ("Saw upload.loadend event on main thread")); 1075 mProxy->mSeenUploadLoadEnd = true; 1076 } 1077 } 1078 1079 if (mProgressEvent) { 1080 // Cache these in case we need them for an error event. 1081 if (mUploadEvent) { 1082 mProxy->mLastUploadLengthComputable = mLengthComputable; 1083 mProxy->mLastUploadLoaded = mLoaded; 1084 mProxy->mLastUploadTotal = mTotal; 1085 } else { 1086 mProxy->mLastLengthComputable = mLengthComputable; 1087 mProxy->mLastLoaded = mLoaded; 1088 mProxy->mLastTotal = mTotal; 1089 } 1090 } 1091 1092 UniquePtr<XMLHttpRequestWorker::StateData> state( 1093 new XMLHttpRequestWorker::StateData()); 1094 1095 state->mStatusResult = mStatusResult; 1096 state->mStatus = mStatus; 1097 1098 state->mStatusText = mStatusText; 1099 1100 state->mReadyState = mReadyState; 1101 1102 state->mResponseURL = mResponseURL; 1103 1104 XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate; 1105 xhr->UpdateState(std::move(state), mType == Events::readystatechange 1106 ? std::move(mResponseData) 1107 : nullptr); 1108 1109 if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) { 1110 return true; 1111 } 1112 1113 XMLHttpRequestEventTarget* target; 1114 if (mUploadEvent) { 1115 target = xhr->GetUploadObjectNoCreate(); 1116 } else { 1117 target = xhr; 1118 } 1119 1120 MOZ_ASSERT(target); 1121 1122 RefPtr<Event> event; 1123 if (mProgressEvent) { 1124 ProgressEventInit init; 1125 init.mBubbles = false; 1126 init.mCancelable = false; 1127 init.mLengthComputable = mLengthComputable; 1128 init.mLoaded = mLoaded; 1129 init.mTotal = mTotal; 1130 1131 event = ProgressEvent::Constructor(target, mType, init); 1132 } else { 1133 event = NS_NewDOMEvent(target, nullptr, nullptr); 1134 1135 if (event) { 1136 event->InitEvent(mType, false, false); 1137 } 1138 } 1139 1140 if (!event) { 1141 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 1142 ("%p unable to fire %s event (%u,%u,%" PRIu64 ",%" PRIu64 ")", 1143 mProxy->mXHR.get(), mType.cStr, mUploadEvent, mLengthComputable, 1144 mLoaded, mTotal)); 1145 return false; 1146 } 1147 1148 event->SetTrusted(true); 1149 1150 MOZ_LOG( 1151 gXMLHttpRequestLog, LogLevel::Debug, 1152 ("%p firing %s event (%u,%u,%" PRIu64 ",%" PRIu64 ")", mProxy->mXHR.get(), 1153 mType.cStr, mUploadEvent, mLengthComputable, mLoaded, mTotal)); 1154 1155 target->DispatchEvent(*event); 1156 1157 return true; 1158 } 1159 1160 bool WorkerThreadProxySyncRunnable::MainThreadRun() { 1161 AssertIsOnMainThread(); 1162 1163 nsCOMPtr<nsIEventTarget> tempTarget = mSyncLoopTarget; 1164 1165 mProxy->mSyncEventResponseTarget.swap(tempTarget); 1166 1167 ErrorResult rv; 1168 RunOnMainThread(rv); 1169 mErrorCode = rv.StealNSResult(); 1170 1171 mProxy->mSyncEventResponseTarget.swap(tempTarget); 1172 1173 return true; 1174 } 1175 1176 void AbortRunnable::RunOnMainThread(ErrorResult& aRv) { 1177 mProxy->mInnerEventStreamId++; 1178 1179 MOZ_ASSERT(mWorkerRef); 1180 1181 MOZ_ASSERT_IF(mProxy->mWorkerRef, 1182 mProxy->mWorkerRef->Private() == mWorkerRef->Private()); 1183 1184 // mProxy wants a valid ThreadSafeWorkerRef for the duration of our call, 1185 // but mProxy->mWorkerRef may be null if a send is not currently active, 1186 // so save the existing value for the duration of the call. 1187 RefPtr<ThreadSafeWorkerRef> oldWorker = std::move(mProxy->mWorkerRef); 1188 1189 // WorkerMainThreadRunnable::mWorkerRef must not be nullptr here, since 1190 // when get here, it means this WorkerMainThreadRunnable had already be 1191 // dispatched successfully and the execution is on the main thread. 1192 MOZ_ASSERT_DEBUG_OR_FUZZING(mWorkerRef); 1193 1194 // Set mProxy->mWorkerRef as AbortRunnable::mWorkerRef which is from 1195 // WorkerMainThreadRunnable during the runnable execution. 1196 // Let AbortRunnable keep a reference for dispatching 1197 // MainThreadStopSyncRunnable back to the Worker thread after the main thread 1198 // execution completes. 1199 mProxy->mWorkerRef = mWorkerRef; 1200 1201 mProxy->mXHR->Abort(aRv); 1202 1203 // Restore the activated WorkerRef to mProxy for the previous Send(). 1204 mProxy->mWorkerRef = std::move(oldWorker); 1205 1206 mProxy->Reset(); 1207 } 1208 1209 void OpenRunnable::MainThreadRunInternal(ErrorResult& aRv) { 1210 MOZ_ASSERT(mWorkerRef); 1211 if (!mProxy->Init(mWorkerRef->Private())) { 1212 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1213 return; 1214 } 1215 1216 if (mBackgroundRequest) { 1217 mProxy->mXHR->SetMozBackgroundRequestExternal(mBackgroundRequest, aRv); 1218 if (aRv.Failed()) { 1219 return; 1220 } 1221 } 1222 1223 if (mOriginStack) { 1224 mProxy->mXHR->SetOriginStack(std::move(mOriginStack)); 1225 } 1226 1227 if (mWithCredentials) { 1228 mProxy->mXHR->SetWithCredentials(mWithCredentials, aRv); 1229 if (NS_WARN_IF(aRv.Failed())) { 1230 return; 1231 } 1232 } 1233 1234 if (mTimeout) { 1235 mProxy->mXHR->SetTimeout(mTimeout, aRv); 1236 if (NS_WARN_IF(aRv.Failed())) { 1237 return; 1238 } 1239 } 1240 1241 if (!mMimeTypeOverride.IsVoid()) { 1242 mProxy->mXHR->OverrideMimeType(mMimeTypeOverride, aRv); 1243 if (NS_WARN_IF(aRv.Failed())) { 1244 return; 1245 } 1246 } 1247 1248 MOZ_ASSERT(!mProxy->mInOpen); 1249 mProxy->mInOpen = true; 1250 1251 mProxy->mXHR->Open( 1252 mMethod, mURL, true, mUser.WasPassed() ? mUser.Value() : VoidCString(), 1253 mPassword.WasPassed() ? mPassword.Value() : VoidCString(), aRv); 1254 1255 MOZ_ASSERT(mProxy->mInOpen); 1256 mProxy->mInOpen = false; 1257 1258 if (NS_WARN_IF(aRv.Failed())) { 1259 return; 1260 } 1261 1262 if (mSource) { 1263 mProxy->mXHR->SetSource(std::move(mSource)); 1264 } 1265 1266 mProxy->mXHR->SetResponseType(mResponseType, aRv); 1267 } 1268 1269 void SendRunnable::RunOnMainThread(ErrorResult& aRv) { 1270 // Before we change any state let's check if we can send. 1271 if (!mProxy->mXHR->CanSend(aRv)) { 1272 return; 1273 } 1274 1275 Nullable< 1276 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString> 1277 payload; 1278 1279 if (!mBlobImpl) { 1280 payload.SetNull(); 1281 } else { 1282 JS::Rooted<JSObject*> globalObject(RootingCx(), 1283 xpc::UnprivilegedJunkScope(fallible)); 1284 if (NS_WARN_IF(!globalObject)) { 1285 aRv.Throw(NS_ERROR_FAILURE); 1286 return; 1287 } 1288 1289 nsCOMPtr<nsIGlobalObject> parent = xpc::NativeGlobal(globalObject); 1290 if (NS_WARN_IF(!parent)) { 1291 aRv.Throw(NS_ERROR_FAILURE); 1292 return; 1293 } 1294 1295 RefPtr<Blob> blob = Blob::Create(parent, mBlobImpl); 1296 MOZ_ASSERT(blob); 1297 1298 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString& 1299 ref = payload.SetValue(); 1300 ref.SetAsBlob() = blob; 1301 } 1302 1303 // Send() has been already called, reset the proxy. 1304 if (mProxy->mWorkerRef) { 1305 mProxy->Reset(); 1306 } 1307 1308 MOZ_ASSERT(mWorkerRef); 1309 mProxy->mWorkerRef = mWorkerRef; 1310 1311 MOZ_ASSERT(!mProxy->mSyncLoopTarget); 1312 mProxy->mSyncLoopTarget.swap(mSyncXHRSyncLoopTarget); 1313 1314 if (mHasUploadListeners) { 1315 // Send() can be called more than once before failure, 1316 // so don't attach the upload listeners more than once. 1317 if (!mProxy->mUploadEventListenersAttached && 1318 !mProxy->AddRemoveEventListeners(true, true)) { 1319 MOZ_ASSERT(false, "This should never fail!"); 1320 } 1321 } 1322 1323 mProxy->mInnerChannelId++; 1324 1325 mProxy->mXHR->Send(payload, aRv); 1326 1327 if (!aRv.Failed()) { 1328 mProxy->mOutstandingSendCount++; 1329 1330 if (!mHasUploadListeners) { 1331 // Send() can be called more than once before failure, 1332 // so don't attach the upload listeners more than once. 1333 if (!mProxy->mUploadEventListenersAttached && 1334 !mProxy->AddRemoveEventListeners(true, true)) { 1335 MOZ_ASSERT(false, "This should never fail!"); 1336 } 1337 } 1338 } else { 1339 // In case of failure we just break the sync loop 1340 mProxy->mSyncLoopTarget = nullptr; 1341 mSyncXHRSyncLoopTarget = nullptr; 1342 } 1343 } 1344 1345 XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate, 1346 nsIGlobalObject* aGlobalObject) 1347 : XMLHttpRequest(aGlobalObject), 1348 mResponseType(XMLHttpRequestResponseType::_empty), 1349 mStateData(new StateData()), 1350 mResponseData(new ResponseData()), 1351 mResponseArrayBufferValue(nullptr), 1352 mResponseJSONValue(JS::UndefinedValue()), 1353 mTimeout(0), 1354 mBackgroundRequest(false), 1355 mWithCredentials(false), 1356 mCanceled(false), 1357 mFlagSendActive(false), 1358 mMozAnon(false), 1359 mMozSystem(false), 1360 mMimeTypeOverride(VoidString()) { 1361 aWorkerPrivate->AssertIsOnWorkerThread(); 1362 1363 mozilla::HoldJSObjects(this); 1364 } 1365 1366 XMLHttpRequestWorker::~XMLHttpRequestWorker() { 1367 ReleaseProxy(XHRIsGoingAway); 1368 1369 MOZ_ASSERT(!mWorkerRef); 1370 1371 mozilla::DropJSObjects(this); 1372 } 1373 1374 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget) 1375 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget) 1376 1377 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestWorker) 1378 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget) 1379 1380 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestWorker) 1381 1382 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestWorker, 1383 XMLHttpRequestEventTarget) 1384 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload) 1385 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob) 1386 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1387 1388 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestWorker, 1389 XMLHttpRequestEventTarget) 1390 tmp->ReleaseProxy(XHRIsGoingAway); 1391 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload) 1392 tmp->mResponseData = nullptr; 1393 tmp->mResponseBlob = nullptr; 1394 tmp->mResponseArrayBufferValue = nullptr; 1395 tmp->mResponseJSONValue.setUndefined(); 1396 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1397 1398 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestWorker, 1399 XMLHttpRequestEventTarget) 1400 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseArrayBufferValue) 1401 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseJSONValue) 1402 NS_IMPL_CYCLE_COLLECTION_TRACE_END 1403 1404 /* static */ 1405 already_AddRefed<XMLHttpRequest> XMLHttpRequestWorker::Construct( 1406 const GlobalObject& aGlobal, const MozXMLHttpRequestParameters& aParams, 1407 ErrorResult& aRv) { 1408 JSContext* cx = aGlobal.Context(); 1409 WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx); 1410 MOZ_ASSERT(workerPrivate); 1411 1412 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 1413 if (NS_WARN_IF(!global)) { 1414 aRv.Throw(NS_ERROR_FAILURE); 1415 return nullptr; 1416 } 1417 1418 RefPtr<XMLHttpRequestWorker> xhr = 1419 new XMLHttpRequestWorker(workerPrivate, global); 1420 1421 if (workerPrivate->XHRParamsAllowed()) { 1422 if (aParams.mMozSystem) { 1423 xhr->mMozAnon = true; 1424 } else { 1425 xhr->mMozAnon = 1426 aParams.mMozAnon.WasPassed() ? aParams.mMozAnon.Value() : false; 1427 } 1428 xhr->mMozSystem = aParams.mMozSystem; 1429 } 1430 1431 return xhr.forget(); 1432 } 1433 1434 void XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType) { 1435 if (mProxy) { 1436 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1437 MOZ_ASSERT(workerPrivate); 1438 if (aType == XHRIsGoingAway) { 1439 // Coming here means the XHR was GC'd, so we can't be pinned. 1440 MOZ_ASSERT(!mProxy->mXMLHttpRequestPrivate || 1441 !mProxy->mXMLHttpRequestPrivate->mPinnedSelfRef); 1442 1443 // We need to clear our weak pointer on the worker thread, let's do it now 1444 // before doing it implicitly in the Proxy dtor on the wrong thread. 1445 mProxy->mXMLHttpRequestPrivate = nullptr; 1446 1447 // We're in a GC finalizer, so we can't do a sync call here (and we don't 1448 // need to). 1449 RefPtr<AsyncTeardownRunnable> runnable = 1450 new AsyncTeardownRunnable(mProxy); 1451 mProxy = nullptr; 1452 1453 if (NS_FAILED(workerPrivate->DispatchToMainThread(runnable.forget()))) { 1454 NS_ERROR("Failed to dispatch teardown runnable!"); 1455 } 1456 } else { 1457 // This isn't necessary if the worker is going away or the XHR is going 1458 // away. 1459 if (aType == Default) { 1460 // Don't let any more events run. 1461 mEventStreamId++; 1462 } 1463 1464 // Ensure we are unpinned before we clear the weak reference. 1465 RefPtr<XMLHttpRequestWorker> self = this; 1466 if (mPinnedSelfRef) { 1467 Unpin(); 1468 } 1469 mProxy->mXMLHttpRequestPrivate = nullptr; 1470 1471 // We need to make a sync call here. 1472 RefPtr<SyncTeardownRunnable> runnable = 1473 new SyncTeardownRunnable(workerPrivate, mProxy); 1474 mProxy = nullptr; 1475 1476 IgnoredErrorResult forAssertionsOnly; 1477 // This runnable _must_ be executed. 1478 // XXX This is a bit weird the failure status is Dead. Dispatching this 1479 // WorkerThreadRunnable in Killing status is not reasonable for Worker. 1480 runnable->Dispatch(workerPrivate, Dead, forAssertionsOnly); 1481 MOZ_DIAGNOSTIC_ASSERT(!forAssertionsOnly.Failed()); 1482 } 1483 } 1484 } 1485 1486 void XMLHttpRequestWorker::MaybePin(ErrorResult& aRv) { 1487 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1488 1489 if (mWorkerRef) { 1490 return; 1491 } 1492 1493 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1494 1495 RefPtr<XMLHttpRequestWorker> self = this; 1496 RefPtr<StrongWorkerRef> workerRef = 1497 StrongWorkerRef::Create(workerPrivate, "XMLHttpRequestWorker", [self]() { 1498 if (!self->mCanceled) { 1499 self->mCanceled = true; 1500 self->ReleaseProxy(WorkerIsGoingAway); 1501 } 1502 }); 1503 if (NS_WARN_IF(!workerRef)) { 1504 aRv.Throw(NS_ERROR_FAILURE); 1505 return; 1506 } 1507 mWorkerRef = MakeRefPtr<ThreadSafeWorkerRef>(workerRef); 1508 1509 mPinnedSelfRef = this; 1510 1511 #ifdef DEBUG 1512 mProxy->DebugStoreWorkerRef(mWorkerRef); 1513 #endif 1514 } 1515 1516 void XMLHttpRequestWorker::SetResponseToNetworkError() { 1517 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("SetResponseToNetworkError")); 1518 mStateData->mStatus = 0; 1519 mStateData->mStatusText.Truncate(); 1520 if (mProxy) { 1521 mProxy->mLastLengthComputable = false; 1522 mProxy->mLastLoaded = 0; 1523 mProxy->mLastTotal = 0; 1524 mProxy->mLastUploadLengthComputable = false; 1525 mProxy->mLastUploadLoaded = 0; 1526 mProxy->mLastUploadTotal = 0; 1527 } 1528 } 1529 1530 void XMLHttpRequestWorker::RequestErrorSteps( 1531 ErrorResult& aRv, const ErrorProgressEventType& aEventType, 1532 nsresult aException) { 1533 // https://xhr.spec.whatwg.org/#request-error-steps 1534 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1535 1536 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 1537 ("RequestErrorSteps(%s)", aEventType.cStr)); 1538 1539 MOZ_ASSERT(mProxy); 1540 1541 // Step 1: Set xhr’s state to done. 1542 mStateData->mReadyState = XMLHttpRequest_Binding::DONE; 1543 1544 // Step 2: Unset xhr’s send() flag. 1545 mFlagSend = false; 1546 1547 // Step 3: Set xhr’s response to a network error. 1548 SetResponseToNetworkError(); 1549 1550 // Step 4: If xhr’s synchronous flag is set, then throw exception. 1551 if (!mProxy || mProxy->mIsSyncXHR) { 1552 aRv.Throw(aException); 1553 return; 1554 } 1555 1556 // Step 5: Fire an event named readystatechange at xhr. 1557 if (!FireEvent(this, Events::readystatechange, false, aRv)) { 1558 return; 1559 } 1560 1561 // Step 6: If xhr’s upload complete flag is unset, then: 1562 if (mUpload && mProxy && mProxy->mSeenUploadLoadStart && 1563 !mProxy->mSeenUploadLoadEnd) { 1564 // Gecko-specific: we can only know whether the proxy XHR's upload 1565 // complete flag is set by waiting for the related upload loadend 1566 // event to happen (at which point upload complete has just been set, 1567 // either in Request Error Steps or processRequestEndOfBody. 1568 1569 // Step 6.1: Set xhr’s upload complete flag. 1570 // We don't need to keep track of this. 1571 1572 // Gecko-specific: we must Fire the loadstart event, 1573 // as we have not done so yet. 1574 if (!FireEvent(mUpload, Events::loadstart, true, aRv)) { 1575 return; 1576 } 1577 1578 // Step 6.2: If xhr’s upload listener flag is set, then: 1579 // We know there must be listeners since we saw an upload loadstart. 1580 1581 // Step 6.2.1: Fire a progress event named event at xhr’s upload object with 1582 // 0 and 0. 1583 if (!FireEvent(mUpload, aEventType, true, aRv)) { 1584 return; 1585 } 1586 1587 // Step 6.2.2: Fire a progress event named loadend at xhr’s upload object 1588 // with 0 and 0. 1589 if (!FireEvent(mUpload, Events::loadend, true, aRv)) { 1590 return; 1591 } 1592 } 1593 1594 // Step 7: Fire a progress event named event at xhr with 0 and 0. 1595 if (!FireEvent(this, aEventType, true, aRv)) { 1596 return; 1597 } 1598 1599 // Step 8: Fire a progress event named loadend at xhr with 0 and 0. 1600 FireEvent(this, Events::loadend, true, aRv); 1601 } 1602 1603 // A false return value here indicates that we should consider the XHR 1604 // to have been re-opened, or something catastrophic to have happened, 1605 // where we should stop running any code we normally would after firing 1606 // the event (such as firing more events). This includes if an exception 1607 // is thrown in aRv. 1608 bool XMLHttpRequestWorker::FireEvent(EventTarget* aTarget, 1609 const EventType& aEventType, 1610 bool aUploadTarget, ErrorResult& aRv) { 1611 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1612 MOZ_ASSERT(aTarget); 1613 1614 if (!mProxy) { 1615 aRv.Throw(NS_ERROR_FAILURE); 1616 return false; 1617 } 1618 1619 uint32_t currentEventStreamId = mEventStreamId; 1620 RefPtr<Event> event; 1621 if (aEventType == Events::readystatechange) { 1622 event = NS_NewDOMEvent(aTarget, nullptr, nullptr); 1623 event->InitEvent(aEventType, false, false); 1624 } else { 1625 if (mProxy->mIsSyncXHR && aEventType == Events::progress) { 1626 return true; 1627 } 1628 1629 ProgressEventInit init; 1630 init.mBubbles = false; 1631 init.mCancelable = false; 1632 if (aUploadTarget) { 1633 init.mLengthComputable = mProxy->mLastUploadLengthComputable; 1634 init.mLoaded = mProxy->mLastUploadLoaded; 1635 init.mTotal = mProxy->mLastUploadTotal; 1636 } else { 1637 init.mLengthComputable = mProxy->mLastLengthComputable; 1638 init.mLoaded = mProxy->mLastLoaded; 1639 init.mTotal = mProxy->mLastTotal; 1640 } 1641 event = ProgressEvent::Constructor(aTarget, aEventType, init); 1642 } 1643 1644 if (!event) { 1645 aRv.Throw(NS_ERROR_FAILURE); 1646 return false; 1647 } 1648 1649 event->SetTrusted(true); 1650 1651 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 1652 ("%p firing %s pre-abort event (%u,%u,%" PRIu64 ",%" PRIu64, this, 1653 aEventType.cStr, aUploadTarget, 1654 aUploadTarget ? mProxy->mLastUploadLengthComputable 1655 : mProxy->mLastLengthComputable, 1656 aUploadTarget ? mProxy->mLastUploadLoaded : mProxy->mLastLoaded, 1657 aUploadTarget ? mProxy->mLastUploadTotal : mProxy->mLastTotal)); 1658 aTarget->DispatchEvent(*event); 1659 1660 // if dispatching the event caused code to run which re-opened us, and 1661 // therefore changed our event stream, return false. 1662 return currentEventStreamId == mEventStreamId; 1663 } 1664 1665 void XMLHttpRequestWorker::Unpin() { 1666 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1667 1668 MOZ_ASSERT(mWorkerRef, "Mismatched calls to Unpin!"); 1669 1670 #ifdef DEBUG 1671 if (mProxy) { 1672 // The proxy will be gone if WorkerIsGoingAway 1673 mProxy->DebugForgetWorkerRef(); 1674 } 1675 #endif 1676 1677 mWorkerRef = nullptr; 1678 1679 mPinnedSelfRef = nullptr; 1680 } 1681 1682 uint16_t XMLHttpRequestWorker::ReadyState() const { 1683 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 1684 ("GetReadyState(%u)", mStateData->mReadyState)); 1685 return mStateData->mReadyState; 1686 } 1687 1688 void XMLHttpRequestWorker::SendInternal(const BodyExtractorBase* aBody, 1689 ErrorResult& aRv) { 1690 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1691 1692 // We don't really need to keep the same body-type when we proxy the send() 1693 // call to the main-thread XHR. Let's extract the nsIInputStream from the 1694 // aBody and let's wrap it into a StreamBlobImpl. 1695 1696 RefPtr<BlobImpl> blobImpl; 1697 1698 if (aBody) { 1699 nsAutoCString charset; 1700 nsAutoCString defaultContentType; 1701 nsCOMPtr<nsIInputStream> uploadStream; 1702 1703 uint64_t size_u64; 1704 aRv = aBody->GetAsStream(getter_AddRefs(uploadStream), &size_u64, 1705 defaultContentType, charset); 1706 if (NS_WARN_IF(aRv.Failed())) { 1707 return; 1708 } 1709 1710 blobImpl = StreamBlobImpl::Create(uploadStream.forget(), 1711 NS_ConvertUTF8toUTF16(defaultContentType), 1712 size_u64, u"StreamBlobImpl"_ns); 1713 MOZ_ASSERT(blobImpl); 1714 } 1715 1716 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1717 1718 RefPtr<SendRunnable> sendRunnable = 1719 new SendRunnable(workerPrivate, mProxy, blobImpl); 1720 1721 // No send() calls when open is running. 1722 if (mProxy->mOpenCount) { 1723 aRv.Throw(NS_ERROR_FAILURE); 1724 return; 1725 } 1726 1727 bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false; 1728 1729 MaybePin(aRv); 1730 if (aRv.Failed()) { 1731 return; 1732 } 1733 1734 RefPtr<XMLHttpRequestWorker> selfRef = this; 1735 AutoUnpinXHR autoUnpin(this); 1736 Maybe<AutoSyncLoopHolder> syncXHRSyncLoop; 1737 1738 nsCOMPtr<nsISerialEventTarget> syncXHRSyncLoopTarget; 1739 bool isSyncXHR = mProxy->mIsSyncXHR; 1740 if (isSyncXHR) { 1741 syncXHRSyncLoop.emplace(workerPrivate, Canceling); 1742 syncXHRSyncLoopTarget = syncXHRSyncLoop->GetSerialEventTarget(); 1743 if (!syncXHRSyncLoopTarget) { 1744 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1745 return; 1746 } 1747 } 1748 1749 mProxy->mOuterChannelId++; 1750 1751 sendRunnable->SetSyncXHRSyncLoopTarget(syncXHRSyncLoopTarget); 1752 sendRunnable->SetHaveUploadListeners(hasUploadListeners); 1753 1754 mFlagSend = true; 1755 1756 sendRunnable->Dispatch(workerPrivate, Canceling, aRv); 1757 if (aRv.Failed()) { 1758 // Dispatch() may have spun the event loop and we may have already unrooted. 1759 // If so we don't want autoUnpin to try again. 1760 if (!mWorkerRef) { 1761 autoUnpin.Clear(); 1762 } 1763 return; 1764 } 1765 1766 if (!isSyncXHR) { 1767 autoUnpin.Clear(); 1768 MOZ_ASSERT(!syncXHRSyncLoop); 1769 return; 1770 } 1771 1772 autoUnpin.Clear(); 1773 1774 bool succeeded = NS_SUCCEEDED(syncXHRSyncLoop->Run()); 1775 1776 // Throw appropriately If a sync XHR failed per spec's RequestErrorSteps 1777 if (isSyncXHR && mProxy) { 1778 nsresult error = mProxy->mLastErrorDetailAtLoadend; 1779 if (error == NS_ERROR_DOM_ABORT_ERR) { 1780 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Info, 1781 ("%p throwing NS_ERROR_DOM_ABORT_ERR", this)); 1782 aRv.Throw(error); 1783 return; 1784 } 1785 if (error == NS_ERROR_DOM_TIMEOUT_ERR) { 1786 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Info, 1787 ("%p throwing NS_ERROR_DOM_TIMEOUT_ERR", this)); 1788 aRv.Throw(error); 1789 return; 1790 } 1791 if (error == NS_ERROR_DOM_NETWORK_ERR || 1792 NS_ERROR_GET_MODULE(error) == NS_ERROR_MODULE_NETWORK) { 1793 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Info, 1794 ("%p throwing NS_ERROR_DOM_NETWORK_ERR (0x%" PRIx32 ")", this, 1795 static_cast<uint32_t>(error))); 1796 aRv.Throw(NS_ERROR_DOM_NETWORK_ERR); 1797 return; 1798 } 1799 } 1800 1801 // Don't clobber an existing exception that we may have thrown on aRv 1802 // already... though can there really be one? In any case, it seems to me 1803 // that this autoSyncLoop->Run() can never fail, since the StopSyncLoop call 1804 // for it will come from ProxyCompleteRunnable and that always passes true for 1805 // the second arg. 1806 if (!succeeded && !aRv.Failed()) { 1807 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 1808 ("%p SendInternal failed; throwing NS_ERROR_FAILURE", this)); 1809 aRv.Throw(NS_ERROR_FAILURE); 1810 } 1811 } 1812 1813 void XMLHttpRequestWorker::Open(const nsACString& aMethod, 1814 const nsACString& aUrl, bool aAsync, 1815 const Optional<nsACString>& aUser, 1816 const Optional<nsACString>& aPassword, 1817 ErrorResult& aRv) { 1818 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1819 1820 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 1821 ("%p Open(%s,%s,%d)", this, PromiseFlatCString(aMethod).get(), 1822 PromiseFlatCString(aUrl).get(), aAsync)); 1823 1824 if (mCanceled) { 1825 aRv.ThrowUncatchableException(); 1826 return; 1827 } 1828 1829 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1830 1831 mFlagSend = false; 1832 1833 bool alsoOverrideMimeType = false; 1834 if (!mProxy) { 1835 Maybe<ClientInfo> clientInfo(workerPrivate->GlobalScope()->GetClientInfo()); 1836 if (clientInfo.isNothing()) { 1837 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1838 return; 1839 } 1840 mProxy = new Proxy(this, clientInfo.ref(), 1841 workerPrivate->GlobalScope()->GetController(), mMozAnon, 1842 mMozSystem); 1843 alsoOverrideMimeType = true; 1844 } 1845 1846 mProxy->mSeenUploadLoadStart = false; 1847 mProxy->mSeenUploadLoadEnd = false; 1848 SetResponseToNetworkError(); 1849 1850 mEventStreamId++; 1851 1852 UniquePtr<SerializedStackHolder> stack; 1853 if (workerPrivate->IsWatchedByDevTools()) { 1854 if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) { 1855 stack = GetCurrentStackForNetMonitor(cx); 1856 } 1857 } 1858 1859 RefPtr<OpenRunnable> runnable = new OpenRunnable( 1860 workerPrivate, mProxy, aMethod, aUrl, aUser, aPassword, 1861 mBackgroundRequest, mWithCredentials, mTimeout, mResponseType, 1862 alsoOverrideMimeType ? mMimeTypeOverride : VoidString(), std::move(stack), 1863 profiler_capture_backtrace()); 1864 1865 ++mProxy->mOpenCount; 1866 runnable->Dispatch(workerPrivate, Canceling, aRv); 1867 if (aRv.Failed()) { 1868 if (mProxy && !--mProxy->mOpenCount) { 1869 ReleaseProxy(); 1870 } 1871 1872 return; 1873 } 1874 1875 // We have been released in one of the nested Open() calls. 1876 if (!mProxy) { 1877 aRv.Throw(NS_ERROR_FAILURE); 1878 return; 1879 } 1880 1881 --mProxy->mOpenCount; 1882 mProxy->mIsSyncXHR = !aAsync; 1883 } 1884 1885 void XMLHttpRequestWorker::SetRequestHeader(const nsACString& aHeader, 1886 const nsACString& aValue, 1887 ErrorResult& aRv) { 1888 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1889 1890 if (mCanceled) { 1891 aRv.ThrowUncatchableException(); 1892 return; 1893 } 1894 1895 if (!mProxy) { 1896 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1897 return; 1898 } 1899 1900 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1901 1902 RefPtr<SetRequestHeaderRunnable> runnable = 1903 new SetRequestHeaderRunnable(workerPrivate, mProxy, aHeader, aValue); 1904 runnable->Dispatch(workerPrivate, Canceling, aRv); 1905 } 1906 1907 void XMLHttpRequestWorker::SetTimeout(uint32_t aTimeout, ErrorResult& aRv) { 1908 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1909 1910 if (mCanceled) { 1911 aRv.ThrowUncatchableException(); 1912 return; 1913 } 1914 1915 mTimeout = aTimeout; 1916 1917 if (!mProxy) { 1918 // Open might not have been called yet, in which case we'll handle the 1919 // timeout in OpenRunnable. 1920 return; 1921 } 1922 1923 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1924 1925 RefPtr<SetTimeoutRunnable> runnable = 1926 new SetTimeoutRunnable(workerPrivate, mProxy, aTimeout); 1927 runnable->Dispatch(workerPrivate, Canceling, aRv); 1928 } 1929 1930 void XMLHttpRequestWorker::SetWithCredentials(bool aWithCredentials, 1931 ErrorResult& aRv) { 1932 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1933 1934 if (mCanceled) { 1935 aRv.ThrowUncatchableException(); 1936 return; 1937 } 1938 1939 mWithCredentials = aWithCredentials; 1940 1941 if (!mProxy) { 1942 // Open might not have been called yet, in which case we'll handle the 1943 // credentials in OpenRunnable. 1944 return; 1945 } 1946 1947 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1948 1949 RefPtr<SetWithCredentialsRunnable> runnable = 1950 new SetWithCredentialsRunnable(workerPrivate, mProxy, aWithCredentials); 1951 runnable->Dispatch(workerPrivate, Canceling, aRv); 1952 } 1953 1954 void XMLHttpRequestWorker::SetMozBackgroundRequest(bool aBackgroundRequest, 1955 ErrorResult& aRv) { 1956 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1957 1958 if (mCanceled) { 1959 aRv.ThrowUncatchableException(); 1960 return; 1961 } 1962 1963 mBackgroundRequest = aBackgroundRequest; 1964 1965 if (!mProxy) { 1966 // Open might not have been called yet, in which case we'll handle the 1967 // background request in OpenRunnable. 1968 return; 1969 } 1970 1971 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1972 1973 RefPtr<SetBackgroundRequestRunnable> runnable = 1974 new SetBackgroundRequestRunnable(workerPrivate, mProxy, 1975 aBackgroundRequest); 1976 runnable->Dispatch(workerPrivate, Canceling, aRv); 1977 } 1978 1979 XMLHttpRequestUpload* XMLHttpRequestWorker::GetUpload(ErrorResult& aRv) { 1980 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 1981 1982 if (mCanceled) { 1983 aRv.ThrowUncatchableException(); 1984 return nullptr; 1985 } 1986 1987 if (!mUpload) { 1988 mUpload = new XMLHttpRequestUpload(this); 1989 } 1990 1991 return mUpload; 1992 } 1993 1994 void XMLHttpRequestWorker::Send( 1995 const Nullable< 1996 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>& 1997 aData, 1998 ErrorResult& aRv) { 1999 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 2000 2001 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("Send()")); 2002 2003 if (mFlagSendActive) { 2004 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT); 2005 return; 2006 } 2007 mFlagSendActive = true; 2008 auto clearRecursionFlag = MakeScopeExit([&]() { 2009 // No one else should have touched this flag. 2010 MOZ_ASSERT(mFlagSendActive); 2011 mFlagSendActive = false; 2012 }); 2013 2014 if (mCanceled) { 2015 aRv.ThrowUncatchableException(); 2016 return; 2017 } 2018 2019 if (mStateData->mReadyState != XMLHttpRequest_Binding::OPENED) { 2020 aRv.ThrowInvalidStateError("XMLHttpRequest state must be OPENED."); 2021 return; 2022 } 2023 2024 if (!mProxy || !mProxy->mXMLHttpRequestPrivate || mFlagSend) { 2025 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 2026 return; 2027 } 2028 2029 if (aData.IsNull()) { 2030 SendInternal(nullptr, aRv); 2031 return; 2032 } 2033 2034 if (aData.Value().IsDocument()) { 2035 MOZ_ASSERT_UNREACHABLE("Documents are not exposed to workers."); 2036 aRv.Throw(NS_ERROR_FAILURE); 2037 return; 2038 } 2039 2040 if (aData.Value().IsBlob()) { 2041 BodyExtractor<const Blob> body(&aData.Value().GetAsBlob()); 2042 SendInternal(&body, aRv); 2043 return; 2044 } 2045 2046 if (aData.Value().IsArrayBuffer()) { 2047 BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer()); 2048 SendInternal(&body, aRv); 2049 return; 2050 } 2051 2052 if (aData.Value().IsArrayBufferView()) { 2053 BodyExtractor<const ArrayBufferView> body( 2054 &aData.Value().GetAsArrayBufferView()); 2055 SendInternal(&body, aRv); 2056 return; 2057 } 2058 2059 if (aData.Value().IsFormData()) { 2060 BodyExtractor<const FormData> body(&aData.Value().GetAsFormData()); 2061 SendInternal(&body, aRv); 2062 return; 2063 } 2064 2065 if (aData.Value().IsURLSearchParams()) { 2066 BodyExtractor<const URLSearchParams> body( 2067 &aData.Value().GetAsURLSearchParams()); 2068 SendInternal(&body, aRv); 2069 return; 2070 } 2071 2072 if (aData.Value().IsUSVString()) { 2073 BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString()); 2074 SendInternal(&body, aRv); 2075 return; 2076 } 2077 } 2078 2079 void XMLHttpRequestWorker::Abort(ErrorResult& aRv) { 2080 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 2081 2082 if (mCanceled) { 2083 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("Abort(canceled)")); 2084 aRv.ThrowUncatchableException(); 2085 return; 2086 } 2087 2088 if (!mProxy) { 2089 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("Abort(no proxy)")); 2090 return; 2091 } 2092 2093 // Spec step 1 2094 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("Abort(step 1))")); 2095 mEventStreamId++; 2096 2097 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 2098 RefPtr<AbortRunnable> runnable = new AbortRunnable(workerPrivate, mProxy); 2099 runnable->Dispatch(workerPrivate, Canceling, aRv); 2100 2101 // Spec step 2 2102 if ((mStateData->mReadyState == XMLHttpRequest_Binding::OPENED && 2103 mFlagSend) || 2104 mStateData->mReadyState == XMLHttpRequest_Binding::HEADERS_RECEIVED || 2105 mStateData->mReadyState == XMLHttpRequest_Binding::LOADING) { 2106 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("Abort(step 2)")); 2107 RequestErrorSteps(aRv, Events::abort); 2108 if (aRv.Failed()) { 2109 return; 2110 } 2111 } 2112 2113 // Spec step 3 2114 if (mStateData->mReadyState == XMLHttpRequest_Binding::DONE) { 2115 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("Abort(step 3)")); 2116 mStateData->mReadyState = XMLHttpRequest_Binding::UNSENT; 2117 } 2118 } 2119 2120 void XMLHttpRequestWorker::GetResponseHeader(const nsACString& aHeader, 2121 nsACString& aResponseHeader, 2122 ErrorResult& aRv) { 2123 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 2124 2125 if (mCanceled) { 2126 aRv.ThrowUncatchableException(); 2127 return; 2128 } 2129 2130 if (!mProxy) { 2131 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 2132 return; 2133 } 2134 2135 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 2136 2137 nsCString responseHeader; 2138 RefPtr<GetResponseHeaderRunnable> runnable = new GetResponseHeaderRunnable( 2139 workerPrivate, mProxy, aHeader, responseHeader); 2140 runnable->Dispatch(workerPrivate, Canceling, aRv); 2141 if (aRv.Failed()) { 2142 return; 2143 } 2144 aResponseHeader = responseHeader; 2145 } 2146 2147 void XMLHttpRequestWorker::GetAllResponseHeaders(nsACString& aResponseHeaders, 2148 ErrorResult& aRv) { 2149 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 2150 2151 if (mCanceled) { 2152 aRv.ThrowUncatchableException(); 2153 return; 2154 } 2155 2156 if (!mProxy) { 2157 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 2158 return; 2159 } 2160 2161 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 2162 2163 nsCString responseHeaders; 2164 RefPtr<GetAllResponseHeadersRunnable> runnable = 2165 new GetAllResponseHeadersRunnable(workerPrivate, mProxy, responseHeaders); 2166 runnable->Dispatch(workerPrivate, Canceling, aRv); 2167 if (aRv.Failed()) { 2168 return; 2169 } 2170 2171 aResponseHeaders = responseHeaders; 2172 } 2173 2174 void XMLHttpRequestWorker::OverrideMimeType(const nsAString& aMimeType, 2175 ErrorResult& aRv) { 2176 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 2177 2178 if (mCanceled) { 2179 aRv.ThrowUncatchableException(); 2180 return; 2181 } 2182 2183 // We're supposed to throw if the state is LOADING or DONE. 2184 if (mStateData->mReadyState == XMLHttpRequest_Binding::LOADING || 2185 mStateData->mReadyState == XMLHttpRequest_Binding::DONE) { 2186 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 2187 return; 2188 } 2189 2190 mMimeTypeOverride = aMimeType; 2191 2192 if (mProxy) { 2193 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 2194 RefPtr<OverrideMimeTypeRunnable> runnable = 2195 new OverrideMimeTypeRunnable(workerPrivate, mProxy, aMimeType); 2196 runnable->Dispatch(workerPrivate, Canceling, aRv); 2197 } 2198 } 2199 2200 void XMLHttpRequestWorker::SetResponseType( 2201 XMLHttpRequestResponseType aResponseType, ErrorResult& aRv) { 2202 MOZ_ASSERT_DEBUG_OR_FUZZING(IsCurrentThreadRunningWorker()); 2203 2204 // "document" is fine for the main thread but not for a worker. Short-circuit 2205 // that here. 2206 if (aResponseType == XMLHttpRequestResponseType::Document) { 2207 return; 2208 } 2209 2210 if (!mProxy) { 2211 // Open() has not been called yet. We store the responseType and we will use 2212 // it later in Open(). 2213 mResponseType = aResponseType; 2214 return; 2215 } 2216 2217 if (mStateData->mReadyState == XMLHttpRequest_Binding::LOADING || 2218 mStateData->mReadyState == XMLHttpRequest_Binding::DONE) { 2219 aRv.ThrowInvalidStateError( 2220 "Cannot set 'responseType' property on XMLHttpRequest after 'send()' " 2221 "(when its state is LOADING or DONE)."); 2222 return; 2223 } 2224 2225 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 2226 RefPtr<SetResponseTypeRunnable> runnable = 2227 new SetResponseTypeRunnable(workerPrivate, mProxy, aResponseType); 2228 runnable->Dispatch(workerPrivate, Canceling, aRv); 2229 if (aRv.Failed()) { 2230 return; 2231 } 2232 2233 mResponseType = runnable->ResponseType(); 2234 } 2235 2236 void XMLHttpRequestWorker::GetResponse(JSContext* aCx, 2237 JS::MutableHandle<JS::Value> aResponse, 2238 ErrorResult& aRv) { 2239 if (NS_FAILED(mResponseData->mResponseResult)) { 2240 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("GetResponse(none)")); 2241 aRv.Throw(mResponseData->mResponseResult); 2242 return; 2243 } 2244 2245 switch (mResponseType) { 2246 case XMLHttpRequestResponseType::_empty: 2247 case XMLHttpRequestResponseType::Text: { 2248 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("GetResponse(text)")); 2249 2250 JSString* str; 2251 2252 if (mResponseData->mResponseText.IsEmpty()) { 2253 aResponse.set(JS_GetEmptyStringValue(aCx)); 2254 return; 2255 } 2256 2257 str = mResponseData->mResponseText.GetAsJSStringCopy(aCx); 2258 if (!str) { 2259 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 2260 return; 2261 } 2262 2263 aResponse.setString(str); 2264 return; 2265 } 2266 2267 case XMLHttpRequestResponseType::Arraybuffer: { 2268 if (!mResponseData->mResponseArrayBufferBuilder) { 2269 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2270 ("GetResponse(arraybuffer, null)")); 2271 aResponse.setNull(); 2272 return; 2273 } 2274 2275 if (!mResponseArrayBufferValue) { 2276 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2277 ("GetResponse(arraybuffer)")); 2278 mResponseArrayBufferValue = 2279 mResponseData->mResponseArrayBufferBuilder->TakeArrayBuffer(aCx); 2280 if (!mResponseArrayBufferValue) { 2281 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 2282 return; 2283 } 2284 } 2285 2286 aResponse.setObject(*mResponseArrayBufferValue); 2287 return; 2288 } 2289 2290 case XMLHttpRequestResponseType::Blob: { 2291 if (!mResponseData->mResponseBlobImpl) { 2292 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2293 ("GetResponse(blob, none)")); 2294 aResponse.setNull(); 2295 return; 2296 } 2297 2298 if (!mResponseBlob) { 2299 mResponseBlob = 2300 Blob::Create(GetOwnerGlobal(), mResponseData->mResponseBlobImpl); 2301 } 2302 2303 if (!mResponseBlob || 2304 !GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse)) { 2305 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2306 ("GetResponse(blob, null)")); 2307 aResponse.setNull(); 2308 } else { 2309 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("GetResponse(blob)")); 2310 } 2311 2312 return; 2313 } 2314 2315 case XMLHttpRequestResponseType::Json: { 2316 if (mResponseData->mResponseJSON.IsVoid()) { 2317 aResponse.setNull(); 2318 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2319 ("GetResponse(json, none)")); 2320 return; 2321 } 2322 2323 if (mResponseJSONValue.isUndefined()) { 2324 // The Unicode converter has already zapped the BOM if there was one 2325 JS::Rooted<JS::Value> value(aCx); 2326 if (!JS_ParseJSON(aCx, mResponseData->mResponseJSON.BeginReading(), 2327 mResponseData->mResponseJSON.Length(), &value)) { 2328 JS_ClearPendingException(aCx); 2329 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2330 ("GetResponse(json, null)")); 2331 mResponseJSONValue.setNull(); 2332 } else { 2333 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, ("GetResponse(json)")); 2334 mResponseJSONValue = value; 2335 } 2336 2337 mResponseData->mResponseJSON.Truncate(); 2338 } 2339 2340 aResponse.set(mResponseJSONValue); 2341 return; 2342 } 2343 2344 default: 2345 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2346 ("GetResponse(invalid type)")); 2347 MOZ_ASSERT_UNREACHABLE("Invalid type"); 2348 aResponse.setNull(); 2349 return; 2350 } 2351 } 2352 2353 void XMLHttpRequestWorker::GetResponseText(DOMString& aResponseText, 2354 ErrorResult& aRv) { 2355 MOZ_DIAGNOSTIC_ASSERT(mResponseData); 2356 2357 if (mResponseType != XMLHttpRequestResponseType::_empty && 2358 mResponseType != XMLHttpRequestResponseType::Text) { 2359 aRv.ThrowInvalidStateError( 2360 "responseText is only available if responseType is '' or 'text'."); 2361 return; 2362 } 2363 2364 if (!mResponseData->mResponseText.GetAsString(aResponseText)) { 2365 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 2366 return; 2367 } 2368 } 2369 2370 void XMLHttpRequestWorker::UpdateState( 2371 UniquePtr<StateData>&& aStateData, 2372 UniquePtr<ResponseData>&& aResponseData) { 2373 mStateData = std::move(aStateData); 2374 2375 UniquePtr<ResponseData> responseData = std::move(aResponseData); 2376 if (responseData) { 2377 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2378 ("UpdateState(readyState=%u, new response data)", 2379 mStateData->mReadyState)); 2380 ResetResponseData(); 2381 mResponseData = std::move(responseData); 2382 } else { 2383 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug, 2384 ("UpdateState(readyState=%u)", mStateData->mReadyState)); 2385 } 2386 2387 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this); 2388 } 2389 2390 void XMLHttpRequestWorker::ResetResponseData() { 2391 mResponseBlob = nullptr; 2392 mResponseArrayBufferValue = nullptr; 2393 mResponseJSONValue.setUndefined(); 2394 } 2395 2396 } // namespace mozilla::dom