Bug 1719855 - Take over preventDefaulted infomation for long-tap events to the origin...
[gecko.git] / dom / xhr / XMLHttpRequestWorker.cpp
blob5ad96c5f27bba7522c9ba525285732f5f2705aed
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 "jsapi.h" // JS::RootedValueArray
13 #include "jsfriendapi.h"
14 #include "js/ArrayBuffer.h" // JS::Is{,Detached}ArrayBufferObject
15 #include "js/GCPolicyAPI.h"
16 #include "js/JSON.h"
17 #include "js/RootingAPI.h" // JS::{Handle,Heap},PersistentRooted
18 #include "js/TracingAPI.h"
19 #include "js/Value.h" // JS::{Undefined,}Value
20 #include "mozilla/ArrayUtils.h"
21 #include "mozilla/HoldDropJSObjects.h"
22 #include "mozilla/dom/Exceptions.h"
23 #include "mozilla/dom/Event.h"
24 #include "mozilla/dom/File.h"
25 #include "mozilla/dom/FormData.h"
26 #include "mozilla/dom/ProgressEvent.h"
27 #include "mozilla/dom/SerializedStackHolder.h"
28 #include "mozilla/dom/StreamBlobImpl.h"
29 #include "mozilla/dom/StructuredCloneHolder.h"
30 #include "mozilla/dom/URLSearchParams.h"
31 #include "mozilla/dom/WorkerScope.h"
32 #include "mozilla/dom/WorkerRef.h"
33 #include "mozilla/dom/WorkerRunnable.h"
34 #include "mozilla/dom/XMLHttpRequestBinding.h"
35 #include "mozilla/Telemetry.h"
36 #include "nsComponentManagerUtils.h"
37 #include "nsContentUtils.h"
38 #include "nsJSUtils.h"
39 #include "nsThreadUtils.h"
41 #include "XMLHttpRequestMainThread.h"
42 #include "XMLHttpRequestUpload.h"
44 #include "mozilla/UniquePtr.h"
46 namespace mozilla::dom {
48 /**
49 * XMLHttpRequest in workers
51 * XHR in workers is implemented by proxying calls/events/etc between the
52 * worker thread and an XMLHttpRequest on the main thread. The glue
53 * object here is the Proxy, which lives on both threads. All other objects
54 * live on either the main thread (the XMLHttpRequest) or the worker thread
55 * (the worker and XHR private objects).
57 * The main thread XHR is always operated in async mode, even for sync XHR
58 * in workers. Calls made on the worker thread are proxied to the main thread
59 * synchronously (meaning the worker thread is blocked until the call
60 * returns). Each proxied call spins up a sync queue, which captures any
61 * synchronously dispatched events and ensures that they run synchronously
62 * on the worker as well. Asynchronously dispatched events are posted to the
63 * worker thread to run asynchronously. Some of the XHR state is mirrored on
64 * the worker thread to avoid needing a cross-thread call on every property
65 * access.
67 * The XHR private is stored in the private slot of the XHR JSObject on the
68 * worker thread. It is destroyed when that JSObject is GCd. The private
69 * roots its JSObject while network activity is in progress. It also
70 * adds itself as a feature to the worker to give itself a chance to clean up
71 * if the worker goes away during an XHR call. It is important that the
72 * rooting and feature registration (collectively called pinning) happens at
73 * the proper times. If we pin for too long we can cause memory leaks or even
74 * shutdown hangs. If we don't pin for long enough we introduce a GC hazard.
76 * The XHR is pinned from the time Send is called to roughly the time loadend
77 * is received. There are some complications involved with Abort and XHR
78 * reuse. We maintain a counter on the main thread of how many times Send was
79 * called on this XHR, and we decrement the counter every time we receive a
80 * loadend event. When the counter reaches zero we dispatch a runnable to the
81 * worker thread to unpin the XHR. We only decrement the counter if the
82 * dispatch was successful, because the worker may no longer be accepting
83 * regular runnables. In the event that we reach Proxy::Teardown and there
84 * the outstanding Send count is still non-zero, we dispatch a control
85 * runnable which is guaranteed to run.
87 * NB: Some of this could probably be simplified now that we have the
88 * inner/outer channel ids.
91 class Proxy final : public nsIDOMEventListener {
92 public:
93 // Read on multiple threads.
94 WorkerPrivate* mWorkerPrivate;
95 XMLHttpRequestWorker* mXMLHttpRequestPrivate;
96 const ClientInfo mClientInfo;
97 const Maybe<ServiceWorkerDescriptor> mController;
99 // XHR Params:
100 bool mMozAnon;
101 bool mMozSystem;
103 // Only touched on the main thread.
104 RefPtr<XMLHttpRequestMainThread> mXHR;
105 RefPtr<XMLHttpRequestUpload> mXHRUpload;
106 nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
107 nsCOMPtr<nsIEventTarget> mSyncEventResponseTarget;
108 uint32_t mInnerEventStreamId;
109 uint32_t mInnerChannelId;
110 uint32_t mOutstandingSendCount;
112 // Only touched on the worker thread.
113 uint32_t mOuterEventStreamId;
114 uint32_t mOuterChannelId;
115 uint32_t mOpenCount;
116 uint64_t mLastLoaded;
117 uint64_t mLastTotal;
118 uint64_t mLastUploadLoaded;
119 uint64_t mLastUploadTotal;
120 bool mIsSyncXHR;
121 bool mLastLengthComputable;
122 bool mLastUploadLengthComputable;
123 bool mSeenLoadStart;
124 bool mSeenUploadLoadStart;
125 bool mDispatchPrematureAbortEvent;
126 bool mDispatchPrematureAbortEventToUpload;
128 // Only touched on the main thread.
129 bool mUploadEventListenersAttached;
130 bool mMainThreadSeenLoadStart;
131 bool mInOpen;
133 public:
134 Proxy(XMLHttpRequestWorker* aXHRPrivate, const ClientInfo& aClientInfo,
135 const Maybe<ServiceWorkerDescriptor>& aController, bool aMozAnon,
136 bool aMozSystem)
137 : mWorkerPrivate(nullptr),
138 mXMLHttpRequestPrivate(aXHRPrivate),
139 mClientInfo(aClientInfo),
140 mController(aController),
141 mMozAnon(aMozAnon),
142 mMozSystem(aMozSystem),
143 mInnerEventStreamId(0),
144 mInnerChannelId(0),
145 mOutstandingSendCount(0),
146 mOuterEventStreamId(0),
147 mOuterChannelId(0),
148 mOpenCount(0),
149 mLastLoaded(0),
150 mLastTotal(0),
151 mLastUploadLoaded(0),
152 mLastUploadTotal(0),
153 mIsSyncXHR(false),
154 mLastLengthComputable(false),
155 mLastUploadLengthComputable(false),
156 mSeenLoadStart(false),
157 mSeenUploadLoadStart(false),
158 mDispatchPrematureAbortEvent(false),
159 mDispatchPrematureAbortEventToUpload(false),
160 mUploadEventListenersAttached(false),
161 mMainThreadSeenLoadStart(false),
162 mInOpen(false) {}
164 NS_DECL_THREADSAFE_ISUPPORTS
165 NS_DECL_NSIDOMEVENTLISTENER
167 bool Init();
169 void Teardown(bool aSendUnpin);
171 bool AddRemoveEventListeners(bool aUpload, bool aAdd);
173 void Reset() {
174 AssertIsOnMainThread();
176 if (mUploadEventListenersAttached) {
177 AddRemoveEventListeners(true, false);
181 already_AddRefed<nsIEventTarget> GetEventTarget() {
182 AssertIsOnMainThread();
184 nsCOMPtr<nsIEventTarget> target =
185 mSyncEventResponseTarget ? mSyncEventResponseTarget : mSyncLoopTarget;
186 return target.forget();
189 private:
190 ~Proxy() {
191 MOZ_ASSERT(!mXHR);
192 MOZ_ASSERT(!mXHRUpload);
193 MOZ_ASSERT(!mOutstandingSendCount);
197 class WorkerThreadProxySyncRunnable : public WorkerMainThreadRunnable {
198 protected:
199 RefPtr<Proxy> mProxy;
201 private:
202 // mErrorCode is set on the main thread by MainThreadRun and it's used to at
203 // the end of the Dispatch() to return the error code.
204 nsresult mErrorCode;
206 public:
207 WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
208 : WorkerMainThreadRunnable(aWorkerPrivate, "XHR"_ns),
209 mProxy(aProxy),
210 mErrorCode(NS_OK) {
211 MOZ_ASSERT(aWorkerPrivate);
212 MOZ_ASSERT(aProxy);
213 aWorkerPrivate->AssertIsOnWorkerThread();
216 void Dispatch(WorkerStatus aFailStatus, ErrorResult& aRv) {
217 WorkerMainThreadRunnable::Dispatch(aFailStatus, aRv);
218 if (NS_WARN_IF(aRv.Failed())) {
219 return;
222 if (NS_FAILED(mErrorCode)) {
223 aRv.Throw(mErrorCode);
227 protected:
228 virtual ~WorkerThreadProxySyncRunnable() = default;
230 virtual void RunOnMainThread(ErrorResult& aRv) = 0;
232 private:
233 virtual bool MainThreadRun() override;
236 class SendRunnable final : public WorkerThreadProxySyncRunnable {
237 RefPtr<BlobImpl> mBlobImpl;
238 nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
239 bool mHasUploadListeners;
241 public:
242 SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
243 BlobImpl* aBlobImpl)
244 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
245 mBlobImpl(aBlobImpl),
246 mHasUploadListeners(false) {}
248 void SetHaveUploadListeners(bool aHasUploadListeners) {
249 mHasUploadListeners = aHasUploadListeners;
252 void SetSyncLoopTarget(nsIEventTarget* aSyncLoopTarget) {
253 mSyncLoopTarget = aSyncLoopTarget;
256 private:
257 ~SendRunnable() = default;
259 virtual void RunOnMainThread(ErrorResult& aRv) override;
262 namespace {
264 enum {
265 STRING_abort = 0,
266 STRING_error,
267 STRING_load,
268 STRING_loadstart,
269 STRING_progress,
270 STRING_timeout,
271 STRING_loadend,
272 STRING_readystatechange,
274 STRING_COUNT,
276 STRING_LAST_XHR = STRING_readystatechange,
277 STRING_LAST_EVENTTARGET = STRING_loadend
280 static_assert(STRING_LAST_XHR >= STRING_LAST_EVENTTARGET, "Bad string setup!");
281 static_assert(STRING_LAST_XHR == STRING_COUNT - 1, "Bad string setup!");
283 const char* const sEventStrings[] = {
284 // XMLHttpRequestEventTarget event types, supported by both XHR and Upload.
285 "abort",
286 "error",
287 "load",
288 "loadstart",
289 "progress",
290 "timeout",
291 "loadend",
293 // XMLHttpRequest event types, supported only by XHR.
294 "readystatechange",
297 static_assert(MOZ_ARRAY_LENGTH(sEventStrings) == STRING_COUNT,
298 "Bad string count!");
300 class MainThreadProxyRunnable : public MainThreadWorkerSyncRunnable {
301 protected:
302 RefPtr<Proxy> mProxy;
304 MainThreadProxyRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
305 : MainThreadWorkerSyncRunnable(aWorkerPrivate, aProxy->GetEventTarget()),
306 mProxy(aProxy) {
307 MOZ_ASSERT(aProxy);
310 virtual ~MainThreadProxyRunnable() = default;
313 class XHRUnpinRunnable final : public MainThreadWorkerControlRunnable {
314 XMLHttpRequestWorker* mXMLHttpRequestPrivate;
316 public:
317 XHRUnpinRunnable(WorkerPrivate* aWorkerPrivate,
318 XMLHttpRequestWorker* aXHRPrivate)
319 : MainThreadWorkerControlRunnable(aWorkerPrivate),
320 mXMLHttpRequestPrivate(aXHRPrivate) {
321 MOZ_ASSERT(aXHRPrivate);
324 private:
325 ~XHRUnpinRunnable() = default;
327 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
328 if (mXMLHttpRequestPrivate->SendInProgress()) {
329 mXMLHttpRequestPrivate->Unpin();
332 return true;
336 class AsyncTeardownRunnable final : public Runnable {
337 RefPtr<Proxy> mProxy;
339 public:
340 explicit AsyncTeardownRunnable(Proxy* aProxy)
341 : Runnable("dom::AsyncTeardownRunnable"), mProxy(aProxy) {
342 MOZ_ASSERT(aProxy);
345 private:
346 ~AsyncTeardownRunnable() = default;
348 NS_IMETHOD
349 Run() override {
350 AssertIsOnMainThread();
352 // This means the XHR was GC'd, so we can't be pinned, and we don't need to
353 // try to unpin.
354 mProxy->Teardown(/* aSendUnpin */ false);
355 mProxy = nullptr;
357 return NS_OK;
361 class LoadStartDetectionRunnable final : public Runnable,
362 public nsIDOMEventListener {
363 WorkerPrivate* mWorkerPrivate;
364 RefPtr<Proxy> mProxy;
365 RefPtr<XMLHttpRequest> mXHR;
366 XMLHttpRequestWorker* mXMLHttpRequestPrivate;
367 nsString mEventType;
368 uint32_t mChannelId;
369 bool mReceivedLoadStart;
371 class ProxyCompleteRunnable final : public MainThreadProxyRunnable {
372 XMLHttpRequestWorker* mXMLHttpRequestPrivate;
373 uint32_t mChannelId;
375 public:
376 ProxyCompleteRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
377 XMLHttpRequestWorker* aXHRPrivate,
378 uint32_t aChannelId)
379 : MainThreadProxyRunnable(aWorkerPrivate, aProxy),
380 mXMLHttpRequestPrivate(aXHRPrivate),
381 mChannelId(aChannelId) {}
383 private:
384 ~ProxyCompleteRunnable() = default;
386 virtual bool WorkerRun(JSContext* aCx,
387 WorkerPrivate* aWorkerPrivate) override {
388 if (mChannelId != mProxy->mOuterChannelId) {
389 // Threads raced, this event is now obsolete.
390 return true;
393 if (mSyncLoopTarget) {
394 aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, NS_OK);
397 if (mXMLHttpRequestPrivate->SendInProgress()) {
398 mXMLHttpRequestPrivate->Unpin();
401 return true;
404 nsresult Cancel() override { return Run(); }
407 public:
408 LoadStartDetectionRunnable(Proxy* aProxy, XMLHttpRequestWorker* aXHRPrivate)
409 : Runnable("dom::LoadStartDetectionRunnable"),
410 mWorkerPrivate(aProxy->mWorkerPrivate),
411 mProxy(aProxy),
412 mXHR(aProxy->mXHR),
413 mXMLHttpRequestPrivate(aXHRPrivate),
414 mChannelId(mProxy->mInnerChannelId),
415 mReceivedLoadStart(false) {
416 AssertIsOnMainThread();
417 mEventType.AssignASCII(sEventStrings[STRING_loadstart]);
420 NS_DECL_ISUPPORTS_INHERITED
421 NS_DECL_NSIRUNNABLE
422 NS_DECL_NSIDOMEVENTLISTENER
424 bool RegisterAndDispatch() {
425 AssertIsOnMainThread();
427 if (NS_FAILED(mXHR->AddEventListener(mEventType, this, false, false))) {
428 NS_WARNING("Failed to add event listener!");
429 return false;
432 return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(this));
435 private:
436 ~LoadStartDetectionRunnable() { AssertIsOnMainThread(); }
439 class EventRunnable final : public MainThreadProxyRunnable {
440 nsString mType;
441 UniquePtr<XMLHttpRequestWorker::ResponseData> mResponseData;
442 nsString mResponseURL;
443 nsCString mStatusText;
444 uint64_t mLoaded;
445 uint64_t mTotal;
446 uint32_t mEventStreamId;
447 uint32_t mStatus;
448 uint16_t mReadyState;
449 bool mUploadEvent;
450 bool mProgressEvent;
451 bool mLengthComputable;
452 nsresult mStatusResult;
453 // mScopeObj is used in PreDispatch only. We init it in our constructor, and
454 // reset() in PreDispatch, to ensure that it's not still linked into the
455 // runtime once we go off-thread.
456 JS::PersistentRooted<JSObject*> mScopeObj;
458 public:
459 EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
460 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal,
461 JS::Handle<JSObject*> aScopeObj)
462 : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
463 mType(aType),
464 mResponseData(new XMLHttpRequestWorker::ResponseData()),
465 mLoaded(aLoaded),
466 mTotal(aTotal),
467 mEventStreamId(aProxy->mInnerEventStreamId),
468 mStatus(0),
469 mReadyState(0),
470 mUploadEvent(aUploadEvent),
471 mProgressEvent(true),
472 mLengthComputable(aLengthComputable),
473 mStatusResult(NS_OK),
474 mScopeObj(RootingCx(), aScopeObj) {}
476 EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
477 JS::Handle<JSObject*> aScopeObj)
478 : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy),
479 mType(aType),
480 mResponseData(new XMLHttpRequestWorker::ResponseData()),
481 mLoaded(0),
482 mTotal(0),
483 mEventStreamId(aProxy->mInnerEventStreamId),
484 mStatus(0),
485 mReadyState(0),
486 mUploadEvent(aUploadEvent),
487 mProgressEvent(false),
488 mLengthComputable(0),
489 mStatusResult(NS_OK),
490 mScopeObj(RootingCx(), aScopeObj) {}
492 private:
493 ~EventRunnable() = default;
495 bool PreDispatch(WorkerPrivate* /* unused */) final;
496 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
499 class SyncTeardownRunnable final : public WorkerThreadProxySyncRunnable {
500 public:
501 SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
502 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy) {}
504 private:
505 ~SyncTeardownRunnable() = default;
507 virtual void RunOnMainThread(ErrorResult& aRv) override {
508 mProxy->Teardown(/* aSendUnpin */ true);
509 MOZ_ASSERT(!mProxy->mSyncLoopTarget);
513 class SetBackgroundRequestRunnable final
514 : public WorkerThreadProxySyncRunnable {
515 bool mValue;
517 public:
518 SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
519 bool aValue)
520 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue) {}
522 private:
523 ~SetBackgroundRequestRunnable() = default;
525 virtual void RunOnMainThread(ErrorResult& aRv) override {
526 // XXXedgar, do we intend to ignore the errors?
527 mProxy->mXHR->SetMozBackgroundRequest(mValue, aRv);
531 class SetWithCredentialsRunnable final : public WorkerThreadProxySyncRunnable {
532 bool mValue;
534 public:
535 SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
536 bool aValue)
537 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue) {}
539 private:
540 ~SetWithCredentialsRunnable() = default;
542 virtual void RunOnMainThread(ErrorResult& aRv) override {
543 mProxy->mXHR->SetWithCredentials(mValue, aRv);
547 class SetResponseTypeRunnable final : public WorkerThreadProxySyncRunnable {
548 XMLHttpRequestResponseType mResponseType;
550 public:
551 SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
552 XMLHttpRequestResponseType aResponseType)
553 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
554 mResponseType(aResponseType) {}
556 XMLHttpRequestResponseType ResponseType() { return mResponseType; }
558 private:
559 ~SetResponseTypeRunnable() = default;
561 virtual void RunOnMainThread(ErrorResult& aRv) override {
562 mProxy->mXHR->SetResponseTypeRaw(mResponseType);
563 mResponseType = mProxy->mXHR->ResponseType();
567 class SetTimeoutRunnable final : public WorkerThreadProxySyncRunnable {
568 uint32_t mTimeout;
570 public:
571 SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
572 uint32_t aTimeout)
573 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
574 mTimeout(aTimeout) {}
576 private:
577 ~SetTimeoutRunnable() = default;
579 virtual void RunOnMainThread(ErrorResult& aRv) override {
580 mProxy->mXHR->SetTimeout(mTimeout, aRv);
584 class AbortRunnable final : public WorkerThreadProxySyncRunnable {
585 public:
586 AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
587 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy) {}
589 private:
590 ~AbortRunnable() = default;
592 virtual void RunOnMainThread(ErrorResult& aRv) override;
595 class GetAllResponseHeadersRunnable final
596 : public WorkerThreadProxySyncRunnable {
597 nsCString& mResponseHeaders;
599 public:
600 GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
601 nsCString& aResponseHeaders)
602 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
603 mResponseHeaders(aResponseHeaders) {}
605 private:
606 ~GetAllResponseHeadersRunnable() = default;
608 virtual void RunOnMainThread(ErrorResult& aRv) override {
609 mProxy->mXHR->GetAllResponseHeaders(mResponseHeaders, aRv);
613 class GetResponseHeaderRunnable final : public WorkerThreadProxySyncRunnable {
614 const nsCString mHeader;
615 nsCString& mValue;
617 public:
618 GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
619 const nsACString& aHeader, nsCString& aValue)
620 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
621 mHeader(aHeader),
622 mValue(aValue) {}
624 private:
625 ~GetResponseHeaderRunnable() = default;
627 virtual void RunOnMainThread(ErrorResult& aRv) override {
628 mProxy->mXHR->GetResponseHeader(mHeader, mValue, aRv);
632 class OpenRunnable final : public WorkerThreadProxySyncRunnable {
633 nsCString mMethod;
634 nsString mURL;
635 Optional<nsAString> mUser;
636 nsString mUserStr;
637 Optional<nsAString> mPassword;
638 nsString mPasswordStr;
639 bool mBackgroundRequest;
640 bool mWithCredentials;
641 uint32_t mTimeout;
642 XMLHttpRequestResponseType mResponseType;
643 const nsString mMimeTypeOverride;
645 // Remember the worker thread's stack when the XHR was opened, so that it can
646 // be passed on to the net monitor.
647 UniquePtr<SerializedStackHolder> mOriginStack;
649 // Remember the worker thread's stack when the XHR was opened for profiling
650 // purposes.
651 UniquePtr<ProfileChunkedBuffer> mSource;
653 public:
654 OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
655 const nsACString& aMethod, const nsAString& aURL,
656 const Optional<nsAString>& aUser,
657 const Optional<nsAString>& aPassword, bool aBackgroundRequest,
658 bool aWithCredentials, uint32_t aTimeout,
659 XMLHttpRequestResponseType aResponseType,
660 const nsString& aMimeTypeOverride,
661 UniquePtr<SerializedStackHolder> aOriginStack,
662 UniquePtr<ProfileChunkedBuffer> aSource = nullptr)
663 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
664 mMethod(aMethod),
665 mURL(aURL),
666 mBackgroundRequest(aBackgroundRequest),
667 mWithCredentials(aWithCredentials),
668 mTimeout(aTimeout),
669 mResponseType(aResponseType),
670 mMimeTypeOverride(aMimeTypeOverride),
671 mOriginStack(std::move(aOriginStack)),
672 mSource(std::move(aSource)) {
673 if (aUser.WasPassed()) {
674 mUserStr = aUser.Value();
675 mUser = &mUserStr;
677 if (aPassword.WasPassed()) {
678 mPasswordStr = aPassword.Value();
679 mPassword = &mPasswordStr;
683 private:
684 ~OpenRunnable() = default;
686 virtual void RunOnMainThread(ErrorResult& aRv) override {
687 WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
688 mProxy->mWorkerPrivate = mWorkerPrivate;
690 MainThreadRunInternal(aRv);
692 mProxy->mWorkerPrivate = oldWorker;
695 void MainThreadRunInternal(ErrorResult& aRv);
698 class SetRequestHeaderRunnable final : public WorkerThreadProxySyncRunnable {
699 nsCString mHeader;
700 nsCString mValue;
702 public:
703 SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
704 const nsACString& aHeader, const nsACString& aValue)
705 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
706 mHeader(aHeader),
707 mValue(aValue) {}
709 private:
710 ~SetRequestHeaderRunnable() = default;
712 virtual void RunOnMainThread(ErrorResult& aRv) override {
713 mProxy->mXHR->SetRequestHeader(mHeader, mValue, aRv);
717 class OverrideMimeTypeRunnable final : public WorkerThreadProxySyncRunnable {
718 nsString mMimeType;
720 public:
721 OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
722 const nsAString& aMimeType)
723 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
724 mMimeType(aMimeType) {}
726 private:
727 ~OverrideMimeTypeRunnable() = default;
729 virtual void RunOnMainThread(ErrorResult& aRv) override {
730 mProxy->mXHR->OverrideMimeType(mMimeType, aRv);
734 class AutoUnpinXHR {
735 XMLHttpRequestWorker* mXMLHttpRequestPrivate;
737 public:
738 explicit AutoUnpinXHR(XMLHttpRequestWorker* aXMLHttpRequestPrivate)
739 : mXMLHttpRequestPrivate(aXMLHttpRequestPrivate) {
740 MOZ_ASSERT(aXMLHttpRequestPrivate);
743 ~AutoUnpinXHR() {
744 if (mXMLHttpRequestPrivate) {
745 mXMLHttpRequestPrivate->Unpin();
749 void Clear() { mXMLHttpRequestPrivate = nullptr; }
752 } // namespace
754 bool Proxy::Init() {
755 AssertIsOnMainThread();
756 MOZ_ASSERT(mWorkerPrivate);
758 if (mXHR) {
759 return true;
762 nsPIDOMWindowInner* ownerWindow = mWorkerPrivate->GetWindow();
763 if (ownerWindow && !ownerWindow->IsCurrentInnerWindow()) {
764 NS_WARNING("Window has navigated, cannot create XHR here.");
765 return false;
768 mXHR = new XMLHttpRequestMainThread(ownerWindow ? ownerWindow->AsGlobal()
769 : nullptr);
770 mXHR->Construct(mWorkerPrivate->GetPrincipal(),
771 mWorkerPrivate->CookieJarSettings(), true,
772 mWorkerPrivate->GetBaseURI(), mWorkerPrivate->GetLoadGroup(),
773 mWorkerPrivate->GetPerformanceStorage(),
774 mWorkerPrivate->CSPEventListener());
776 mXHR->SetParameters(mMozAnon, mMozSystem);
777 mXHR->SetClientInfoAndController(mClientInfo, mController);
779 ErrorResult rv;
780 mXHRUpload = mXHR->GetUpload(rv);
781 if (NS_WARN_IF(rv.Failed())) {
782 mXHR = nullptr;
783 return false;
786 if (!AddRemoveEventListeners(false, true)) {
787 mXHR = nullptr;
788 mXHRUpload = nullptr;
789 return false;
792 return true;
795 void Proxy::Teardown(bool aSendUnpin) {
796 AssertIsOnMainThread();
798 if (mXHR) {
799 Reset();
801 // NB: We are intentionally dropping events coming from xhr.abort on the
802 // floor.
803 AddRemoveEventListeners(false, false);
805 ErrorResult rv;
806 mXHR->Abort(rv);
807 if (NS_WARN_IF(rv.Failed())) {
808 rv.SuppressException();
811 if (mOutstandingSendCount) {
812 if (aSendUnpin) {
813 RefPtr<XHRUnpinRunnable> runnable =
814 new XHRUnpinRunnable(mWorkerPrivate, mXMLHttpRequestPrivate);
815 MOZ_ALWAYS_TRUE(runnable->Dispatch());
818 if (mSyncLoopTarget) {
819 // We have an unclosed sync loop. Fix that now.
820 RefPtr<MainThreadStopSyncLoopRunnable> runnable =
821 new MainThreadStopSyncLoopRunnable(
822 mWorkerPrivate, std::move(mSyncLoopTarget), NS_ERROR_FAILURE);
823 MOZ_ALWAYS_TRUE(runnable->Dispatch());
826 mOutstandingSendCount = 0;
829 mWorkerPrivate = nullptr;
830 mXHRUpload = nullptr;
831 mXHR = nullptr;
834 MOZ_ASSERT(!mWorkerPrivate);
835 MOZ_ASSERT(!mSyncLoopTarget);
836 // If there are rare edge cases left that violate our invariants
837 // just ensure that they won't harm us too much.
838 mWorkerPrivate = nullptr;
839 mSyncLoopTarget = nullptr;
842 bool Proxy::AddRemoveEventListeners(bool aUpload, bool aAdd) {
843 AssertIsOnMainThread();
845 NS_ASSERTION(!aUpload || (mUploadEventListenersAttached && !aAdd) ||
846 (!mUploadEventListenersAttached && aAdd),
847 "Messed up logic for upload listeners!");
849 RefPtr<DOMEventTargetHelper> targetHelper =
850 aUpload ? static_cast<XMLHttpRequestUpload*>(mXHRUpload.get())
851 : static_cast<XMLHttpRequestEventTarget*>(mXHR.get());
852 MOZ_ASSERT(targetHelper, "This should never fail!");
854 uint32_t lastEventType = aUpload ? STRING_LAST_EVENTTARGET : STRING_LAST_XHR;
856 nsAutoString eventType;
857 for (uint32_t index = 0; index <= lastEventType; index++) {
858 eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
859 if (aAdd) {
860 if (NS_FAILED(targetHelper->AddEventListener(eventType, this, false))) {
861 return false;
863 } else {
864 targetHelper->RemoveEventListener(eventType, this, false);
868 if (aUpload) {
869 mUploadEventListenersAttached = aAdd;
872 return true;
875 NS_IMPL_ISUPPORTS(Proxy, nsIDOMEventListener)
877 NS_IMETHODIMP
878 Proxy::HandleEvent(Event* aEvent) {
879 AssertIsOnMainThread();
881 if (!mWorkerPrivate || !mXMLHttpRequestPrivate) {
882 NS_ERROR("Shouldn't get here!");
883 return NS_OK;
886 nsAutoString type;
887 aEvent->GetType(type);
889 bool isUploadTarget = mXHR != aEvent->GetTarget();
890 ProgressEvent* progressEvent = aEvent->AsProgressEvent();
892 if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
893 if (mXHR->ReadyState() == 1) {
894 mInnerEventStreamId++;
899 AutoJSAPI jsapi;
900 JSObject* junkScope = xpc::UnprivilegedJunkScope(fallible);
901 if (!junkScope || !jsapi.Init(junkScope)) {
902 return NS_ERROR_FAILURE;
904 JSContext* cx = jsapi.cx();
906 JS::Rooted<JS::Value> value(cx);
907 if (!GetOrCreateDOMReflectorNoWrap(cx, mXHR, &value)) {
908 return NS_ERROR_FAILURE;
911 JS::Rooted<JSObject*> scope(cx, &value.toObject());
913 RefPtr<EventRunnable> runnable;
914 if (progressEvent) {
915 if (!mIsSyncXHR || !type.EqualsASCII(sEventStrings[STRING_progress])) {
916 runnable = new EventRunnable(
917 this, isUploadTarget, type, progressEvent->LengthComputable(),
918 progressEvent->Loaded(), progressEvent->Total(), scope);
920 } else {
921 runnable = new EventRunnable(this, isUploadTarget, type, scope);
924 if (runnable) {
925 runnable->Dispatch();
929 if (!isUploadTarget) {
930 if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
931 mMainThreadSeenLoadStart = true;
932 } else if (mMainThreadSeenLoadStart &&
933 type.EqualsASCII(sEventStrings[STRING_loadend])) {
934 mMainThreadSeenLoadStart = false;
936 RefPtr<LoadStartDetectionRunnable> runnable =
937 new LoadStartDetectionRunnable(this, mXMLHttpRequestPrivate);
938 if (!runnable->RegisterAndDispatch()) {
939 NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
944 return NS_OK;
947 NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable, Runnable,
948 nsIDOMEventListener)
950 NS_IMETHODIMP
951 LoadStartDetectionRunnable::Run() {
952 AssertIsOnMainThread();
954 mXHR->RemoveEventListener(mEventType, this, false);
956 if (!mReceivedLoadStart) {
957 if (mProxy->mOutstandingSendCount > 1) {
958 mProxy->mOutstandingSendCount--;
959 } else if (mProxy->mOutstandingSendCount == 1) {
960 mProxy->Reset();
962 RefPtr<ProxyCompleteRunnable> runnable = new ProxyCompleteRunnable(
963 mWorkerPrivate, mProxy, mXMLHttpRequestPrivate, mChannelId);
964 if (runnable->Dispatch()) {
965 mProxy->mWorkerPrivate = nullptr;
966 mProxy->mSyncLoopTarget = nullptr;
967 mProxy->mOutstandingSendCount--;
972 mProxy = nullptr;
973 mXHR = nullptr;
974 mXMLHttpRequestPrivate = nullptr;
975 return NS_OK;
978 NS_IMETHODIMP
979 LoadStartDetectionRunnable::HandleEvent(Event* aEvent) {
980 AssertIsOnMainThread();
982 #ifdef DEBUG
984 nsAutoString type;
985 aEvent->GetType(type);
986 MOZ_ASSERT(type == mEventType);
988 #endif
990 mReceivedLoadStart = true;
991 return NS_OK;
994 bool EventRunnable::PreDispatch(WorkerPrivate* /* unused */) {
995 AssertIsOnMainThread();
997 AutoJSAPI jsapi;
998 DebugOnly<bool> ok = jsapi.Init(xpc::NativeGlobal(mScopeObj));
999 MOZ_ASSERT(ok);
1000 JSContext* cx = jsapi.cx();
1001 // Now keep the mScopeObj alive for the duration
1002 JS::Rooted<JSObject*> scopeObj(cx, mScopeObj);
1003 // And reset mScopeObj now, before we have a chance to run its destructor on
1004 // some background thread.
1005 mScopeObj.reset();
1007 RefPtr<XMLHttpRequestMainThread>& xhr = mProxy->mXHR;
1008 MOZ_ASSERT(xhr);
1010 ErrorResult rv;
1012 XMLHttpRequestResponseType type = xhr->ResponseType();
1014 // We want to take the result data only if this is available.
1015 if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
1016 switch (type) {
1017 case XMLHttpRequestResponseType::_empty:
1018 case XMLHttpRequestResponseType::Text: {
1019 xhr->GetResponseText(mResponseData->mResponseText, rv);
1020 mResponseData->mResponseResult = rv.StealNSResult();
1021 break;
1024 case XMLHttpRequestResponseType::Blob: {
1025 mResponseData->mResponseBlobImpl = xhr->GetResponseBlobImpl();
1026 break;
1029 case XMLHttpRequestResponseType::Arraybuffer: {
1030 mResponseData->mResponseArrayBufferBuilder =
1031 xhr->GetResponseArrayBufferBuilder();
1032 break;
1035 case XMLHttpRequestResponseType::Json: {
1036 mResponseData->mResponseResult =
1037 xhr->GetResponseTextForJSON(mResponseData->mResponseJSON);
1038 break;
1041 default:
1042 MOZ_ASSERT_UNREACHABLE("Invalid response type");
1043 return false;
1047 mStatus = xhr->GetStatus(rv);
1048 mStatusResult = rv.StealNSResult();
1050 xhr->GetStatusText(mStatusText, rv);
1051 MOZ_ASSERT(!rv.Failed());
1053 mReadyState = xhr->ReadyState();
1055 xhr->GetResponseURL(mResponseURL);
1057 return true;
1060 bool EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1061 if (mEventStreamId != mProxy->mOuterEventStreamId) {
1062 // Threads raced, this event is now obsolete.
1063 return true;
1066 if (!mProxy->mXMLHttpRequestPrivate) {
1067 // Object was finalized, bail.
1068 return true;
1071 if (mType.EqualsASCII(sEventStrings[STRING_loadstart])) {
1072 if (mUploadEvent) {
1073 mProxy->mSeenUploadLoadStart = true;
1074 } else {
1075 mProxy->mSeenLoadStart = true;
1077 } else if (mType.EqualsASCII(sEventStrings[STRING_loadend])) {
1078 if (mUploadEvent) {
1079 mProxy->mSeenUploadLoadStart = false;
1080 if (mProxy->mDispatchPrematureAbortEventToUpload) {
1081 // We've already dispatched premature abort events.
1082 return true;
1084 } else {
1085 mProxy->mSeenLoadStart = false;
1086 if (mProxy->mDispatchPrematureAbortEvent) {
1087 // We've already dispatched premature abort events.
1088 return true;
1091 } else if (mType.EqualsASCII(sEventStrings[STRING_abort])) {
1092 if ((mUploadEvent && mProxy->mDispatchPrematureAbortEventToUpload) ||
1093 (!mUploadEvent && mProxy->mDispatchPrematureAbortEvent)) {
1094 // We've already dispatched premature abort events.
1095 return true;
1099 if (mProgressEvent) {
1100 // Cache these for premature abort events.
1101 if (mUploadEvent) {
1102 mProxy->mLastUploadLengthComputable = mLengthComputable;
1103 mProxy->mLastUploadLoaded = mLoaded;
1104 mProxy->mLastUploadTotal = mTotal;
1105 } else {
1106 mProxy->mLastLengthComputable = mLengthComputable;
1107 mProxy->mLastLoaded = mLoaded;
1108 mProxy->mLastTotal = mTotal;
1112 UniquePtr<XMLHttpRequestWorker::StateData> state(
1113 new XMLHttpRequestWorker::StateData());
1115 state->mStatusResult = mStatusResult;
1116 state->mStatus = mStatus;
1118 state->mStatusText = mStatusText;
1120 state->mReadyState = mReadyState;
1122 state->mResponseURL = mResponseURL;
1124 XMLHttpRequestWorker* xhr = mProxy->mXMLHttpRequestPrivate;
1125 xhr->UpdateState(std::move(state),
1126 mType.EqualsASCII(sEventStrings[STRING_readystatechange])
1127 ? std::move(mResponseData)
1128 : nullptr);
1130 if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
1131 return true;
1134 XMLHttpRequestEventTarget* target;
1135 if (mUploadEvent) {
1136 target = xhr->GetUploadObjectNoCreate();
1137 } else {
1138 target = xhr;
1141 MOZ_ASSERT(target);
1143 RefPtr<Event> event;
1144 if (mProgressEvent) {
1145 ProgressEventInit init;
1146 init.mBubbles = false;
1147 init.mCancelable = false;
1148 init.mLengthComputable = mLengthComputable;
1149 init.mLoaded = mLoaded;
1150 init.mTotal = mTotal;
1152 event = ProgressEvent::Constructor(target, mType, init);
1153 } else {
1154 event = NS_NewDOMEvent(target, nullptr, nullptr);
1156 if (event) {
1157 event->InitEvent(mType, false, false);
1161 if (!event) {
1162 return false;
1165 event->SetTrusted(true);
1167 target->DispatchEvent(*event);
1169 return true;
1172 bool WorkerThreadProxySyncRunnable::MainThreadRun() {
1173 AssertIsOnMainThread();
1175 nsCOMPtr<nsIEventTarget> tempTarget = mSyncLoopTarget;
1177 mProxy->mSyncEventResponseTarget.swap(tempTarget);
1179 ErrorResult rv;
1180 RunOnMainThread(rv);
1181 mErrorCode = rv.StealNSResult();
1183 mProxy->mSyncEventResponseTarget.swap(tempTarget);
1185 return true;
1188 void AbortRunnable::RunOnMainThread(ErrorResult& aRv) {
1189 mProxy->mInnerEventStreamId++;
1191 WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
1192 mProxy->mWorkerPrivate = mWorkerPrivate;
1194 mProxy->mXHR->Abort(aRv);
1196 mProxy->mWorkerPrivate = oldWorker;
1198 mProxy->Reset();
1201 void OpenRunnable::MainThreadRunInternal(ErrorResult& aRv) {
1202 if (!mProxy->Init()) {
1203 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1204 return;
1207 if (mBackgroundRequest) {
1208 mProxy->mXHR->SetMozBackgroundRequestExternal(mBackgroundRequest, aRv);
1209 if (aRv.Failed()) {
1210 return;
1214 if (mOriginStack) {
1215 mProxy->mXHR->SetOriginStack(std::move(mOriginStack));
1218 if (mWithCredentials) {
1219 mProxy->mXHR->SetWithCredentials(mWithCredentials, aRv);
1220 if (NS_WARN_IF(aRv.Failed())) {
1221 return;
1225 if (mTimeout) {
1226 mProxy->mXHR->SetTimeout(mTimeout, aRv);
1227 if (NS_WARN_IF(aRv.Failed())) {
1228 return;
1232 if (!mMimeTypeOverride.IsVoid()) {
1233 mProxy->mXHR->OverrideMimeType(mMimeTypeOverride, aRv);
1234 if (NS_WARN_IF(aRv.Failed())) {
1235 return;
1239 MOZ_ASSERT(!mProxy->mInOpen);
1240 mProxy->mInOpen = true;
1242 mProxy->mXHR->Open(
1243 mMethod, mURL, true, mUser.WasPassed() ? mUser.Value() : VoidString(),
1244 mPassword.WasPassed() ? mPassword.Value() : VoidString(), aRv);
1246 MOZ_ASSERT(mProxy->mInOpen);
1247 mProxy->mInOpen = false;
1249 if (NS_WARN_IF(aRv.Failed())) {
1250 return;
1253 if (mSource) {
1254 mProxy->mXHR->SetSource(std::move(mSource));
1257 mProxy->mXHR->SetResponseType(mResponseType, aRv);
1260 void SendRunnable::RunOnMainThread(ErrorResult& aRv) {
1261 // Before we change any state let's check if we can send.
1262 if (!mProxy->mXHR->CanSend(aRv)) {
1263 return;
1266 Nullable<
1267 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>
1268 payload;
1270 if (!mBlobImpl) {
1271 payload.SetNull();
1272 } else {
1273 JS::Rooted<JSObject*> globalObject(RootingCx(),
1274 xpc::UnprivilegedJunkScope(fallible));
1275 if (NS_WARN_IF(!globalObject)) {
1276 aRv.Throw(NS_ERROR_FAILURE);
1277 return;
1280 nsCOMPtr<nsIGlobalObject> parent = xpc::NativeGlobal(globalObject);
1281 if (NS_WARN_IF(!parent)) {
1282 aRv.Throw(NS_ERROR_FAILURE);
1283 return;
1286 RefPtr<Blob> blob = Blob::Create(parent, mBlobImpl);
1287 MOZ_ASSERT(blob);
1289 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString&
1290 ref = payload.SetValue();
1291 ref.SetAsBlob() = blob;
1294 // Send() has been already called, reset the proxy.
1295 if (mProxy->mWorkerPrivate) {
1296 mProxy->Reset();
1299 mProxy->mWorkerPrivate = mWorkerPrivate;
1301 MOZ_ASSERT(!mProxy->mSyncLoopTarget);
1302 mProxy->mSyncLoopTarget.swap(mSyncLoopTarget);
1304 if (mHasUploadListeners) {
1305 // Send() can be called more than once before failure,
1306 // so don't attach the upload listeners more than once.
1307 if (!mProxy->mUploadEventListenersAttached &&
1308 !mProxy->AddRemoveEventListeners(true, true)) {
1309 MOZ_ASSERT(false, "This should never fail!");
1313 mProxy->mInnerChannelId++;
1315 mProxy->mXHR->Send(payload, aRv);
1317 if (!aRv.Failed()) {
1318 mProxy->mOutstandingSendCount++;
1320 if (!mHasUploadListeners) {
1321 // Send() can be called more than once before failure,
1322 // so don't attach the upload listeners more than once.
1323 if (!mProxy->mUploadEventListenersAttached &&
1324 !mProxy->AddRemoveEventListeners(true, true)) {
1325 MOZ_ASSERT(false, "This should never fail!");
1328 } else {
1329 // In case of failure we just break the sync loop
1330 mProxy->mSyncLoopTarget = nullptr;
1331 mSyncLoopTarget = nullptr;
1335 XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate,
1336 nsIGlobalObject* aGlobalObject)
1337 : XMLHttpRequest(aGlobalObject),
1338 mWorkerPrivate(aWorkerPrivate),
1339 mResponseType(XMLHttpRequestResponseType::_empty),
1340 mStateData(new StateData()),
1341 mResponseData(new ResponseData()),
1342 mResponseArrayBufferValue(nullptr),
1343 mResponseJSONValue(JS::UndefinedValue()),
1344 mTimeout(0),
1345 mBackgroundRequest(false),
1346 mWithCredentials(false),
1347 mCanceled(false),
1348 mFlagSendActive(false),
1349 mMozAnon(false),
1350 mMozSystem(false),
1351 mMimeTypeOverride(VoidString()) {
1352 mWorkerPrivate->AssertIsOnWorkerThread();
1354 mozilla::HoldJSObjects(this);
1357 XMLHttpRequestWorker::~XMLHttpRequestWorker() {
1358 mWorkerPrivate->AssertIsOnWorkerThread();
1360 ReleaseProxy(XHRIsGoingAway);
1362 MOZ_ASSERT(!mWorkerRef);
1364 mozilla::DropJSObjects(this);
1367 NS_IMPL_ADDREF_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget)
1368 NS_IMPL_RELEASE_INHERITED(XMLHttpRequestWorker, XMLHttpRequestEventTarget)
1370 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XMLHttpRequestWorker)
1371 NS_INTERFACE_MAP_END_INHERITING(XMLHttpRequestEventTarget)
1373 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequestWorker)
1375 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequestWorker,
1376 XMLHttpRequestEventTarget)
1377 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
1378 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
1379 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1381 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequestWorker,
1382 XMLHttpRequestEventTarget)
1383 tmp->ReleaseProxy(XHRIsGoingAway);
1384 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
1385 tmp->mResponseData = nullptr;
1386 tmp->mResponseBlob = nullptr;
1387 tmp->mResponseArrayBufferValue = nullptr;
1388 tmp->mResponseJSONValue.setUndefined();
1389 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1391 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequestWorker,
1392 XMLHttpRequestEventTarget)
1393 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseArrayBufferValue)
1394 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResponseJSONValue)
1395 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1397 /* static */
1398 already_AddRefed<XMLHttpRequest> XMLHttpRequestWorker::Construct(
1399 const GlobalObject& aGlobal, const MozXMLHttpRequestParameters& aParams,
1400 ErrorResult& aRv) {
1401 JSContext* cx = aGlobal.Context();
1402 WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
1403 MOZ_ASSERT(workerPrivate);
1405 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
1406 if (NS_WARN_IF(!global)) {
1407 aRv.Throw(NS_ERROR_FAILURE);
1408 return nullptr;
1411 RefPtr<XMLHttpRequestWorker> xhr =
1412 new XMLHttpRequestWorker(workerPrivate, global);
1414 if (workerPrivate->XHRParamsAllowed()) {
1415 if (aParams.mMozSystem)
1416 xhr->mMozAnon = true;
1417 else
1418 xhr->mMozAnon = aParams.mMozAnon;
1419 xhr->mMozSystem = aParams.mMozSystem;
1422 return xhr.forget();
1425 void XMLHttpRequestWorker::ReleaseProxy(ReleaseType aType) {
1426 // Can't assert that we're on the worker thread here because mWorkerPrivate
1427 // may be gone.
1429 if (mProxy) {
1430 if (aType == XHRIsGoingAway) {
1431 // We're in a GC finalizer, so we can't do a sync call here (and we don't
1432 // need to).
1433 RefPtr<AsyncTeardownRunnable> runnable =
1434 new AsyncTeardownRunnable(mProxy);
1435 mProxy = nullptr;
1437 if (NS_FAILED(mWorkerPrivate->DispatchToMainThread(runnable.forget()))) {
1438 NS_ERROR("Failed to dispatch teardown runnable!");
1440 } else {
1441 // This isn't necessary if the worker is going away or the XHR is going
1442 // away.
1443 if (aType == Default) {
1444 // Don't let any more events run.
1445 mProxy->mOuterEventStreamId++;
1448 // We need to make a sync call here.
1449 RefPtr<SyncTeardownRunnable> runnable =
1450 new SyncTeardownRunnable(mWorkerPrivate, mProxy);
1451 mProxy = nullptr;
1453 IgnoredErrorResult forAssertionsOnly;
1454 // This runnable _must_ be executed.
1455 runnable->Dispatch(Dead, forAssertionsOnly);
1456 MOZ_DIAGNOSTIC_ASSERT(!forAssertionsOnly.Failed());
1461 void XMLHttpRequestWorker::MaybePin(ErrorResult& aRv) {
1462 mWorkerPrivate->AssertIsOnWorkerThread();
1464 if (mWorkerRef) {
1465 return;
1468 RefPtr<XMLHttpRequestWorker> self = this;
1469 mWorkerRef =
1470 StrongWorkerRef::Create(mWorkerPrivate, "XMLHttpRequestWorker", [self]() {
1471 if (!self->mCanceled) {
1472 self->mCanceled = true;
1473 self->ReleaseProxy(WorkerIsGoingAway);
1476 if (NS_WARN_IF(!mWorkerRef)) {
1477 aRv.Throw(NS_ERROR_FAILURE);
1478 return;
1481 NS_ADDREF_THIS();
1484 void XMLHttpRequestWorker::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv) {
1485 mWorkerPrivate->AssertIsOnWorkerThread();
1486 MOZ_ASSERT(mProxy);
1488 // Only send readystatechange event when state changed.
1489 bool isStateChanged = false;
1490 if ((mStateData->mReadyState == 1 && mStateData->mFlagSend) ||
1491 mStateData->mReadyState == 2 || mStateData->mReadyState == 3) {
1492 isStateChanged = true;
1493 mStateData->mReadyState = 4;
1496 if (mProxy->mSeenUploadLoadStart) {
1497 MOZ_ASSERT(mUpload);
1499 DispatchPrematureAbortEvent(mUpload, u"abort"_ns, true, aRv);
1500 if (aRv.Failed()) {
1501 return;
1504 DispatchPrematureAbortEvent(mUpload, u"loadend"_ns, true, aRv);
1505 if (aRv.Failed()) {
1506 return;
1509 // Similarly to null check in ::Open, mProxy may have been cleared here.
1510 if (!mProxy) {
1511 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1512 return;
1515 mProxy->mSeenUploadLoadStart = false;
1516 mProxy->mDispatchPrematureAbortEventToUpload = true;
1519 if (mProxy->mSeenLoadStart) {
1520 if (isStateChanged) {
1521 DispatchPrematureAbortEvent(this, u"readystatechange"_ns, false, aRv);
1522 if (aRv.Failed()) {
1523 return;
1527 DispatchPrematureAbortEvent(this, u"abort"_ns, false, aRv);
1528 if (aRv.Failed()) {
1529 return;
1532 DispatchPrematureAbortEvent(this, u"loadend"_ns, false, aRv);
1533 if (aRv.Failed()) {
1534 return;
1537 // Similarly to null check in ::Open, mProxy may have been cleared here.
1538 if (!mProxy) {
1539 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1540 return;
1543 mProxy->mSeenLoadStart = false;
1544 mProxy->mDispatchPrematureAbortEvent = true;
1548 void XMLHttpRequestWorker::DispatchPrematureAbortEvent(
1549 EventTarget* aTarget, const nsAString& aEventType, bool aUploadTarget,
1550 ErrorResult& aRv) {
1551 mWorkerPrivate->AssertIsOnWorkerThread();
1552 MOZ_ASSERT(aTarget);
1554 if (!mProxy) {
1555 aRv.Throw(NS_ERROR_FAILURE);
1556 return;
1559 RefPtr<Event> event;
1560 if (aEventType.EqualsLiteral("readystatechange")) {
1561 event = NS_NewDOMEvent(aTarget, nullptr, nullptr);
1562 event->InitEvent(aEventType, false, false);
1563 } else {
1564 if (mProxy->mIsSyncXHR &&
1565 aEventType.EqualsASCII(sEventStrings[STRING_progress])) {
1566 return;
1569 ProgressEventInit init;
1570 init.mBubbles = false;
1571 init.mCancelable = false;
1572 if (aUploadTarget) {
1573 init.mLengthComputable = mProxy->mLastUploadLengthComputable;
1574 init.mLoaded = mProxy->mLastUploadLoaded;
1575 init.mTotal = mProxy->mLastUploadTotal;
1576 } else {
1577 init.mLengthComputable = mProxy->mLastLengthComputable;
1578 init.mLoaded = mProxy->mLastLoaded;
1579 init.mTotal = mProxy->mLastTotal;
1581 event = ProgressEvent::Constructor(aTarget, aEventType, init);
1584 if (!event) {
1585 aRv.Throw(NS_ERROR_FAILURE);
1586 return;
1589 event->SetTrusted(true);
1591 aTarget->DispatchEvent(*event);
1594 void XMLHttpRequestWorker::Unpin() {
1595 mWorkerPrivate->AssertIsOnWorkerThread();
1597 MOZ_ASSERT(mWorkerRef, "Mismatched calls to Unpin!");
1598 mWorkerRef = nullptr;
1600 NS_RELEASE_THIS();
1603 void XMLHttpRequestWorker::SendInternal(const BodyExtractorBase* aBody,
1604 ErrorResult& aRv) {
1605 mWorkerPrivate->AssertIsOnWorkerThread();
1607 // We don't really need to keep the same body-type when we proxy the send()
1608 // call to the main-thread XHR. Let's extract the nsIInputStream from the
1609 // aBody and let's wrap it into a StreamBlobImpl.
1611 RefPtr<BlobImpl> blobImpl;
1613 if (aBody) {
1614 nsAutoCString charset;
1615 nsAutoCString defaultContentType;
1616 nsCOMPtr<nsIInputStream> uploadStream;
1618 uint64_t size_u64;
1619 aRv = aBody->GetAsStream(getter_AddRefs(uploadStream), &size_u64,
1620 defaultContentType, charset);
1621 if (NS_WARN_IF(aRv.Failed())) {
1622 return;
1625 blobImpl = StreamBlobImpl::Create(uploadStream.forget(),
1626 NS_ConvertUTF8toUTF16(defaultContentType),
1627 size_u64, u"StreamBlobImpl"_ns);
1628 MOZ_ASSERT(blobImpl);
1631 RefPtr<SendRunnable> sendRunnable =
1632 new SendRunnable(mWorkerPrivate, mProxy, blobImpl);
1634 // No send() calls when open is running.
1635 if (mProxy->mOpenCount) {
1636 aRv.Throw(NS_ERROR_FAILURE);
1637 return;
1640 bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
1642 MaybePin(aRv);
1643 if (aRv.Failed()) {
1644 return;
1647 AutoUnpinXHR autoUnpin(this);
1648 Maybe<AutoSyncLoopHolder> autoSyncLoop;
1650 nsCOMPtr<nsISerialEventTarget> syncLoopTarget;
1651 bool isSyncXHR = mProxy->mIsSyncXHR;
1652 if (isSyncXHR) {
1653 autoSyncLoop.emplace(mWorkerPrivate, Canceling);
1654 syncLoopTarget = autoSyncLoop->GetSerialEventTarget();
1655 if (!syncLoopTarget) {
1656 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1657 return;
1661 mProxy->mOuterChannelId++;
1662 mProxy->mDispatchPrematureAbortEvent = false;
1663 mProxy->mDispatchPrematureAbortEventToUpload = false;
1665 sendRunnable->SetSyncLoopTarget(syncLoopTarget);
1666 sendRunnable->SetHaveUploadListeners(hasUploadListeners);
1668 mStateData->mFlagSend = true;
1670 sendRunnable->Dispatch(Canceling, aRv);
1671 if (aRv.Failed()) {
1672 // Dispatch() may have spun the event loop and we may have already unrooted.
1673 // If so we don't want autoUnpin to try again.
1674 if (!mWorkerRef) {
1675 autoUnpin.Clear();
1677 return;
1680 if (!isSyncXHR) {
1681 autoUnpin.Clear();
1682 MOZ_ASSERT(!autoSyncLoop);
1683 return;
1686 autoUnpin.Clear();
1688 bool succeeded = NS_SUCCEEDED(autoSyncLoop->Run());
1689 mStateData->mFlagSend = false;
1691 // Don't clobber an existing exception that we may have thrown on aRv
1692 // already... though can there really be one? In any case, it seems to me
1693 // that this autoSyncLoop->Run() can never fail, since the StopSyncLoop call
1694 // for it will come from ProxyCompleteRunnable and that always passes true for
1695 // the second arg.
1696 if (!succeeded && !aRv.Failed()) {
1697 aRv.Throw(NS_ERROR_FAILURE);
1701 void XMLHttpRequestWorker::Open(const nsACString& aMethod,
1702 const nsAString& aUrl, bool aAsync,
1703 const Optional<nsAString>& aUser,
1704 const Optional<nsAString>& aPassword,
1705 ErrorResult& aRv) {
1706 mWorkerPrivate->AssertIsOnWorkerThread();
1708 if (mCanceled) {
1709 aRv.ThrowUncatchableException();
1710 return;
1713 bool alsoOverrideMimeType = false;
1714 if (mProxy) {
1715 MaybeDispatchPrematureAbortEvents(aRv);
1716 if (aRv.Failed()) {
1717 return;
1719 } else {
1720 Maybe<ClientInfo> clientInfo(
1721 mWorkerPrivate->GlobalScope()->GetClientInfo());
1722 if (clientInfo.isNothing()) {
1723 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1724 return;
1726 mProxy = new Proxy(this, clientInfo.ref(),
1727 mWorkerPrivate->GlobalScope()->GetController(), mMozAnon,
1728 mMozSystem);
1729 alsoOverrideMimeType = true;
1732 mProxy->mOuterEventStreamId++;
1734 UniquePtr<SerializedStackHolder> stack;
1735 if (mWorkerPrivate->IsWatchedByDevTools()) {
1736 if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
1737 stack = GetCurrentStackForNetMonitor(cx);
1741 RefPtr<OpenRunnable> runnable = new OpenRunnable(
1742 mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
1743 mBackgroundRequest, mWithCredentials, mTimeout, mResponseType,
1744 alsoOverrideMimeType ? mMimeTypeOverride : VoidString(), std::move(stack),
1745 profiler_capture_backtrace());
1747 ++mProxy->mOpenCount;
1748 runnable->Dispatch(Canceling, aRv);
1749 if (aRv.Failed()) {
1750 if (mProxy && !--mProxy->mOpenCount) {
1751 ReleaseProxy();
1754 return;
1757 // We have been released in one of the nested Open() calls.
1758 if (!mProxy) {
1759 aRv.Throw(NS_ERROR_FAILURE);
1760 return;
1763 --mProxy->mOpenCount;
1764 mProxy->mIsSyncXHR = !aAsync;
1767 void XMLHttpRequestWorker::SetRequestHeader(const nsACString& aHeader,
1768 const nsACString& aValue,
1769 ErrorResult& aRv) {
1770 mWorkerPrivate->AssertIsOnWorkerThread();
1772 if (mCanceled) {
1773 aRv.ThrowUncatchableException();
1774 return;
1777 if (!mProxy) {
1778 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1779 return;
1782 RefPtr<SetRequestHeaderRunnable> runnable =
1783 new SetRequestHeaderRunnable(mWorkerPrivate, mProxy, aHeader, aValue);
1784 runnable->Dispatch(Canceling, aRv);
1787 void XMLHttpRequestWorker::SetTimeout(uint32_t aTimeout, ErrorResult& aRv) {
1788 mWorkerPrivate->AssertIsOnWorkerThread();
1790 if (mCanceled) {
1791 aRv.ThrowUncatchableException();
1792 return;
1795 mTimeout = aTimeout;
1797 if (!mProxy) {
1798 // Open may not have been called yet, in which case we'll handle the
1799 // timeout in OpenRunnable.
1800 return;
1803 RefPtr<SetTimeoutRunnable> runnable =
1804 new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout);
1805 runnable->Dispatch(Canceling, aRv);
1808 void XMLHttpRequestWorker::SetWithCredentials(bool aWithCredentials,
1809 ErrorResult& aRv) {
1810 mWorkerPrivate->AssertIsOnWorkerThread();
1812 if (mCanceled) {
1813 aRv.ThrowUncatchableException();
1814 return;
1817 mWithCredentials = aWithCredentials;
1819 if (!mProxy) {
1820 // Open may not have been called yet, in which case we'll handle the
1821 // credentials in OpenRunnable.
1822 return;
1825 RefPtr<SetWithCredentialsRunnable> runnable =
1826 new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials);
1827 runnable->Dispatch(Canceling, aRv);
1830 void XMLHttpRequestWorker::SetMozBackgroundRequest(bool aBackgroundRequest,
1831 ErrorResult& aRv) {
1832 mWorkerPrivate->AssertIsOnWorkerThread();
1834 if (mCanceled) {
1835 aRv.ThrowUncatchableException();
1836 return;
1839 mBackgroundRequest = aBackgroundRequest;
1841 if (!mProxy) {
1842 // Open may not have been called yet, in which case we'll handle the
1843 // background request in OpenRunnable.
1844 return;
1847 RefPtr<SetBackgroundRequestRunnable> runnable =
1848 new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy,
1849 aBackgroundRequest);
1850 runnable->Dispatch(Canceling, aRv);
1853 XMLHttpRequestUpload* XMLHttpRequestWorker::GetUpload(ErrorResult& aRv) {
1854 mWorkerPrivate->AssertIsOnWorkerThread();
1856 if (mCanceled) {
1857 aRv.ThrowUncatchableException();
1858 return nullptr;
1861 if (!mUpload) {
1862 mUpload = new XMLHttpRequestUpload(this);
1865 return mUpload;
1868 void XMLHttpRequestWorker::Send(
1869 const Nullable<
1870 DocumentOrBlobOrArrayBufferViewOrArrayBufferOrFormDataOrURLSearchParamsOrUSVString>&
1871 aData,
1872 ErrorResult& aRv) {
1873 mWorkerPrivate->AssertIsOnWorkerThread();
1875 if (mFlagSendActive) {
1876 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_XHR_HAS_INVALID_CONTEXT);
1877 return;
1879 mFlagSendActive = true;
1880 auto clearRecursionFlag = MakeScopeExit([&]() {
1881 // No one else should have touched this flag.
1882 MOZ_ASSERT(mFlagSendActive);
1883 mFlagSendActive = false;
1886 if (mCanceled) {
1887 aRv.ThrowUncatchableException();
1888 return;
1891 if (mStateData->mReadyState != XMLHttpRequest_Binding::OPENED) {
1892 aRv.ThrowInvalidStateError("XMLHttpRequest state must be OPENED.");
1893 return;
1896 if (!mProxy || mStateData->mFlagSend) {
1897 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1898 return;
1901 if (aData.IsNull()) {
1902 SendInternal(nullptr, aRv);
1903 return;
1906 if (aData.Value().IsDocument()) {
1907 MOZ_ASSERT_UNREACHABLE("Documents are not exposed to workers.");
1908 aRv.Throw(NS_ERROR_FAILURE);
1909 return;
1912 if (aData.Value().IsBlob()) {
1913 BodyExtractor<const Blob> body(&aData.Value().GetAsBlob());
1914 SendInternal(&body, aRv);
1915 return;
1918 if (aData.Value().IsArrayBuffer()) {
1919 BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer());
1920 SendInternal(&body, aRv);
1921 return;
1924 if (aData.Value().IsArrayBufferView()) {
1925 BodyExtractor<const ArrayBufferView> body(
1926 &aData.Value().GetAsArrayBufferView());
1927 SendInternal(&body, aRv);
1928 return;
1931 if (aData.Value().IsFormData()) {
1932 BodyExtractor<const FormData> body(&aData.Value().GetAsFormData());
1933 SendInternal(&body, aRv);
1934 return;
1937 if (aData.Value().IsURLSearchParams()) {
1938 BodyExtractor<const URLSearchParams> body(
1939 &aData.Value().GetAsURLSearchParams());
1940 SendInternal(&body, aRv);
1941 return;
1944 if (aData.Value().IsUSVString()) {
1945 BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString());
1946 SendInternal(&body, aRv);
1947 return;
1951 void XMLHttpRequestWorker::Abort(ErrorResult& aRv) {
1952 mWorkerPrivate->AssertIsOnWorkerThread();
1954 if (mCanceled) {
1955 aRv.ThrowUncatchableException();
1956 return;
1959 if (!mProxy) {
1960 return;
1963 // Set our status to 0 and statusText to "" if we
1964 // will be aborting an ongoing fetch, so the upcoming
1965 // abort events we dispatch have the correct info.
1966 if ((mStateData->mReadyState == XMLHttpRequest_Binding::OPENED &&
1967 mStateData->mFlagSend) ||
1968 mStateData->mReadyState == XMLHttpRequest_Binding::HEADERS_RECEIVED ||
1969 mStateData->mReadyState == XMLHttpRequest_Binding::LOADING ||
1970 mStateData->mReadyState == XMLHttpRequest_Binding::DONE) {
1971 mStateData->mStatus = 0;
1972 mStateData->mStatusText.Truncate();
1975 MaybeDispatchPrematureAbortEvents(aRv);
1976 if (aRv.Failed()) {
1977 return;
1980 if (mStateData->mReadyState == 4) {
1981 // No one did anything to us while we fired abort events, so reset our state
1982 // to "unsent"
1983 mStateData->mReadyState = 0;
1986 mProxy->mOuterEventStreamId++;
1988 RefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
1989 runnable->Dispatch(Canceling, aRv);
1992 void XMLHttpRequestWorker::GetResponseHeader(const nsACString& aHeader,
1993 nsACString& aResponseHeader,
1994 ErrorResult& aRv) {
1995 mWorkerPrivate->AssertIsOnWorkerThread();
1997 if (mCanceled) {
1998 aRv.ThrowUncatchableException();
1999 return;
2002 if (!mProxy) {
2003 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2004 return;
2007 nsCString responseHeader;
2008 RefPtr<GetResponseHeaderRunnable> runnable = new GetResponseHeaderRunnable(
2009 mWorkerPrivate, mProxy, aHeader, responseHeader);
2010 runnable->Dispatch(Canceling, aRv);
2011 if (aRv.Failed()) {
2012 return;
2014 aResponseHeader = responseHeader;
2017 void XMLHttpRequestWorker::GetAllResponseHeaders(nsACString& aResponseHeaders,
2018 ErrorResult& aRv) {
2019 mWorkerPrivate->AssertIsOnWorkerThread();
2021 if (mCanceled) {
2022 aRv.ThrowUncatchableException();
2023 return;
2026 if (!mProxy) {
2027 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2028 return;
2031 nsCString responseHeaders;
2032 RefPtr<GetAllResponseHeadersRunnable> runnable =
2033 new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy,
2034 responseHeaders);
2035 runnable->Dispatch(Canceling, aRv);
2036 if (aRv.Failed()) {
2037 return;
2040 aResponseHeaders = responseHeaders;
2043 void XMLHttpRequestWorker::OverrideMimeType(const nsAString& aMimeType,
2044 ErrorResult& aRv) {
2045 mWorkerPrivate->AssertIsOnWorkerThread();
2047 if (mCanceled) {
2048 aRv.ThrowUncatchableException();
2049 return;
2052 // We're supposed to throw if the state is LOADING or DONE.
2053 if (mStateData->mReadyState == XMLHttpRequest_Binding::LOADING ||
2054 mStateData->mReadyState == XMLHttpRequest_Binding::DONE) {
2055 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2056 return;
2059 mMimeTypeOverride = aMimeType;
2061 if (mProxy) {
2062 RefPtr<OverrideMimeTypeRunnable> runnable =
2063 new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
2064 runnable->Dispatch(Canceling, aRv);
2068 void XMLHttpRequestWorker::SetResponseType(
2069 XMLHttpRequestResponseType aResponseType, ErrorResult& aRv) {
2070 mWorkerPrivate->AssertIsOnWorkerThread();
2072 // "document" is fine for the main thread but not for a worker. Short-circuit
2073 // that here.
2074 if (aResponseType == XMLHttpRequestResponseType::Document) {
2075 return;
2078 if (!mProxy) {
2079 // Open() has not been called yet. We store the responseType and we will use
2080 // it later in Open().
2081 mResponseType = aResponseType;
2082 return;
2085 if (mStateData->mReadyState == XMLHttpRequest_Binding::LOADING ||
2086 mStateData->mReadyState == XMLHttpRequest_Binding::DONE) {
2087 aRv.ThrowInvalidStateError(
2088 "Cannot set 'responseType' property on XMLHttpRequest after 'send()' "
2089 "(when its state is LOADING or DONE).");
2090 return;
2093 RefPtr<SetResponseTypeRunnable> runnable =
2094 new SetResponseTypeRunnable(mWorkerPrivate, mProxy, aResponseType);
2095 runnable->Dispatch(Canceling, aRv);
2096 if (aRv.Failed()) {
2097 return;
2100 mResponseType = runnable->ResponseType();
2103 void XMLHttpRequestWorker::GetResponse(JSContext* aCx,
2104 JS::MutableHandle<JS::Value> aResponse,
2105 ErrorResult& aRv) {
2106 if (NS_FAILED(mResponseData->mResponseResult)) {
2107 aRv.Throw(mResponseData->mResponseResult);
2108 return;
2111 switch (mResponseType) {
2112 case XMLHttpRequestResponseType::_empty:
2113 case XMLHttpRequestResponseType::Text: {
2114 JSString* str;
2116 if (mResponseData->mResponseText.IsEmpty()) {
2117 aResponse.set(JS_GetEmptyStringValue(aCx));
2118 return;
2121 XMLHttpRequestStringSnapshotReaderHelper helper(
2122 mResponseData->mResponseText);
2124 str = JS_NewUCStringCopyN(aCx, helper.Buffer(), helper.Length());
2125 if (!str) {
2126 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2127 return;
2130 aResponse.setString(str);
2131 return;
2134 case XMLHttpRequestResponseType::Arraybuffer: {
2135 if (!mResponseData->mResponseArrayBufferBuilder) {
2136 aResponse.setNull();
2137 return;
2140 if (!mResponseArrayBufferValue) {
2141 mResponseArrayBufferValue =
2142 mResponseData->mResponseArrayBufferBuilder->TakeArrayBuffer(aCx);
2143 if (!mResponseArrayBufferValue) {
2144 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2145 return;
2149 aResponse.setObject(*mResponseArrayBufferValue);
2150 return;
2153 case XMLHttpRequestResponseType::Blob: {
2154 if (!mResponseData->mResponseBlobImpl) {
2155 aResponse.setNull();
2156 return;
2159 if (!mResponseBlob) {
2160 mResponseBlob =
2161 Blob::Create(GetOwnerGlobal(), mResponseData->mResponseBlobImpl);
2164 if (!mResponseBlob ||
2165 !GetOrCreateDOMReflector(aCx, mResponseBlob, aResponse)) {
2166 aResponse.setNull();
2169 return;
2172 case XMLHttpRequestResponseType::Json: {
2173 if (mResponseData->mResponseJSON.IsVoid()) {
2174 aResponse.setNull();
2175 return;
2178 if (mResponseJSONValue.isUndefined()) {
2179 // The Unicode converter has already zapped the BOM if there was one
2180 JS::Rooted<JS::Value> value(aCx);
2181 if (!JS_ParseJSON(aCx, mResponseData->mResponseJSON.BeginReading(),
2182 mResponseData->mResponseJSON.Length(), &value)) {
2183 JS_ClearPendingException(aCx);
2184 mResponseJSONValue.setNull();
2185 } else {
2186 mResponseJSONValue = value;
2189 mResponseData->mResponseJSON.Truncate();
2192 aResponse.set(mResponseJSONValue);
2193 return;
2196 default:
2197 MOZ_ASSERT_UNREACHABLE("Invalid type");
2198 aResponse.setNull();
2199 return;
2203 void XMLHttpRequestWorker::GetResponseText(DOMString& aResponseText,
2204 ErrorResult& aRv) {
2205 MOZ_DIAGNOSTIC_ASSERT(mResponseData);
2207 if (mResponseType != XMLHttpRequestResponseType::_empty &&
2208 mResponseType != XMLHttpRequestResponseType::Text) {
2209 aRv.ThrowInvalidStateError(
2210 "responseText is only available if responseType is '' or 'text'.");
2211 return;
2214 if (!mResponseData->mResponseText.GetAsString(aResponseText)) {
2215 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2216 return;
2220 void XMLHttpRequestWorker::UpdateState(
2221 UniquePtr<StateData>&& aStateData,
2222 UniquePtr<ResponseData>&& aResponseData) {
2223 mStateData = std::move(aStateData);
2225 UniquePtr<ResponseData> responseData = std::move(aResponseData);
2226 if (responseData) {
2227 ResetResponseData();
2228 mResponseData = std::move(responseData);
2231 XMLHttpRequest_Binding::ClearCachedResponseTextValue(this);
2234 void XMLHttpRequestWorker::ResetResponseData() {
2235 mResponseBlob = nullptr;
2236 mResponseArrayBufferValue = nullptr;
2237 mResponseJSONValue.setUndefined();
2240 } // namespace mozilla::dom