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