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