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