no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / xhr / XMLHttpRequestWorker.cpp
blob09e0bc8ed6ece139f60665f35d46837eb1d007b6
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/. */
7 #include "XMLHttpRequestWorker.h"
9 #include "nsIDOMEventListener.h"
11 #include "GeckoProfiler.h"
12 #include "jsfriendapi.h"
13 #include "js/ArrayBuffer.h" // JS::Is{,Detached}ArrayBufferObject
14 #include "js/GCPolicyAPI.h"
15 #include "js/JSON.h"
16 #include "js/RootingAPI.h" // JS::{Handle,Heap,PersistentRooted}
17 #include "js/TracingAPI.h"
18 #include "js/Value.h" // JS::{Undefined,}Value
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/HoldDropJSObjects.h"
21 #include "mozilla/dom/Exceptions.h"
22 #include "mozilla/dom/Event.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/WorkerScope.h"
31 #include "mozilla/dom/WorkerRef.h"
32 #include "mozilla/dom/WorkerRunnable.h"
33 #include "mozilla/dom/XMLHttpRequestBinding.h"
34 #include "mozilla/Telemetry.h"
35 #include "nsComponentManagerUtils.h"
36 #include "nsContentUtils.h"
37 #include "nsJSUtils.h"
38 #include "nsThreadUtils.h"
40 #include "XMLHttpRequestMainThread.h"
41 #include "XMLHttpRequestUpload.h"
43 #include "mozilla/UniquePtr.h"
45 extern mozilla::LazyLogModule gXMLHttpRequestLog;
47 namespace mozilla::dom {
49 using EventType = XMLHttpRequest::EventType;
50 using Events = XMLHttpRequest::Events;
52 /**
53 * XMLHttpRequest in workers
55 * XHR in workers is implemented by proxying calls/events/etc between the
56 * worker thread and an XMLHttpRequest on the main thread. The glue
57 * object here is the Proxy, which lives on both threads. All other objects
58 * live on either the main thread (the XMLHttpRequest) or the worker thread
59 * (the worker and XHR private objects).
61 * The main thread XHR is always operated in async mode, even for sync XHR
62 * in workers. Calls made on the worker thread are proxied to the main thread
63 * synchronously (meaning the worker thread is blocked until the call
64 * returns). Each proxied call spins up a sync queue, which captures any
65 * synchronously dispatched events and ensures that they run synchronously
66 * on the worker as well. Asynchronously dispatched events are posted to the
67 * worker thread to run asynchronously. Some of the XHR state is mirrored on
68 * the worker thread to avoid needing a cross-thread call on every property
69 * access.
71 * The XHR private is stored in the private slot of the XHR JSObject on the
72 * worker thread. It is destroyed when that JSObject is GCd. The private
73 * roots its JSObject while network activity is in progress. It also
74 * adds itself as a feature to the worker to give itself a chance to clean up
75 * if the worker goes away during an XHR call. It is important that the
76 * rooting and feature registration (collectively called pinning) happens at
77 * the proper times. If we pin for too long we can cause memory leaks or even
78 * shutdown hangs. If we don't pin for long enough we introduce a GC hazard.
80 * The XHR is pinned from the time Send is called to roughly the time loadend
81 * is received. There are some complications involved with Abort and XHR
82 * reuse. We maintain a counter on the main thread of how many times Send was
83 * called on this XHR, and we decrement the counter every time we receive a
84 * loadend event. When the counter reaches zero we dispatch a runnable to the
85 * worker thread to unpin the XHR. We only decrement the counter if the
86 * dispatch was successful, because the worker may no longer be accepting
87 * regular runnables. In the event that we reach Proxy::Teardown and there
88 * the outstanding Send count is still non-zero, we dispatch a control
89 * runnable which is guaranteed to run.
91 * NB: Some of this could probably be simplified now that we have the
92 * inner/outer channel ids.
95 class Proxy final : public nsIDOMEventListener {
96 public:
97 // Read on multiple threads.
98 WorkerPrivate* mWorkerPrivate;
99 const ClientInfo mClientInfo;
100 const Maybe<ServiceWorkerDescriptor> mController;
102 // Only ever dereferenced and/or checked on the worker thread. Cleared
103 // explicitly on the worker thread inside XMLHttpRequestWorker::ReleaseProxy.
104 WeakPtr<XMLHttpRequestWorker> mXMLHttpRequestPrivate;
106 // XHR Params:
107 bool mMozAnon;
108 bool mMozSystem;
110 // Only touched on the main thread.
111 RefPtr<XMLHttpRequestMainThread> mXHR;
112 RefPtr<XMLHttpRequestUpload> mXHRUpload;
113 nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
114 nsCOMPtr<nsIEventTarget> mSyncEventResponseTarget;
115 uint32_t mInnerEventStreamId;
116 uint32_t mInnerChannelId;
117 uint32_t mOutstandingSendCount;
119 // Only touched on the worker thread.
120 uint32_t mOuterEventStreamId;
121 uint32_t mOuterChannelId;
122 uint32_t mOpenCount;
123 uint64_t mLastLoaded;
124 uint64_t mLastTotal;
125 uint64_t mLastUploadLoaded;
126 uint64_t mLastUploadTotal;
127 nsresult mLastErrorDetailAtLoadend;
128 bool mIsSyncXHR;
129 bool mLastLengthComputable;
130 bool mLastUploadLengthComputable;
131 bool mSeenLoadStart;
132 bool mSeenUploadLoadStart;
133 bool mDispatchPrematureAbortEvent;
134 bool mDispatchPrematureAbortEventToUpload;
136 // Only touched on the main thread.
137 bool mUploadEventListenersAttached;
138 bool mMainThreadSeenLoadStart;
139 bool mInOpen;
141 public:
142 Proxy(XMLHttpRequestWorker* aXHRPrivate, const ClientInfo& aClientInfo,
143 const Maybe<ServiceWorkerDescriptor>& aController, bool aMozAnon,
144 bool aMozSystem)
145 : mWorkerPrivate(nullptr),
146 mClientInfo(aClientInfo),
147 mController(aController),
148 mXMLHttpRequestPrivate(aXHRPrivate),
149 mMozAnon(aMozAnon),
150 mMozSystem(aMozSystem),
151 mInnerEventStreamId(0),
152 mInnerChannelId(0),
153 mOutstandingSendCount(0),
154 mOuterEventStreamId(0),
155 mOuterChannelId(0),
156 mOpenCount(0),
157 mLastLoaded(0),
158 mLastTotal(0),
159 mLastUploadLoaded(0),
160 mLastUploadTotal(0),
161 mLastErrorDetailAtLoadend(NS_OK),
162 mIsSyncXHR(false),
163 mLastLengthComputable(false),
164 mLastUploadLengthComputable(false),
165 mSeenLoadStart(false),
166 mSeenUploadLoadStart(false),
167 mDispatchPrematureAbortEvent(false),
168 mDispatchPrematureAbortEventToUpload(false),
169 mUploadEventListenersAttached(false),
170 mMainThreadSeenLoadStart(false),
171 mInOpen(false) {}
173 NS_DECL_THREADSAFE_ISUPPORTS
174 NS_DECL_NSIDOMEVENTLISTENER
176 bool Init();
178 void Teardown();
180 bool AddRemoveEventListeners(bool aUpload, bool aAdd);
182 void Reset() {
183 AssertIsOnMainThread();
185 if (mUploadEventListenersAttached) {
186 AddRemoveEventListeners(true, false);
190 already_AddRefed<nsIEventTarget> GetEventTarget() {
191 AssertIsOnMainThread();
193 nsCOMPtr<nsIEventTarget> target =
194 mSyncEventResponseTarget ? mSyncEventResponseTarget : mSyncLoopTarget;
195 return target.forget();
198 #ifdef DEBUG
199 void DebugStoreWorkerRef(RefPtr<StrongWorkerRef>& aWorkerRef) {
200 MOZ_ASSERT(!NS_IsMainThread());
201 mXHR->mTSWorkerRef = new ThreadSafeWorkerRef(aWorkerRef);
204 void DebugForgetWorkerRef() {
205 MOZ_ASSERT(!NS_IsMainThread());
206 mXHR->mTSWorkerRef = nullptr;
208 #endif
210 private:
211 ~Proxy() {
212 MOZ_ASSERT(!mXHR);
213 MOZ_ASSERT(!mXHRUpload);
214 MOZ_ASSERT(!mOutstandingSendCount);
218 class WorkerThreadProxySyncRunnable : public WorkerMainThreadRunnable {
219 protected:
220 RefPtr<Proxy> mProxy;
222 private:
223 // mErrorCode is set on the main thread by MainThreadRun and it's used at the
224 // end of the Dispatch() to return the error code.
225 nsresult mErrorCode;
227 public:
228 WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
229 : WorkerMainThreadRunnable(aWorkerPrivate, "XHR"_ns),
230 mProxy(aProxy),
231 mErrorCode(NS_OK) {
232 MOZ_ASSERT(aWorkerPrivate);
233 MOZ_ASSERT(aProxy);
234 aWorkerPrivate->AssertIsOnWorkerThread();
237 void Dispatch(WorkerStatus aFailStatus, ErrorResult& aRv) {
238 WorkerMainThreadRunnable::Dispatch(aFailStatus, aRv);
239 if (NS_WARN_IF(aRv.Failed())) {
240 return;
243 if (NS_FAILED(mErrorCode)) {
244 aRv.Throw(mErrorCode);
248 protected:
249 virtual ~WorkerThreadProxySyncRunnable() = default;
251 virtual void RunOnMainThread(ErrorResult& aRv) = 0;
253 private:
254 virtual bool MainThreadRun() override;
257 class SendRunnable final : public WorkerThreadProxySyncRunnable {
258 RefPtr<BlobImpl> mBlobImpl;
259 nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
260 bool mHasUploadListeners;
262 public:
263 SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
264 BlobImpl* aBlobImpl)
265 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
266 mBlobImpl(aBlobImpl),
267 mHasUploadListeners(false) {}
269 void SetHaveUploadListeners(bool aHasUploadListeners) {
270 mHasUploadListeners = aHasUploadListeners;
273 void SetSyncLoopTarget(nsIEventTarget* aSyncLoopTarget) {
274 mSyncLoopTarget = aSyncLoopTarget;
277 private:
278 ~SendRunnable() = default;
280 virtual void RunOnMainThread(ErrorResult& aRv) override;
283 namespace {
285 class MainThreadProxyRunnable : public MainThreadWorkerSyncRunnable {
286 protected:
287 RefPtr<Proxy> mProxy;
289 MainThreadProxyRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
290 const char* aName = "MainThreadProxyRunnable")
291 : MainThreadWorkerSyncRunnable(aWorkerPrivate, aProxy->GetEventTarget(),
292 aName),
293 mProxy(aProxy) {
294 MOZ_ASSERT(aProxy);
297 virtual ~MainThreadProxyRunnable() = default;
300 class AsyncTeardownRunnable final : public Runnable {
301 RefPtr<Proxy> mProxy;
303 public:
304 explicit AsyncTeardownRunnable(Proxy* aProxy)
305 : Runnable("dom::AsyncTeardownRunnable"), mProxy(aProxy) {
306 MOZ_ASSERT(aProxy);
309 private:
310 ~AsyncTeardownRunnable() = default;
312 NS_IMETHOD
313 Run() override {
314 AssertIsOnMainThread();
316 mProxy->Teardown();
317 mProxy = nullptr;
319 return NS_OK;
323 class LoadStartDetectionRunnable final : public Runnable,
324 public nsIDOMEventListener {
325 WorkerPrivate* mWorkerPrivate;
326 RefPtr<Proxy> mProxy;
327 RefPtr<XMLHttpRequest> mXHR;
328 uint32_t mChannelId;
329 bool mReceivedLoadStart;
331 class ProxyCompleteRunnable final : public MainThreadProxyRunnable {
332 uint32_t mChannelId;
334 public:
335 ProxyCompleteRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
336 uint32_t aChannelId)
337 : MainThreadProxyRunnable(aWorkerPrivate, aProxy,
338 "ProxyCompleteRunnable"),
339 mChannelId(aChannelId) {}
341 private:
342 ~ProxyCompleteRunnable() = default;
344 virtual bool WorkerRun(JSContext* aCx,
345 WorkerPrivate* aWorkerPrivate) override {
346 if (mChannelId != mProxy->mOuterChannelId) {
347 // Threads raced, this event is now obsolete.
348 return true;
351 if (mSyncLoopTarget) {
352 aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, NS_OK);
355 XMLHttpRequestWorker* xhrw = mProxy->mXMLHttpRequestPrivate.get();
356 if (xhrw && xhrw->SendInProgress()) {
357 xhrw->Unpin();
360 return true;
363 nsresult Cancel() override { return Run(); }
366 public:
367 explicit LoadStartDetectionRunnable(Proxy* aProxy)
368 : Runnable("dom::LoadStartDetectionRunnable"),
369 mWorkerPrivate(aProxy->mWorkerPrivate),
370 mProxy(aProxy),
371 mXHR(aProxy->mXHR),
372 mChannelId(mProxy->mInnerChannelId),
373 mReceivedLoadStart(false) {
374 AssertIsOnMainThread();
377 NS_DECL_ISUPPORTS_INHERITED
378 NS_DECL_NSIRUNNABLE
379 NS_DECL_NSIDOMEVENTLISTENER
381 bool RegisterAndDispatch() {
382 AssertIsOnMainThread();
384 if (NS_FAILED(
385 mXHR->AddEventListener(Events::loadstart, this, false, false))) {
386 NS_WARNING("Failed to add event listener!");
387 return false;
390 return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(this));
393 private:
394 ~LoadStartDetectionRunnable() { AssertIsOnMainThread(); }
397 class EventRunnable final : public MainThreadProxyRunnable {
398 const EventType& mType;
399 UniquePtr<XMLHttpRequestWorker::ResponseData> mResponseData;
400 nsString mResponseURL;
401 nsCString mStatusText;
402 uint64_t mLoaded;
403 uint64_t mTotal;
404 uint32_t mEventStreamId;
405 uint32_t mStatus;
406 uint16_t mReadyState;
407 bool mUploadEvent;
408 bool mProgressEvent;
409 bool mLengthComputable;
410 nsresult mStatusResult;
411 nsresult mErrorDetail;
412 // mScopeObj is used in PreDispatch only. We init it in our constructor, and
413 // reset() in PreDispatch, to ensure that it's not still linked into the
414 // runtime once we go off-thread.
415 JS::PersistentRooted<JSObject*> mScopeObj;
417 public:
418 EventRunnable(Proxy* aProxy, bool aUploadEvent, const EventType& aType,
419 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal,
420 JS::Handle<JSObject*> aScopeObj)
421 : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy,
422 "EventRunnable"),
423 mType(aType),
424 mResponseData(new XMLHttpRequestWorker::ResponseData()),
425 mLoaded(aLoaded),
426 mTotal(aTotal),
427 mEventStreamId(aProxy->mInnerEventStreamId),
428 mStatus(0),
429 mReadyState(0),
430 mUploadEvent(aUploadEvent),
431 mProgressEvent(true),
432 mLengthComputable(aLengthComputable),
433 mStatusResult(NS_OK),
434 mErrorDetail(NS_OK),
435 mScopeObj(RootingCx(), aScopeObj) {}
437 EventRunnable(Proxy* aProxy, bool aUploadEvent, const EventType& aType,
438 JS::Handle<JSObject*> aScopeObj)
439 : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy,
440 "EventRunnable"),
441 mType(aType),
442 mResponseData(new XMLHttpRequestWorker::ResponseData()),
443 mLoaded(0),
444 mTotal(0),
445 mEventStreamId(aProxy->mInnerEventStreamId),
446 mStatus(0),
447 mReadyState(0),
448 mUploadEvent(aUploadEvent),
449 mProgressEvent(false),
450 mLengthComputable(0),
451 mStatusResult(NS_OK),
452 mErrorDetail(NS_OK),
453 mScopeObj(RootingCx(), aScopeObj) {}
455 private:
456 ~EventRunnable() = default;
458 bool PreDispatch(WorkerPrivate* /* unused */) final;
459 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
462 class SyncTeardownRunnable final : public WorkerThreadProxySyncRunnable {
463 public:
464 SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
465 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy) {}
467 private:
468 ~SyncTeardownRunnable() = default;
470 virtual void RunOnMainThread(ErrorResult& aRv) override {
471 mProxy->Teardown();
472 MOZ_ASSERT(!mProxy->mSyncLoopTarget);
476 class SetBackgroundRequestRunnable final
477 : public WorkerThreadProxySyncRunnable {
478 bool mValue;
480 public:
481 SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
482 bool aValue)
483 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue) {}
485 private:
486 ~SetBackgroundRequestRunnable() = default;
488 virtual void RunOnMainThread(ErrorResult& aRv) override {
489 // XXXedgar, do we intend to ignore the errors?
490 mProxy->mXHR->SetMozBackgroundRequest(mValue, aRv);
494 class SetWithCredentialsRunnable final : public WorkerThreadProxySyncRunnable {
495 bool mValue;
497 public:
498 SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
499 bool aValue)
500 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue) {}
502 private:
503 ~SetWithCredentialsRunnable() = default;
505 virtual void RunOnMainThread(ErrorResult& aRv) override {
506 mProxy->mXHR->SetWithCredentials(mValue, aRv);
510 class SetResponseTypeRunnable final : public WorkerThreadProxySyncRunnable {
511 XMLHttpRequestResponseType mResponseType;
513 public:
514 SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
515 XMLHttpRequestResponseType aResponseType)
516 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
517 mResponseType(aResponseType) {}
519 XMLHttpRequestResponseType ResponseType() { return mResponseType; }
521 private:
522 ~SetResponseTypeRunnable() = default;
524 virtual void RunOnMainThread(ErrorResult& aRv) override {
525 mProxy->mXHR->SetResponseTypeRaw(mResponseType);
526 mResponseType = mProxy->mXHR->ResponseType();
530 class SetTimeoutRunnable final : public WorkerThreadProxySyncRunnable {
531 uint32_t mTimeout;
533 public:
534 SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
535 uint32_t aTimeout)
536 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
537 mTimeout(aTimeout) {}
539 private:
540 ~SetTimeoutRunnable() = default;
542 virtual void RunOnMainThread(ErrorResult& aRv) override {
543 mProxy->mXHR->SetTimeout(mTimeout, aRv);
547 class AbortRunnable final : public WorkerThreadProxySyncRunnable {
548 public:
549 AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
550 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy) {}
552 private:
553 ~AbortRunnable() = default;
555 virtual void RunOnMainThread(ErrorResult& aRv) override;
558 class GetAllResponseHeadersRunnable final
559 : public WorkerThreadProxySyncRunnable {
560 nsCString& mResponseHeaders;
562 public:
563 GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
564 nsCString& aResponseHeaders)
565 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
566 mResponseHeaders(aResponseHeaders) {}
568 private:
569 ~GetAllResponseHeadersRunnable() = default;
571 virtual void RunOnMainThread(ErrorResult& aRv) override {
572 mProxy->mXHR->GetAllResponseHeaders(mResponseHeaders, aRv);
576 class GetResponseHeaderRunnable final : public WorkerThreadProxySyncRunnable {
577 const nsCString mHeader;
578 nsCString& mValue;
580 public:
581 GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
582 const nsACString& aHeader, nsCString& aValue)
583 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
584 mHeader(aHeader),
585 mValue(aValue) {}
587 private:
588 ~GetResponseHeaderRunnable() = default;
590 virtual void RunOnMainThread(ErrorResult& aRv) override {
591 mProxy->mXHR->GetResponseHeader(mHeader, mValue, aRv);
595 class OpenRunnable final : public WorkerThreadProxySyncRunnable {
596 nsCString mMethod;
597 nsString mURL;
598 Optional<nsAString> mUser;
599 nsString mUserStr;
600 Optional<nsAString> mPassword;
601 nsString mPasswordStr;
602 bool mBackgroundRequest;
603 bool mWithCredentials;
604 uint32_t mTimeout;
605 XMLHttpRequestResponseType mResponseType;
606 const nsString mMimeTypeOverride;
608 // Remember the worker thread's stack when the XHR was opened, so that it can
609 // be passed on to the net monitor.
610 UniquePtr<SerializedStackHolder> mOriginStack;
612 // Remember the worker thread's stack when the XHR was opened for profiling
613 // purposes.
614 UniquePtr<ProfileChunkedBuffer> mSource;
616 public:
617 OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
618 const nsACString& aMethod, const nsAString& aURL,
619 const Optional<nsAString>& aUser,
620 const Optional<nsAString>& aPassword, bool aBackgroundRequest,
621 bool aWithCredentials, uint32_t aTimeout,
622 XMLHttpRequestResponseType aResponseType,
623 const nsString& aMimeTypeOverride,
624 UniquePtr<SerializedStackHolder> aOriginStack,
625 UniquePtr<ProfileChunkedBuffer> aSource = nullptr)
626 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
627 mMethod(aMethod),
628 mURL(aURL),
629 mBackgroundRequest(aBackgroundRequest),
630 mWithCredentials(aWithCredentials),
631 mTimeout(aTimeout),
632 mResponseType(aResponseType),
633 mMimeTypeOverride(aMimeTypeOverride),
634 mOriginStack(std::move(aOriginStack)),
635 mSource(std::move(aSource)) {
636 if (aUser.WasPassed()) {
637 mUserStr = aUser.Value();
638 mUser = &mUserStr;
640 if (aPassword.WasPassed()) {
641 mPasswordStr = aPassword.Value();
642 mPassword = &mPasswordStr;
646 private:
647 ~OpenRunnable() = default;
649 virtual void RunOnMainThread(ErrorResult& aRv) override {
650 WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
651 mProxy->mWorkerPrivate = mWorkerPrivate;
653 MainThreadRunInternal(aRv);
655 mProxy->mWorkerPrivate = oldWorker;
658 void MainThreadRunInternal(ErrorResult& aRv);
661 class SetRequestHeaderRunnable final : public WorkerThreadProxySyncRunnable {
662 nsCString mHeader;
663 nsCString mValue;
665 public:
666 SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
667 const nsACString& aHeader, const nsACString& aValue)
668 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
669 mHeader(aHeader),
670 mValue(aValue) {}
672 private:
673 ~SetRequestHeaderRunnable() = default;
675 virtual void RunOnMainThread(ErrorResult& aRv) override {
676 mProxy->mXHR->SetRequestHeader(mHeader, mValue, aRv);
680 class OverrideMimeTypeRunnable final : public WorkerThreadProxySyncRunnable {
681 nsString mMimeType;
683 public:
684 OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
685 const nsAString& aMimeType)
686 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
687 mMimeType(aMimeType) {}
689 private:
690 ~OverrideMimeTypeRunnable() = default;
692 virtual void RunOnMainThread(ErrorResult& aRv) override {
693 mProxy->mXHR->OverrideMimeType(mMimeType, aRv);
697 class AutoUnpinXHR {
698 XMLHttpRequestWorker* mXMLHttpRequestPrivate;
700 public:
701 explicit AutoUnpinXHR(XMLHttpRequestWorker* aXMLHttpRequestPrivate)
702 : mXMLHttpRequestPrivate(aXMLHttpRequestPrivate) {
703 MOZ_ASSERT(aXMLHttpRequestPrivate);
706 ~AutoUnpinXHR() {
707 if (mXMLHttpRequestPrivate) {
708 mXMLHttpRequestPrivate->Unpin();
712 void Clear() { mXMLHttpRequestPrivate = nullptr; }
715 } // namespace
717 bool Proxy::Init() {
718 AssertIsOnMainThread();
719 MOZ_ASSERT(mWorkerPrivate);
721 if (mXHR) {
722 return true;
725 nsPIDOMWindowInner* ownerWindow = mWorkerPrivate->GetWindow();
726 if (ownerWindow && !ownerWindow->IsCurrentInnerWindow()) {
727 NS_WARNING("Window has navigated, cannot create XHR here.");
728 return false;
731 mXHR = new XMLHttpRequestMainThread(ownerWindow ? ownerWindow->AsGlobal()
732 : nullptr);
733 mXHR->Construct(mWorkerPrivate->GetPrincipal(),
734 mWorkerPrivate->CookieJarSettings(), true,
735 mWorkerPrivate->GetBaseURI(), mWorkerPrivate->GetLoadGroup(),
736 mWorkerPrivate->GetPerformanceStorage(),
737 mWorkerPrivate->CSPEventListener());
739 mXHR->SetParameters(mMozAnon, mMozSystem);
740 mXHR->SetClientInfoAndController(mClientInfo, mController);
742 ErrorResult rv;
743 mXHRUpload = mXHR->GetUpload(rv);
744 if (NS_WARN_IF(rv.Failed())) {
745 mXHR = nullptr;
746 return false;
749 if (!AddRemoveEventListeners(false, true)) {
750 mXHR = nullptr;
751 mXHRUpload = nullptr;
752 return false;
755 return true;
758 void Proxy::Teardown() {
759 AssertIsOnMainThread();
761 if (mXHR) {
762 Reset();
764 // NB: We are intentionally dropping events coming from xhr.abort on the
765 // floor.
766 AddRemoveEventListeners(false, false);
768 ErrorResult rv;
769 mXHR->Abort(rv);
770 if (NS_WARN_IF(rv.Failed())) {
771 rv.SuppressException();
774 if (mOutstandingSendCount) {
775 if (mSyncLoopTarget) {
776 // We have an unclosed sync loop. Fix that now.
777 RefPtr<MainThreadStopSyncLoopRunnable> runnable =
778 new MainThreadStopSyncLoopRunnable(
779 mWorkerPrivate, std::move(mSyncLoopTarget), NS_ERROR_FAILURE);
780 MOZ_ALWAYS_TRUE(runnable->Dispatch());
783 mOutstandingSendCount = 0;
786 mWorkerPrivate = nullptr;
787 mXHRUpload = nullptr;
788 mXHR = nullptr;
791 MOZ_ASSERT(!mWorkerPrivate);
792 MOZ_ASSERT(!mSyncLoopTarget);
793 // If there are rare edge cases left that violate our invariants
794 // just ensure that they won't harm us too much.
795 mWorkerPrivate = nullptr;
796 mSyncLoopTarget = nullptr;
799 bool Proxy::AddRemoveEventListeners(bool aUpload, bool aAdd) {
800 AssertIsOnMainThread();
802 NS_ASSERTION(!aUpload || (mUploadEventListenersAttached && !aAdd) ||
803 (!mUploadEventListenersAttached && aAdd),
804 "Messed up logic for upload listeners!");
806 RefPtr<DOMEventTargetHelper> targetHelper =
807 aUpload ? static_cast<XMLHttpRequestUpload*>(mXHRUpload.get())
808 : static_cast<XMLHttpRequestEventTarget*>(mXHR.get());
809 MOZ_ASSERT(targetHelper, "This should never fail!");
811 for (const EventType* type : Events::All) {
812 if (aUpload && *type == Events::readystatechange) {
813 continue;
815 if (aAdd) {
816 if (NS_FAILED(targetHelper->AddEventListener(*type, this, false))) {
817 return false;
819 } else {
820 targetHelper->RemoveEventListener(*type, this, false);
824 if (aUpload) {
825 mUploadEventListenersAttached = aAdd;
828 return true;
831 NS_IMPL_ISUPPORTS(Proxy, nsIDOMEventListener)
833 NS_IMETHODIMP
834 Proxy::HandleEvent(Event* aEvent) {
835 AssertIsOnMainThread();
837 // EventRunnable::WorkerRun will bail out if mXMLHttpRequestWorker is null,
838 // so we do not need to prevent the dispatch from the main thread such that
839 // we do not need to touch it off-worker-thread.
840 if (!mWorkerPrivate) {
841 NS_ERROR("Shouldn't get here!");
842 return NS_OK;
845 nsAutoString _type;
846 aEvent->GetType(_type);
847 const EventType* typePtr = Events::Find(_type);
848 MOZ_DIAGNOSTIC_ASSERT(typePtr, "Shouldn't get non-XMLHttpRequest events");
849 const EventType& type = *typePtr;
851 bool isUploadTarget = mXHR != aEvent->GetTarget();
852 ProgressEvent* progressEvent = aEvent->AsProgressEvent();
854 if (mInOpen && type == Events::readystatechange) {
855 if (mXHR->ReadyState() == 1) {
856 mInnerEventStreamId++;
861 AutoJSAPI jsapi;
862 JSObject* junkScope = xpc::UnprivilegedJunkScope(fallible);
863 if (!junkScope || !jsapi.Init(junkScope)) {
864 return NS_ERROR_FAILURE;
866 JSContext* cx = jsapi.cx();
868 JS::Rooted<JS::Value> value(cx);
869 if (!GetOrCreateDOMReflectorNoWrap(cx, mXHR, &value)) {
870 return NS_ERROR_FAILURE;
873 JS::Rooted<JSObject*> scope(cx, &value.toObject());
875 RefPtr<EventRunnable> runnable;
876 if (progressEvent) {
877 if (!mIsSyncXHR || type != Events::progress) {
878 runnable = new EventRunnable(
879 this, isUploadTarget, type, progressEvent->LengthComputable(),
880 progressEvent->Loaded(), progressEvent->Total(), scope);
882 } else {
883 runnable = new EventRunnable(this, isUploadTarget, type, scope);
886 if (runnable) {
887 runnable->Dispatch();
891 if (!isUploadTarget) {
892 if (type == Events::loadstart) {
893 mMainThreadSeenLoadStart = true;
894 } else if (mMainThreadSeenLoadStart && type == Events::loadend) {
895 mMainThreadSeenLoadStart = false;
897 RefPtr<LoadStartDetectionRunnable> runnable =
898 new LoadStartDetectionRunnable(this);
899 if (!runnable->RegisterAndDispatch()) {
900 NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
905 return NS_OK;
908 NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable, Runnable,
909 nsIDOMEventListener)
911 NS_IMETHODIMP
912 LoadStartDetectionRunnable::Run() {
913 AssertIsOnMainThread();
915 mXHR->RemoveEventListener(Events::loadstart, this, false);
917 if (!mReceivedLoadStart) {
918 if (mProxy->mOutstandingSendCount > 1) {
919 mProxy->mOutstandingSendCount--;
920 } else if (mProxy->mOutstandingSendCount == 1) {
921 mProxy->Reset();
923 RefPtr<ProxyCompleteRunnable> runnable =
924 new ProxyCompleteRunnable(mWorkerPrivate, mProxy, mChannelId);
925 if (runnable->Dispatch()) {
926 mProxy->mWorkerPrivate = nullptr;
927 mProxy->mSyncLoopTarget = nullptr;
928 mProxy->mOutstandingSendCount--;
933 mProxy = nullptr;
934 mXHR = nullptr;
935 return NS_OK;
938 NS_IMETHODIMP
939 LoadStartDetectionRunnable::HandleEvent(Event* aEvent) {
940 AssertIsOnMainThread();
942 #ifdef DEBUG
944 nsAutoString type;
945 aEvent->GetType(type);
946 MOZ_ASSERT(type == Events::loadstart);
948 #endif
950 mReceivedLoadStart = true;
951 return NS_OK;
954 bool EventRunnable::PreDispatch(WorkerPrivate* /* unused */) {
955 AssertIsOnMainThread();
957 AutoJSAPI jsapi;
958 DebugOnly<bool> ok = jsapi.Init(xpc::NativeGlobal(mScopeObj));
959 MOZ_ASSERT(ok);
960 JSContext* cx = jsapi.cx();
961 // Now keep the mScopeObj alive for the duration
962 JS::Rooted<JSObject*> scopeObj(cx, mScopeObj);
963 // And reset mScopeObj now, before we have a chance to run its destructor on
964 // some background thread.
965 mScopeObj.reset();
967 RefPtr<XMLHttpRequestMainThread>& xhr = mProxy->mXHR;
968 MOZ_ASSERT(xhr);
970 ErrorResult rv;
972 XMLHttpRequestResponseType type = xhr->ResponseType();
974 // We want to take the result data only if this is available.
975 if (mType == Events::readystatechange) {
976 switch (type) {
977 case XMLHttpRequestResponseType::_empty:
978 case XMLHttpRequestResponseType::Text: {
979 xhr->GetResponseText(mResponseData->mResponseText, rv);
980 mResponseData->mResponseResult = rv.StealNSResult();
981 break;
984 case XMLHttpRequestResponseType::Blob: {
985 mResponseData->mResponseBlobImpl = xhr->GetResponseBlobImpl();
986 break;
989 case XMLHttpRequestResponseType::Arraybuffer: {
990 mResponseData->mResponseArrayBufferBuilder =
991 xhr->GetResponseArrayBufferBuilder();
992 break;
995 case XMLHttpRequestResponseType::Json: {
996 mResponseData->mResponseResult =
997 xhr->GetResponseTextForJSON(mResponseData->mResponseJSON);
998 break;
1001 default:
1002 MOZ_ASSERT_UNREACHABLE("Invalid response type");
1003 return false;
1007 mStatus = xhr->GetStatus(rv);
1008 mStatusResult = rv.StealNSResult();
1010 mErrorDetail = xhr->ErrorDetail();
1012 xhr->GetStatusText(mStatusText, rv);
1013 MOZ_ASSERT(!rv.Failed());
1015 mReadyState = xhr->ReadyState();
1017 xhr->GetResponseURL(mResponseURL);
1019 return true;
1022 bool EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1023 if (mEventStreamId != mProxy->mOuterEventStreamId) {
1024 // Threads raced, this event is now obsolete.
1025 return true;
1028 if (!mProxy->mXMLHttpRequestPrivate) {
1029 // Object was finalized, bail.
1030 return true;
1033 if (mType == Events::loadend) {
1034 mProxy->mLastErrorDetailAtLoadend = mErrorDetail;
1037 if (mType == Events::loadstart) {
1038 if (mUploadEvent) {
1039 mProxy->mSeenUploadLoadStart = true;
1040 } else {
1041 mProxy->mSeenLoadStart = true;
1043 } else if (mType == Events::loadend) {
1044 if (mUploadEvent) {
1045 mProxy->mSeenUploadLoadStart = false;
1046 if (mProxy->mDispatchPrematureAbortEventToUpload) {
1047 // We've already dispatched premature abort events.
1048 return true;
1050 } else {
1051 mProxy->mSeenLoadStart = false;
1052 if (mProxy->mDispatchPrematureAbortEvent) {
1053 // We've already dispatched premature abort events.
1054 return true;
1057 } else if (mType == Events::abort) {
1058 if ((mUploadEvent && mProxy->mDispatchPrematureAbortEventToUpload) ||
1059 (!mUploadEvent && mProxy->mDispatchPrematureAbortEvent)) {
1060 // We've already dispatched premature abort events.
1061 return true;
1065 if (mProgressEvent) {
1066 // Cache these for premature abort events.
1067 if (mUploadEvent) {
1068 mProxy->mLastUploadLengthComputable = mLengthComputable;
1069 mProxy->mLastUploadLoaded = mLoaded;
1070 mProxy->mLastUploadTotal = mTotal;
1071 } else {
1072 mProxy->mLastLengthComputable = mLengthComputable;
1073 mProxy->mLastLoaded = mLoaded;
1074 mProxy->mLastTotal = mTotal;
1078 UniquePtr<XMLHttpRequestWorker::StateData> state(
1079 new XMLHttpRequestWorker::StateData());
1081 state->mStatusResult = mStatusResult;
1082 state->mStatus = mStatus;
1084 state->mStatusText = mStatusText;
1086 state->mReadyState = mReadyState;
1088 state->mResponseURL = mResponseURL;
1090 XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate;
1091 xhr->UpdateState(std::move(state), mType == Events::readystatechange
1092 ? std::move(mResponseData)
1093 : nullptr);
1095 if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
1096 return true;
1099 XMLHttpRequestEventTarget* target;
1100 if (mUploadEvent) {
1101 target = xhr->GetUploadObjectNoCreate();
1102 } else {
1103 target = xhr;
1106 MOZ_ASSERT(target);
1108 RefPtr<Event> event;
1109 if (mProgressEvent) {
1110 ProgressEventInit init;
1111 init.mBubbles = false;
1112 init.mCancelable = false;
1113 init.mLengthComputable = mLengthComputable;
1114 init.mLoaded = mLoaded;
1115 init.mTotal = mTotal;
1117 event = ProgressEvent::Constructor(target, mType, init);
1118 } else {
1119 event = NS_NewDOMEvent(target, nullptr, nullptr);
1121 if (event) {
1122 event->InitEvent(mType, false, false);
1126 if (!event) {
1127 return false;
1130 event->SetTrusted(true);
1132 if (MOZ_LOG_TEST(gXMLHttpRequestLog, LogLevel::Debug)) {
1133 nsAutoString type;
1134 event->GetType(type);
1135 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
1136 ("%p firing %s event (%u,%u,%" PRIu64 ",%" PRIu64 ")",
1137 mProxy->mXHR.get(), NS_ConvertUTF16toUTF8(type).get(),
1138 mUploadEvent, mLengthComputable, mLoaded, mTotal));
1141 target->DispatchEvent(*event);
1143 return true;
1146 bool WorkerThreadProxySyncRunnable::MainThreadRun() {
1147 AssertIsOnMainThread();
1149 nsCOMPtr<nsIEventTarget> tempTarget = mSyncLoopTarget;
1151 mProxy->mSyncEventResponseTarget.swap(tempTarget);
1153 ErrorResult rv;
1154 RunOnMainThread(rv);
1155 mErrorCode = rv.StealNSResult();
1157 mProxy->mSyncEventResponseTarget.swap(tempTarget);
1159 return true;
1162 void AbortRunnable::RunOnMainThread(ErrorResult& aRv) {
1163 mProxy->mInnerEventStreamId++;
1165 WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
1166 mProxy->mWorkerPrivate = mWorkerPrivate;
1168 mProxy->mXHR->Abort(aRv);
1170 mProxy->mWorkerPrivate = oldWorker;
1172 mProxy->Reset();
1175 void OpenRunnable::MainThreadRunInternal(ErrorResult& aRv) {
1176 if (!mProxy->Init()) {
1177 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1178 return;
1181 if (mBackgroundRequest) {
1182 mProxy->mXHR->SetMozBackgroundRequestExternal(mBackgroundRequest, aRv);
1183 if (aRv.Failed()) {
1184 return;
1188 if (mOriginStack) {
1189 mProxy->mXHR->SetOriginStack(std::move(mOriginStack));
1192 if (mWithCredentials) {
1193 mProxy->mXHR->SetWithCredentials(mWithCredentials, aRv);
1194 if (NS_WARN_IF(aRv.Failed())) {
1195 return;
1199 if (mTimeout) {
1200 mProxy->mXHR->SetTimeout(mTimeout, aRv);
1201 if (NS_WARN_IF(aRv.Failed())) {
1202 return;
1206 if (!mMimeTypeOverride.IsVoid()) {
1207 mProxy->mXHR->OverrideMimeType(mMimeTypeOverride, aRv);
1208 if (NS_WARN_IF(aRv.Failed())) {
1209 return;
1213 MOZ_ASSERT(!mProxy->mInOpen);
1214 mProxy->mInOpen = true;
1216 mProxy->mXHR->Open(
1217 mMethod, mURL, true, mUser.WasPassed() ? mUser.Value() : VoidString(),
1218 mPassword.WasPassed() ? mPassword.Value() : VoidString(), aRv);
1220 MOZ_ASSERT(mProxy->mInOpen);
1221 mProxy->mInOpen = false;
1223 if (NS_WARN_IF(aRv.Failed())) {
1224 return;
1227 if (mSource) {
1228 mProxy->mXHR->SetSource(std::move(mSource));
1231 mProxy->mXHR->SetResponseType(mResponseType, aRv);
1234 void SendRunnable::RunOnMainThread(ErrorResult& aRv) {
1235 // Before we change any state let's check if we can send.
1236 if (!mProxy->mXHR->CanSend(aRv)) {
1237 return;
1240 Nullable<
1241 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>
1242 payload;
1244 if (!mBlobImpl) {
1245 payload.SetNull();
1246 } else {
1247 JS::Rooted<JSObject*> globalObject(RootingCx(),
1248 xpc::UnprivilegedJunkScope(fallible));
1249 if (NS_WARN_IF(!globalObject)) {
1250 aRv.Throw(NS_ERROR_FAILURE);
1251 return;
1254 nsCOMPtr<nsIGlobalObject> parent = xpc::NativeGlobal(globalObject);
1255 if (NS_WARN_IF(!parent)) {
1256 aRv.Throw(NS_ERROR_FAILURE);
1257 return;
1260 RefPtr<Blob> blob = Blob::Create(parent, mBlobImpl);
1261 MOZ_ASSERT(blob);
1263 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString&
1264 ref = payload.SetValue();
1265 ref.SetAsBlob() = blob;
1268 // Send() has been already called, reset the proxy.
1269 if (mProxy->mWorkerPrivate) {
1270 mProxy->Reset();
1273 mProxy->mWorkerPrivate = mWorkerPrivate;
1275 MOZ_ASSERT(!mProxy->mSyncLoopTarget);
1276 mProxy->mSyncLoopTarget.swap(mSyncLoopTarget);
1278 if (mHasUploadListeners) {
1279 // Send() can be called more than once before failure,
1280 // so don't attach the upload listeners more than once.
1281 if (!mProxy->mUploadEventListenersAttached &&
1282 !mProxy->AddRemoveEventListeners(true, true)) {
1283 MOZ_ASSERT(false, "This should never fail!");
1287 mProxy->mInnerChannelId++;
1289 mProxy->mXHR->Send(payload, aRv);
1291 if (!aRv.Failed()) {
1292 mProxy->mOutstandingSendCount++;
1294 if (!mHasUploadListeners) {
1295 // Send() can be called more than once before failure,
1296 // so don't attach the upload listeners more than once.
1297 if (!mProxy->mUploadEventListenersAttached &&
1298 !mProxy->AddRemoveEventListeners(true, true)) {
1299 MOZ_ASSERT(false, "This should never fail!");
1302 } else {
1303 // In case of failure we just break the sync loop
1304 mProxy->mSyncLoopTarget = nullptr;
1305 mSyncLoopTarget = nullptr;
1309 XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate,
1310 nsIGlobalObject* aGlobalObject)
1311 : XMLHttpRequest(aGlobalObject),
1312 mWorkerPrivate(aWorkerPrivate),
1313 mResponseType(XMLHttpRequestResponseType::_empty),
1314 mStateData(new StateData()),
1315 mResponseData(new ResponseData()),
1316 mResponseArrayBufferValue(nullptr),
1317 mResponseJSONValue(JS::UndefinedValue()),
1318 mTimeout(0),
1319 mBackgroundRequest(false),
1320 mWithCredentials(false),
1321 mCanceled(false),
1322 mFlagSendActive(false),
1323 mMozAnon(false),
1324 mMozSystem(false),
1325 mMimeTypeOverride(VoidString()) {
1326 mWorkerPrivate->AssertIsOnWorkerThread();
1328 mozilla::HoldJSObjects(this);
1331 XMLHttpRequestWorker::~XMLHttpRequestWorker() {
1332 mWorkerPrivate->AssertIsOnWorkerThread();
1334 ReleaseProxy(XHRIsGoingAway);
1336 MOZ_ASSERT(!mWorkerRef);
1338 mozilla::DropJSObjects(this);
1341 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget)
1342 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget)
1344 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestWorker)
1345 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget)
1347 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestWorker)
1349 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestWorker,
1350 XMLHttpRequestEventTarget)
1351 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
1352 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
1353 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1355 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestWorker,
1356 XMLHttpRequestEventTarget)
1357 tmp->ReleaseProxy(XHRIsGoingAway);
1358 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
1359 tmp->mResponseData = nullptr;
1360 tmp->mResponseBlob = nullptr;
1361 tmp->mResponseArrayBufferValue = nullptr;
1362 tmp->mResponseJSONValue.setUndefined();
1363 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1365 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestWorker,
1366 XMLHttpRequestEventTarget)
1367 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseArrayBufferValue)
1368 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseJSONValue)
1369 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1371 /* static */
1372 already_AddRefed<XMLHttpRequest> XMLHttpRequestWorker::Construct(
1373 const GlobalObject& aGlobal, const MozXMLHttpRequestParameters& aParams,
1374 ErrorResult& aRv) {
1375 JSContext* cx = aGlobal.Context();
1376 WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
1377 MOZ_ASSERT(workerPrivate);
1379 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
1380 if (NS_WARN_IF(!global)) {
1381 aRv.Throw(NS_ERROR_FAILURE);
1382 return nullptr;
1385 RefPtr<XMLHttpRequestWorker> xhr =
1386 new XMLHttpRequestWorker(workerPrivate, global);
1388 if (workerPrivate->XHRParamsAllowed()) {
1389 if (aParams.mMozSystem)
1390 xhr->mMozAnon = true;
1391 else
1392 xhr->mMozAnon = aParams.mMozAnon;
1393 xhr->mMozSystem = aParams.mMozSystem;
1396 return xhr.forget();
1399 void XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType) {
1400 // Can't assert that we're on the worker thread here because mWorkerPrivate
1401 // may be gone.
1403 if (mProxy) {
1404 if (aType == XHRIsGoingAway) {
1405 // Coming here means the XHR was GC'd, so we can't be pinned.
1406 MOZ_ASSERT(!mProxy->mXMLHttpRequestPrivate ||
1407 !mProxy->mXMLHttpRequestPrivate->mPinnedSelfRef);
1409 // We need to clear our weak pointer on the worker thread, let's do it now
1410 // before doing it implicitly in the Proxy dtor on the wrong thread.
1411 mProxy->mXMLHttpRequestPrivate = nullptr;
1413 // We're in a GC finalizer, so we can't do a sync call here (and we don't
1414 // need to).
1415 RefPtr<AsyncTeardownRunnable> runnable =
1416 new AsyncTeardownRunnable(mProxy);
1417 mProxy = nullptr;
1419 if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
1420 NS_ERROR("Failed to dispatch teardown runnable!");
1422 } else {
1423 // This isn't necessary if the worker is going away or the XHR is going
1424 // away.
1425 if (aType == Default) {
1426 // Don't let any more events run.
1427 mProxy->mOuterEventStreamId++;
1430 // Ensure we are unpinned before we clear the weak reference.
1431 RefPtr<XMLHttpRequestWorker> self = this;
1432 if (mPinnedSelfRef) {
1433 Unpin();
1435 mProxy->mXMLHttpRequestPrivate = nullptr;
1437 // We need to make a sync call here.
1438 RefPtr<SyncTeardownRunnable> runnable =
1439 new SyncTeardownRunnable(mWorkerPrivate, mProxy);
1440 mProxy = nullptr;
1442 IgnoredErrorResult forAssertionsOnly;
1443 // This runnable _must_ be executed.
1444 runnable->Dispatch(Dead, forAssertionsOnly);
1445 MOZ_DIAGNOSTIC_ASSERT(!forAssertionsOnly.Failed());
1450 void XMLHttpRequestWorker::MaybePin(ErrorResult& aRv) {
1451 mWorkerPrivate->AssertIsOnWorkerThread();
1453 if (mWorkerRef) {
1454 return;
1457 RefPtr<XMLHttpRequestWorker> self = this;
1458 mWorkerRef =
1459 StrongWorkerRef::Create(mWorkerPrivate, "XMLHttpRequestWorker", [self]() {
1460 if (!self->mCanceled) {
1461 self->mCanceled = true;
1462 self->ReleaseProxy(WorkerIsGoingAway);
1465 if (NS_WARN_IF(!mWorkerRef)) {
1466 aRv.Throw(NS_ERROR_FAILURE);
1467 return;
1470 mPinnedSelfRef = this;
1472 #ifdef DEBUG
1473 mProxy->DebugStoreWorkerRef(mWorkerRef);
1474 #endif
1477 void XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv) {
1478 mWorkerPrivate->AssertIsOnWorkerThread();
1479 MOZ_ASSERT(mProxy);
1481 // Only send readystatechange event when state changed.
1482 bool isStateChanged = false;
1483 if ((mStateData->mReadyState == 1 && mStateData->mFlagSend) ||
1484 mStateData->mReadyState == 2 || mStateData->mReadyState == 3) {
1485 isStateChanged = true;
1486 mStateData->mReadyState = 4;
1489 if (mProxy->mSeenUploadLoadStart) {
1490 MOZ_ASSERT(mUpload);
1492 FireEvent(mUpload, Events::abort, true, aRv);
1493 if (aRv.Failed()) {
1494 return;
1497 FireEvent(mUpload, Events::loadend, true, aRv);
1498 if (aRv.Failed()) {
1499 return;
1502 // Similarly to null check in ::Open, mProxy may have been cleared here.
1503 if (!mProxy) {
1504 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1505 return;
1508 mProxy->mSeenUploadLoadStart = false;
1509 mProxy->mDispatchPrematureAbortEventToUpload = true;
1512 if (mProxy->mSeenLoadStart) {
1513 if (isStateChanged) {
1514 FireEvent(this, Events::readystatechange, false, aRv);
1515 if (aRv.Failed()) {
1516 return;
1520 FireEvent(this, Events::abort, false, aRv);
1521 if (aRv.Failed()) {
1522 return;
1525 FireEvent(this, Events::loadend, false, aRv);
1526 if (aRv.Failed()) {
1527 return;
1530 // Similarly to null check in ::Open, mProxy may have been cleared here.
1531 if (!mProxy) {
1532 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1533 return;
1536 mProxy->mSeenLoadStart = false;
1537 mProxy->mDispatchPrematureAbortEvent = true;
1541 void XMLHttpRequestWorker::FireEvent(EventTarget* aTarget,
1542 const EventType& aEventType,
1543 bool aUploadTarget, ErrorResult& aRv) {
1544 mWorkerPrivate->AssertIsOnWorkerThread();
1545 MOZ_ASSERT(aTarget);
1547 if (!mProxy) {
1548 aRv.Throw(NS_ERROR_FAILURE);
1549 return;
1552 RefPtr<Event> event;
1553 if (aEventType == Events::readystatechange) {
1554 event = NS_NewDOMEvent(aTarget, nullptr, nullptr);
1555 event->InitEvent(aEventType, false, false);
1556 } else {
1557 if (mProxy->mIsSyncXHR && aEventType == Events::progress) {
1558 return;
1561 ProgressEventInit init;
1562 init.mBubbles = false;
1563 init.mCancelable = false;
1564 if (aUploadTarget) {
1565 init.mLengthComputable = mProxy->mLastUploadLengthComputable;
1566 init.mLoaded = mProxy->mLastUploadLoaded;
1567 init.mTotal = mProxy->mLastUploadTotal;
1568 } else {
1569 init.mLengthComputable = mProxy->mLastLengthComputable;
1570 init.mLoaded = mProxy->mLastLoaded;
1571 init.mTotal = mProxy->mLastTotal;
1573 event = ProgressEvent::Constructor(aTarget, aEventType, init);
1576 if (!event) {
1577 aRv.Throw(NS_ERROR_FAILURE);
1578 return;
1581 event->SetTrusted(true);
1583 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
1584 ("%p firing %s pre-abort event (%u,%u,%" PRIu64 ",%" PRIu64, this,
1585 aEventType.cStr, aUploadTarget,
1586 aUploadTarget ? mProxy->mLastUploadLengthComputable
1587 : mProxy->mLastLengthComputable,
1588 aUploadTarget ? mProxy->mLastUploadLoaded : mProxy->mLastLoaded,
1589 aUploadTarget ? mProxy->mLastUploadTotal : mProxy->mLastTotal));
1590 aTarget->DispatchEvent(*event);
1593 void XMLHttpRequestWorker::Unpin() {
1594 mWorkerPrivate->AssertIsOnWorkerThread();
1596 MOZ_ASSERT(mWorkerRef, "Mismatched calls to Unpin!");
1598 #ifdef DEBUG
1599 if (mProxy) {
1600 // The proxy will be gone if WorkerIsGoingAway
1601 mProxy->DebugForgetWorkerRef();
1603 #endif
1605 mWorkerRef = nullptr;
1607 mPinnedSelfRef = nullptr;
1610 void XMLHttpRequestWorker::SendInternal(const BodyExtractorBase* aBody,
1611 ErrorResult& aRv) {
1612 mWorkerPrivate->AssertIsOnWorkerThread();
1614 // We don't really need to keep the same body-type when we proxy the send()
1615 // call to the main-thread XHR. Let's extract the nsIInputStream from the
1616 // aBody and let's wrap it into a StreamBlobImpl.
1618 RefPtr<BlobImpl> blobImpl;
1620 if (aBody) {
1621 nsAutoCString charset;
1622 nsAutoCString defaultContentType;
1623 nsCOMPtr<nsIInputStream> uploadStream;
1625 uint64_t size_u64;
1626 aRv = aBody->GetAsStream(getter_AddRefs(uploadStream), &size_u64,
1627 defaultContentType, charset);
1628 if (NS_WARN_IF(aRv.Failed())) {
1629 return;
1632 blobImpl = StreamBlobImpl::Create(uploadStream.forget(),
1633 NS_ConvertUTF8toUTF16(defaultContentType),
1634 size_u64, u"StreamBlobImpl"_ns);
1635 MOZ_ASSERT(blobImpl);
1638 RefPtr<SendRunnable> sendRunnable =
1639 new SendRunnable(mWorkerPrivate, mProxy, blobImpl);
1641 // No send() calls when open is running.
1642 if (mProxy->mOpenCount) {
1643 aRv.Throw(NS_ERROR_FAILURE);
1644 return;
1647 bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
1649 MaybePin(aRv);
1650 if (aRv.Failed()) {
1651 return;
1654 RefPtr<XMLHttpRequestWorker> selfRef = this;
1655 AutoUnpinXHR autoUnpin(this);
1656 Maybe<AutoSyncLoopHolder> autoSyncLoop;
1658 nsCOMPtr<nsISerialEventTarget> syncLoopTarget;
1659 bool isSyncXHR = mProxy->mIsSyncXHR;
1660 if (isSyncXHR) {
1661 autoSyncLoop.emplace(mWorkerPrivate, Canceling);
1662 syncLoopTarget = autoSyncLoop->GetSerialEventTarget();
1663 if (!syncLoopTarget) {
1664 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1665 return;
1669 mProxy->mOuterChannelId++;
1670 mProxy->mDispatchPrematureAbortEvent = false;
1671 mProxy->mDispatchPrematureAbortEventToUpload = false;
1673 sendRunnable->SetSyncLoopTarget(syncLoopTarget);
1674 sendRunnable->SetHaveUploadListeners(hasUploadListeners);
1676 mStateData->mFlagSend = true;
1678 sendRunnable->Dispatch(Canceling, aRv);
1679 if (aRv.Failed()) {
1680 // Dispatch() may have spun the event loop and we may have already unrooted.
1681 // If so we don't want autoUnpin to try again.
1682 if (!mWorkerRef) {
1683 autoUnpin.Clear();
1685 return;
1688 if (!isSyncXHR) {
1689 autoUnpin.Clear();
1690 MOZ_ASSERT(!autoSyncLoop);
1691 return;
1694 autoUnpin.Clear();
1696 bool succeeded = NS_SUCCEEDED(autoSyncLoop->Run());
1697 mStateData->mFlagSend = false;
1699 // Throw appropriately If a sync XHR failed per spec's RequestErrorSteps
1700 if (isSyncXHR && mProxy) {
1701 nsresult error = mProxy->mLastErrorDetailAtLoadend;
1702 if (error == NS_ERROR_DOM_ABORT_ERR) {
1703 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Info,
1704 ("%p throwing NS_ERROR_DOM_ABORT_ERR", this));
1705 aRv.Throw(error);
1706 return;
1708 if (error == NS_ERROR_DOM_TIMEOUT_ERR) {
1709 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Info,
1710 ("%p throwing NS_ERROR_DOM_TIMEOUT_ERR", this));
1711 aRv.Throw(error);
1712 return;
1714 if (error == NS_ERROR_DOM_NETWORK_ERR ||
1715 NS_ERROR_GET_MODULE(error) == NS_ERROR_MODULE_NETWORK) {
1716 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Info,
1717 ("%p throwing NS_ERROR_DOM_NETWORK_ERR (0x%" PRIx32 ")", this,
1718 static_cast<uint32_t>(error)));
1719 aRv.Throw(NS_ERROR_DOM_NETWORK_ERR);
1720 return;
1724 // Don't clobber an existing exception that we may have thrown on aRv
1725 // already... though can there really be one? In any case, it seems to me
1726 // that this autoSyncLoop->Run() can never fail, since the StopSyncLoop call
1727 // for it will come from ProxyCompleteRunnable and that always passes true for
1728 // the second arg.
1729 if (!succeeded && !aRv.Failed()) {
1730 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
1731 ("%p SendInternal failed; throwing NS_ERROR_FAILURE", this));
1732 aRv.Throw(NS_ERROR_FAILURE);
1736 void XMLHttpRequestWorker::Open(const nsACString& aMethod,
1737 const nsAString& aUrl, bool aAsync,
1738 const Optional<nsAString>& aUser,
1739 const Optional<nsAString>& aPassword,
1740 ErrorResult& aRv) {
1741 mWorkerPrivate->AssertIsOnWorkerThread();
1743 MOZ_LOG(gXMLHttpRequestLog, LogLevel::Debug,
1744 ("%p Open(%s,%s,%d)", this, nsAutoCString(aMethod).get(),
1745 NS_ConvertUTF16toUTF8(aUrl).get(), aAsync));
1747 if (mCanceled) {
1748 aRv.ThrowUncatchableException();
1749 return;
1752 bool alsoOverrideMimeType = false;
1753 if (mProxy) {
1754 MaybeDispatchPrematureAbortEvents(aRv);
1755 if (aRv.Failed()) {
1756 return;
1758 } else {
1759 Maybe<ClientInfo> clientInfo(
1760 mWorkerPrivate->GlobalScope()->GetClientInfo());
1761 if (clientInfo.isNothing()) {
1762 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1763 return;
1765 mProxy = new Proxy(this, clientInfo.ref(),
1766 mWorkerPrivate->GlobalScope()->GetController(), mMozAnon,
1767 mMozSystem);
1768 alsoOverrideMimeType = true;
1771 mProxy->mOuterEventStreamId++;
1773 UniquePtr<SerializedStackHolder> stack;
1774 if (mWorkerPrivate->IsWatchedByDevTools()) {
1775 if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
1776 stack = GetCurrentStackForNetMonitor(cx);
1780 RefPtr<OpenRunnable> runnable = new OpenRunnable(
1781 mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
1782 mBackgroundRequest, mWithCredentials, mTimeout, mResponseType,
1783 alsoOverrideMimeType ? mMimeTypeOverride : VoidString(), std::move(stack),
1784 profiler_capture_backtrace());
1786 ++mProxy->mOpenCount;
1787 runnable->Dispatch(Canceling, aRv);
1788 if (aRv.Failed()) {
1789 if (mProxy && !--mProxy->mOpenCount) {
1790 ReleaseProxy();
1793 return;
1796 // We have been released in one of the nested Open() calls.
1797 if (!mProxy) {
1798 aRv.Throw(NS_ERROR_FAILURE);
1799 return;
1802 --mProxy->mOpenCount;
1803 mProxy->mIsSyncXHR = !aAsync;
1806 void XMLHttpRequestWorker::SetRequestHeader(const nsACString& aHeader,
1807 const nsACString& aValue,
1808 ErrorResult& aRv) {
1809 mWorkerPrivate->AssertIsOnWorkerThread();
1811 if (mCanceled) {
1812 aRv.ThrowUncatchableException();
1813 return;
1816 if (!mProxy) {
1817 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1818 return;
1821 RefPtr<SetRequestHeaderRunnable> runnable =
1822 new SetRequestHeaderRunnable(mWorkerPrivate, mProxy, aHeader, aValue);
1823 runnable->Dispatch(Canceling, aRv);
1826 void XMLHttpRequestWorker::SetTimeout(uint32_t aTimeout, ErrorResult& aRv) {
1827 mWorkerPrivate->AssertIsOnWorkerThread();
1829 if (mCanceled) {
1830 aRv.ThrowUncatchableException();
1831 return;
1834 mTimeout = aTimeout;
1836 if (!mProxy) {
1837 // Open might not have been called yet, in which case we'll handle the
1838 // timeout in OpenRunnable.
1839 return;
1842 RefPtr<SetTimeoutRunnable> runnable =
1843 new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout);
1844 runnable->Dispatch(Canceling, aRv);
1847 void XMLHttpRequestWorker::SetWithCredentials(bool aWithCredentials,
1848 ErrorResult& aRv) {
1849 mWorkerPrivate->AssertIsOnWorkerThread();
1851 if (mCanceled) {
1852 aRv.ThrowUncatchableException();
1853 return;
1856 mWithCredentials = aWithCredentials;
1858 if (!mProxy) {
1859 // Open might not have been called yet, in which case we'll handle the
1860 // credentials in OpenRunnable.
1861 return;
1864 RefPtr<SetWithCredentialsRunnable> runnable =
1865 new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials);
1866 runnable->Dispatch(Canceling, aRv);
1869 void XMLHttpRequestWorker::SetMozBackgroundRequest(bool aBackgroundRequest,
1870 ErrorResult& aRv) {
1871 mWorkerPrivate->AssertIsOnWorkerThread();
1873 if (mCanceled) {
1874 aRv.ThrowUncatchableException();
1875 return;
1878 mBackgroundRequest = aBackgroundRequest;
1880 if (!mProxy) {
1881 // Open might not have been called yet, in which case we'll handle the
1882 // background request in OpenRunnable.
1883 return;
1886 RefPtr<SetBackgroundRequestRunnable> runnable =
1887 new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy,
1888 aBackgroundRequest);
1889 runnable->Dispatch(Canceling, aRv);
1892 XMLHttpRequestUpload* XMLHttpRequestWorker::GetUpload(ErrorResult& aRv) {
1893 mWorkerPrivate->AssertIsOnWorkerThread();
1895 if (mCanceled) {
1896 aRv.ThrowUncatchableException();
1897 return nullptr;
1900 if (!mUpload) {
1901 mUpload = new XMLHttpRequestUpload(this);
1904 return mUpload;
1907 void XMLHttpRequestWorker::Send(
1908 const Nullable<
1909 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>&
1910 aData,
1911 ErrorResult& aRv) {
1912 mWorkerPrivate->AssertIsOnWorkerThread();
1914 if (mFlagSendActive) {
1915 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT);
1916 return;
1918 mFlagSendActive = true;
1919 auto clearRecursionFlag = MakeScopeExit([&]() {
1920 // No one else should have touched this flag.
1921 MOZ_ASSERT(mFlagSendActive);
1922 mFlagSendActive = false;
1925 if (mCanceled) {
1926 aRv.ThrowUncatchableException();
1927 return;
1930 if (mStateData->mReadyState != XMLHttpRequest_Binding::OPENED) {
1931 aRv.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
1932 return;
1935 if (!mProxy || !mProxy->mXMLHttpRequestPrivate || mStateData->mFlagSend) {
1936 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1937 return;
1940 if (aData.IsNull()) {
1941 SendInternal(nullptr, aRv);
1942 return;
1945 if (aData.Value().IsDocument()) {
1946 MOZ_ASSERT_UNREACHABLE("Documents are not exposed to workers.");
1947 aRv.Throw(NS_ERROR_FAILURE);
1948 return;
1951 if (aData.Value().IsBlob()) {
1952 BodyExtractor<const Blob> body(&aData.Value().GetAsBlob());
1953 SendInternal(&body, aRv);
1954 return;
1957 if (aData.Value().IsArrayBuffer()) {
1958 BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer());
1959 SendInternal(&body, aRv);
1960 return;
1963 if (aData.Value().IsArrayBufferView()) {
1964 BodyExtractor<const ArrayBufferView> body(
1965 &aData.Value().GetAsArrayBufferView());
1966 SendInternal(&body, aRv);
1967 return;
1970 if (aData.Value().IsFormData()) {
1971 BodyExtractor<const FormData> body(&aData.Value().GetAsFormData());
1972 SendInternal(&body, aRv);
1973 return;
1976 if (aData.Value().IsURLSearchParams()) {
1977 BodyExtractor<const URLSearchParams> body(
1978 &aData.Value().GetAsURLSearchParams());
1979 SendInternal(&body, aRv);
1980 return;
1983 if (aData.Value().IsUSVString()) {
1984 BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString());
1985 SendInternal(&body, aRv);
1986 return;
1990 void XMLHttpRequestWorker::Abort(ErrorResult& aRv) {
1991 mWorkerPrivate->AssertIsOnWorkerThread();
1993 if (mCanceled) {
1994 aRv.ThrowUncatchableException();
1995 return;
1998 if (!mProxy) {
1999 return;
2002 // Set our status to 0 and statusText to "" if we
2003 // will be aborting an ongoing fetch, so the upcoming
2004 // abort events we dispatch have the correct info.
2005 if ((mStateData->mReadyState == XMLHttpRequest_Binding::OPENED &&
2006 mStateData->mFlagSend) ||
2007 mStateData->mReadyState == XMLHttpRequest_Binding::HEADERS_RECEIVED ||
2008 mStateData->mReadyState == XMLHttpRequest_Binding::LOADING ||
2009 mStateData->mReadyState == XMLHttpRequest_Binding::DONE) {
2010 mStateData->mStatus = 0;
2011 mStateData->mStatusText.Truncate();
2014 MaybeDispatchPrematureAbortEvents(aRv);
2015 if (aRv.Failed()) {
2016 return;
2019 if (mStateData->mReadyState == 4) {
2020 // No one did anything to us while we fired abort events, so reset our state
2021 // to "unsent"
2022 mStateData->mReadyState = 0;
2025 mProxy->mOuterEventStreamId++;
2027 RefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
2028 runnable->Dispatch(Canceling, aRv);
2031 void XMLHttpRequestWorker::GetResponseHeader(const nsACString& aHeader,
2032 nsACString& aResponseHeader,
2033 ErrorResult& aRv) {
2034 mWorkerPrivate->AssertIsOnWorkerThread();
2036 if (mCanceled) {
2037 aRv.ThrowUncatchableException();
2038 return;
2041 if (!mProxy) {
2042 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2043 return;
2046 nsCString responseHeader;
2047 RefPtr<GetResponseHeaderRunnable> runnable = new GetResponseHeaderRunnable(
2048 mWorkerPrivate, mProxy, aHeader, responseHeader);
2049 runnable->Dispatch(Canceling, aRv);
2050 if (aRv.Failed()) {
2051 return;
2053 aResponseHeader = responseHeader;
2056 void XMLHttpRequestWorker::GetAllResponseHeaders(nsACString& aResponseHeaders,
2057 ErrorResult& aRv) {
2058 mWorkerPrivate->AssertIsOnWorkerThread();
2060 if (mCanceled) {
2061 aRv.ThrowUncatchableException();
2062 return;
2065 if (!mProxy) {
2066 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2067 return;
2070 nsCString responseHeaders;
2071 RefPtr<GetAllResponseHeadersRunnable> runnable =
2072 new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy,
2073 responseHeaders);
2074 runnable->Dispatch(Canceling, aRv);
2075 if (aRv.Failed()) {
2076 return;
2079 aResponseHeaders = responseHeaders;
2082 void XMLHttpRequestWorker::OverrideMimeType(const nsAString& aMimeType,
2083 ErrorResult& aRv) {
2084 mWorkerPrivate->AssertIsOnWorkerThread();
2086 if (mCanceled) {
2087 aRv.ThrowUncatchableException();
2088 return;
2091 // We're supposed to throw if the state is LOADING or DONE.
2092 if (mStateData->mReadyState == XMLHttpRequest_Binding::LOADING ||
2093 mStateData->mReadyState == XMLHttpRequest_Binding::DONE) {
2094 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2095 return;
2098 mMimeTypeOverride = aMimeType;
2100 if (mProxy) {
2101 RefPtr<OverrideMimeTypeRunnable> runnable =
2102 new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
2103 runnable->Dispatch(Canceling, aRv);
2107 void XMLHttpRequestWorker::SetResponseType(
2108 XMLHttpRequestResponseType aResponseType, ErrorResult& aRv) {
2109 mWorkerPrivate->AssertIsOnWorkerThread();
2111 // "document" is fine for the main thread but not for a worker. Short-circuit
2112 // that here.
2113 if (aResponseType == XMLHttpRequestResponseType::Document) {
2114 return;
2117 if (!mProxy) {
2118 // Open() has not been called yet. We store the responseType and we will use
2119 // it later in Open().
2120 mResponseType = aResponseType;
2121 return;
2124 if (mStateData->mReadyState == XMLHttpRequest_Binding::LOADING ||
2125 mStateData->mReadyState == XMLHttpRequest_Binding::DONE) {
2126 aRv.ThrowInvalidStateError(
2127 "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
2128 "(when its state is LOADING or DONE).");
2129 return;
2132 RefPtr<SetResponseTypeRunnable> runnable =
2133 new SetResponseTypeRunnable(mWorkerPrivate, mProxy, aResponseType);
2134 runnable->Dispatch(Canceling, aRv);
2135 if (aRv.Failed()) {
2136 return;
2139 mResponseType = runnable->ResponseType();
2142 void XMLHttpRequestWorker::GetResponse(JSContext* aCx,
2143 JS::MutableHandle<JS::Value> aResponse,
2144 ErrorResult& aRv) {
2145 if (NS_FAILED(mResponseData->mResponseResult)) {
2146 aRv.Throw(mResponseData->mResponseResult);
2147 return;
2150 switch (mResponseType) {
2151 case XMLHttpRequestResponseType::_empty:
2152 case XMLHttpRequestResponseType::Text: {
2153 JSString* str;
2155 if (mResponseData->mResponseText.IsEmpty()) {
2156 aResponse.set(JS_GetEmptyStringValue(aCx));
2157 return;
2160 str = mResponseData->mResponseText.GetAsJSStringCopy(aCx);
2161 if (!str) {
2162 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2163 return;
2166 aResponse.setString(str);
2167 return;
2170 case XMLHttpRequestResponseType::Arraybuffer: {
2171 if (!mResponseData->mResponseArrayBufferBuilder) {
2172 aResponse.setNull();
2173 return;
2176 if (!mResponseArrayBufferValue) {
2177 mResponseArrayBufferValue =
2178 mResponseData->mResponseArrayBufferBuilder->TakeArrayBuffer(aCx);
2179 if (!mResponseArrayBufferValue) {
2180 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2181 return;
2185 aResponse.setObject(*mResponseArrayBufferValue);
2186 return;
2189 case XMLHttpRequestResponseType::Blob: {
2190 if (!mResponseData->mResponseBlobImpl) {
2191 aResponse.setNull();
2192 return;
2195 if (!mResponseBlob) {
2196 mResponseBlob =
2197 Blob::Create(GetOwnerGlobal(), mResponseData->mResponseBlobImpl);
2200 if (!mResponseBlob ||
2201 !GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse)) {
2202 aResponse.setNull();
2205 return;
2208 case XMLHttpRequestResponseType::Json: {
2209 if (mResponseData->mResponseJSON.IsVoid()) {
2210 aResponse.setNull();
2211 return;
2214 if (mResponseJSONValue.isUndefined()) {
2215 // The Unicode converter has already zapped the BOM if there was one
2216 JS::Rooted<JS::Value> value(aCx);
2217 if (!JS_ParseJSON(aCx, mResponseData->mResponseJSON.BeginReading(),
2218 mResponseData->mResponseJSON.Length(), &value)) {
2219 JS_ClearPendingException(aCx);
2220 mResponseJSONValue.setNull();
2221 } else {
2222 mResponseJSONValue = value;
2225 mResponseData->mResponseJSON.Truncate();
2228 aResponse.set(mResponseJSONValue);
2229 return;
2232 default:
2233 MOZ_ASSERT_UNREACHABLE("Invalid type");
2234 aResponse.setNull();
2235 return;
2239 void XMLHttpRequestWorker::GetResponseText(DOMString& aResponseText,
2240 ErrorResult& aRv) {
2241 MOZ_DIAGNOSTIC_ASSERT(mResponseData);
2243 if (mResponseType != XMLHttpRequestResponseType::_empty &&
2244 mResponseType != XMLHttpRequestResponseType::Text) {
2245 aRv.ThrowInvalidStateError(
2246 "responseText is only available if responseType is '' or 'text'.");
2247 return;
2250 if (!mResponseData->mResponseText.GetAsString(aResponseText)) {
2251 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2252 return;
2256 void XMLHttpRequestWorker::UpdateState(
2257 UniquePtr<StateData>&& aStateData,
2258 UniquePtr<ResponseData>&& aResponseData) {
2259 mStateData = std::move(aStateData);
2261 UniquePtr<ResponseData> responseData = std::move(aResponseData);
2262 if (responseData) {
2263 ResetResponseData();
2264 mResponseData = std::move(responseData);
2267 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
2270 void XMLHttpRequestWorker::ResetResponseData() {
2271 mResponseBlob = nullptr;
2272 mResponseArrayBufferValue = nullptr;
2273 mResponseJSONValue.setUndefined();
2276 } // namespace mozilla::dom