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