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