Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "XMLHttpRequest.h"
8 #include "nsIDOMEvent.h"
9 #include "nsIDOMEventListener.h"
10 #include "nsIDOMProgressEvent.h"
11 #include "nsIRunnable.h"
12 #include "nsIVariant.h"
13 #include "nsIXMLHttpRequest.h"
14 #include "nsIXPConnect.h"
16 #include "jsfriendapi.h"
17 #include "mozilla/ArrayUtils.h"
18 #include "mozilla/dom/Exceptions.h"
19 #include "nsComponentManagerUtils.h"
20 #include "nsContentUtils.h"
21 #include "nsCxPusher.h"
22 #include "nsJSUtils.h"
23 #include "nsThreadUtils.h"
25 #include "File.h"
26 #include "RuntimeService.h"
27 #include "WorkerPrivate.h"
28 #include "WorkerRunnable.h"
29 #include "XMLHttpRequestUpload.h"
31 using namespace mozilla;
33 using namespace mozilla::dom;
34 USING_WORKERS_NAMESPACE
36 // XXX Need to figure this out...
37 #define UNCATCHABLE_EXCEPTION NS_ERROR_OUT_OF_MEMORY
39 /**
40 * XMLHttpRequest in workers
41 *
42 * XHR in workers is implemented by proxying calls/events/etc between the
43 * worker thread and an nsXMLHttpRequest on the main thread. The glue
44 * object here is the Proxy, which lives on both threads. All other objects
45 * live on either the main thread (the nsXMLHttpRequest) or the worker thread
46 * (the worker and XHR private objects).
47 *
48 * The main thread XHR is always operated in async mode, even for sync XHR
49 * in workers. Calls made on the worker thread are proxied to the main thread
50 * synchronously (meaning the worker thread is blocked until the call
51 * returns). Each proxied call spins up a sync queue, which captures any
52 * synchronously dispatched events and ensures that they run synchronously
53 * on the worker as well. Asynchronously dispatched events are posted to the
54 * worker thread to run asynchronously. Some of the XHR state is mirrored on
55 * the worker thread to avoid needing a cross-thread call on every property
56 * access.
57 *
58 * The XHR private is stored in the private slot of the XHR JSObject on the
59 * worker thread. It is destroyed when that JSObject is GCd. The private
60 * roots its JSObject while network activity is in progress. It also
61 * adds itself as a feature to the worker to give itself a chance to clean up
62 * if the worker goes away during an XHR call. It is important that the
63 * rooting and feature registration (collectively called pinning) happens at
64 * the proper times. If we pin for too long we can cause memory leaks or even
65 * shutdown hangs. If we don't pin for long enough we introduce a GC hazard.
66 *
67 * The XHR is pinned from the time Send is called to roughly the time loadend
68 * is received. There are some complications involved with Abort and XHR
69 * reuse. We maintain a counter on the main thread of how many times Send was
70 * called on this XHR, and we decrement the counter every time we receive a
71 * loadend event. When the counter reaches zero we dispatch a runnable to the
72 * worker thread to unpin the XHR. We only decrement the counter if the
73 * dispatch was successful, because the worker may no longer be accepting
74 * regular runnables. In the event that we reach Proxy::Teardown and there
75 * the outstanding Send count is still non-zero, we dispatch a control
76 * runnable which is guaranteed to run.
77 *
78 * NB: Some of this could probably be simplified now that we have the
79 * inner/outer channel ids.
80 */
82 BEGIN_WORKERS_NAMESPACE
84 class Proxy MOZ_FINAL : public nsIDOMEventListener
85 {
86 public:
87 // Read on multiple threads.
88 WorkerPrivate* mWorkerPrivate;
89 XMLHttpRequest* mXMLHttpRequestPrivate;
91 // XHR Params:
92 bool mMozAnon;
93 bool mMozSystem;
95 // Only touched on the main thread.
96 nsRefPtr<nsXMLHttpRequest> mXHR;
97 nsCOMPtr<nsIXMLHttpRequestUpload> mXHRUpload;
98 nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
99 nsCOMPtr<nsIEventTarget> mSyncEventResponseTarget;
100 uint32_t mInnerEventStreamId;
101 uint32_t mInnerChannelId;
102 uint32_t mOutstandingSendCount;
104 // Only touched on the worker thread.
105 uint32_t mOuterEventStreamId;
106 uint32_t mOuterChannelId;
107 uint64_t mLastLoaded;
108 uint64_t mLastTotal;
109 uint64_t mLastUploadLoaded;
110 uint64_t mLastUploadTotal;
111 bool mIsSyncXHR;
112 bool mLastLengthComputable;
113 bool mLastUploadLengthComputable;
114 bool mSeenLoadStart;
115 bool mSeenUploadLoadStart;
117 // Only touched on the main thread.
118 bool mUploadEventListenersAttached;
119 bool mMainThreadSeenLoadStart;
120 bool mInOpen;
122 public:
123 Proxy(XMLHttpRequest* aXHRPrivate, bool aMozAnon, bool aMozSystem)
124 : mWorkerPrivate(nullptr), mXMLHttpRequestPrivate(aXHRPrivate),
125 mMozAnon(aMozAnon), mMozSystem(aMozSystem),
126 mInnerEventStreamId(0), mInnerChannelId(0), mOutstandingSendCount(0),
127 mOuterEventStreamId(0), mOuterChannelId(0), mLastLoaded(0), mLastTotal(0),
128 mLastUploadLoaded(0), mLastUploadTotal(0), mIsSyncXHR(false),
129 mLastLengthComputable(false), mLastUploadLengthComputable(false),
130 mSeenLoadStart(false), mSeenUploadLoadStart(false),
131 mUploadEventListenersAttached(false), mMainThreadSeenLoadStart(false),
132 mInOpen(false)
133 { }
135 NS_DECL_THREADSAFE_ISUPPORTS
136 NS_DECL_NSIDOMEVENTLISTENER
138 bool
139 Init();
141 void
142 Teardown();
144 bool
145 AddRemoveEventListeners(bool aUpload, bool aAdd);
147 void
148 Reset()
149 {
150 AssertIsOnMainThread();
152 if (mUploadEventListenersAttached) {
153 AddRemoveEventListeners(true, false);
154 }
155 }
157 already_AddRefed<nsIEventTarget>
158 GetEventTarget()
159 {
160 AssertIsOnMainThread();
162 nsCOMPtr<nsIEventTarget> target = mSyncEventResponseTarget ?
163 mSyncEventResponseTarget :
164 mSyncLoopTarget;
165 return target.forget();
166 }
168 private:
169 ~Proxy()
170 {
171 MOZ_ASSERT(!mXHR);
172 MOZ_ASSERT(!mXHRUpload);
173 MOZ_ASSERT(!mOutstandingSendCount);
174 }
175 };
177 END_WORKERS_NAMESPACE
179 namespace {
181 inline void
182 ConvertResponseTypeToString(XMLHttpRequestResponseType aType,
183 nsString& aString)
184 {
185 using namespace
186 mozilla::dom::XMLHttpRequestResponseTypeValues;
188 size_t index = static_cast<size_t>(aType);
189 MOZ_ASSERT(index < ArrayLength(strings), "Codegen gave us a bad value!");
191 aString.AssignASCII(strings[index].value, strings[index].length);
192 }
194 inline XMLHttpRequestResponseType
195 ConvertStringToResponseType(const nsAString& aString)
196 {
197 using namespace
198 mozilla::dom::XMLHttpRequestResponseTypeValues;
200 for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
201 if (aString.EqualsASCII(strings[index].value, strings[index].length)) {
202 return static_cast<XMLHttpRequestResponseType>(index);
203 }
204 }
206 MOZ_ASSUME_UNREACHABLE("Don't know anything about this response type!");
207 }
209 enum
210 {
211 STRING_abort = 0,
212 STRING_error,
213 STRING_load,
214 STRING_loadstart,
215 STRING_progress,
216 STRING_timeout,
217 STRING_readystatechange,
218 STRING_loadend,
220 STRING_COUNT,
222 STRING_LAST_XHR = STRING_loadend,
223 STRING_LAST_EVENTTARGET = STRING_timeout
224 };
226 static_assert(STRING_LAST_XHR >= STRING_LAST_EVENTTARGET, "Bad string setup!");
227 static_assert(STRING_LAST_XHR == STRING_COUNT - 1, "Bad string setup!");
229 const char* const sEventStrings[] = {
230 // nsIXMLHttpRequestEventTarget event types, supported by both XHR and Upload.
231 "abort",
232 "error",
233 "load",
234 "loadstart",
235 "progress",
236 "timeout",
238 // nsIXMLHttpRequest event types, supported only by XHR.
239 "readystatechange",
240 "loadend",
241 };
243 static_assert(MOZ_ARRAY_LENGTH(sEventStrings) == STRING_COUNT,
244 "Bad string count!");
246 class MainThreadProxyRunnable : public MainThreadWorkerSyncRunnable
247 {
248 protected:
249 nsRefPtr<Proxy> mProxy;
251 MainThreadProxyRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
252 : MainThreadWorkerSyncRunnable(aWorkerPrivate, aProxy->GetEventTarget()),
253 mProxy(aProxy)
254 {
255 MOZ_ASSERT(aProxy);
256 }
258 virtual ~MainThreadProxyRunnable()
259 { }
260 };
262 class XHRUnpinRunnable MOZ_FINAL : public MainThreadWorkerControlRunnable
263 {
264 XMLHttpRequest* mXMLHttpRequestPrivate;
266 public:
267 XHRUnpinRunnable(WorkerPrivate* aWorkerPrivate,
268 XMLHttpRequest* aXHRPrivate)
269 : MainThreadWorkerControlRunnable(aWorkerPrivate),
270 mXMLHttpRequestPrivate(aXHRPrivate)
271 {
272 MOZ_ASSERT(aXHRPrivate);
273 }
275 private:
276 ~XHRUnpinRunnable()
277 { }
279 bool
280 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
281 {
282 mXMLHttpRequestPrivate->Unpin();
284 return true;
285 }
286 };
288 class AsyncTeardownRunnable MOZ_FINAL : public nsRunnable
289 {
290 nsRefPtr<Proxy> mProxy;
292 public:
293 AsyncTeardownRunnable(Proxy* aProxy)
294 : mProxy(aProxy)
295 {
296 MOZ_ASSERT(aProxy);
297 }
299 NS_DECL_ISUPPORTS_INHERITED
301 private:
302 ~AsyncTeardownRunnable()
303 { }
305 NS_IMETHOD
306 Run() MOZ_OVERRIDE
307 {
308 AssertIsOnMainThread();
310 mProxy->Teardown();
311 mProxy = nullptr;
313 return NS_OK;
314 }
315 };
317 class LoadStartDetectionRunnable MOZ_FINAL : public nsRunnable,
318 public nsIDOMEventListener
319 {
320 WorkerPrivate* mWorkerPrivate;
321 nsRefPtr<Proxy> mProxy;
322 nsRefPtr<nsXMLHttpRequest> mXHR;
323 XMLHttpRequest* mXMLHttpRequestPrivate;
324 nsString mEventType;
325 uint32_t mChannelId;
326 bool mReceivedLoadStart;
328 class ProxyCompleteRunnable MOZ_FINAL : public MainThreadProxyRunnable
329 {
330 XMLHttpRequest* mXMLHttpRequestPrivate;
331 uint32_t mChannelId;
333 public:
334 ProxyCompleteRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
335 XMLHttpRequest* aXHRPrivate, uint32_t aChannelId)
336 : MainThreadProxyRunnable(aWorkerPrivate, aProxy),
337 mXMLHttpRequestPrivate(aXHRPrivate), mChannelId(aChannelId)
338 { }
340 private:
341 ~ProxyCompleteRunnable()
342 { }
344 virtual bool
345 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
346 {
347 if (mChannelId != mProxy->mOuterChannelId) {
348 // Threads raced, this event is now obsolete.
349 return true;
350 }
352 if (mSyncLoopTarget) {
353 aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, true);
354 }
356 mXMLHttpRequestPrivate->Unpin();
358 return true;
359 }
361 NS_IMETHOD
362 Cancel() MOZ_OVERRIDE
363 {
364 // This must run!
365 nsresult rv = MainThreadProxyRunnable::Cancel();
366 nsresult rv2 = Run();
367 return NS_FAILED(rv) ? rv : rv2;
368 }
369 };
371 public:
372 LoadStartDetectionRunnable(Proxy* aProxy, XMLHttpRequest* aXHRPrivate)
373 : mWorkerPrivate(aProxy->mWorkerPrivate), mProxy(aProxy), mXHR(aProxy->mXHR),
374 mXMLHttpRequestPrivate(aXHRPrivate), mChannelId(mProxy->mInnerChannelId),
375 mReceivedLoadStart(false)
376 {
377 AssertIsOnMainThread();
378 mEventType.AssignWithConversion(sEventStrings[STRING_loadstart]);
379 }
381 NS_DECL_ISUPPORTS_INHERITED
382 NS_DECL_NSIRUNNABLE
383 NS_DECL_NSIDOMEVENTLISTENER
385 bool
386 RegisterAndDispatch()
387 {
388 AssertIsOnMainThread();
390 if (NS_FAILED(mXHR->AddEventListener(mEventType, this, false, false, 2))) {
391 NS_WARNING("Failed to add event listener!");
392 return false;
393 }
395 return NS_SUCCEEDED(NS_DispatchToCurrentThread(this));
396 }
398 private:
399 ~LoadStartDetectionRunnable()
400 {
401 AssertIsOnMainThread();
402 }
403 };
405 class EventRunnable MOZ_FINAL : public MainThreadProxyRunnable
406 {
407 nsString mType;
408 nsString mResponseType;
409 JSAutoStructuredCloneBuffer mResponseBuffer;
410 nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
411 JS::Heap<JS::Value> mResponse;
412 nsString mResponseText;
413 nsCString mStatusText;
414 uint64_t mLoaded;
415 uint64_t mTotal;
416 uint32_t mEventStreamId;
417 uint32_t mStatus;
418 uint16_t mReadyState;
419 bool mUploadEvent;
420 bool mProgressEvent;
421 bool mLengthComputable;
422 nsresult mResponseTextResult;
423 nsresult mStatusResult;
424 nsresult mResponseResult;
426 public:
427 class StateDataAutoRooter : private JS::CustomAutoRooter
428 {
429 XMLHttpRequest::StateData* mStateData;
430 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
432 public:
433 explicit StateDataAutoRooter(JSContext* aCx, XMLHttpRequest::StateData* aData
434 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
435 : CustomAutoRooter(aCx), mStateData(aData)
436 {
437 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
438 }
440 private:
441 virtual void trace(JSTracer* aTrc)
442 {
443 JS_CallHeapValueTracer(aTrc, &mStateData->mResponse,
444 "XMLHttpRequest::StateData::mResponse");
445 }
446 };
448 EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
449 bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal)
450 : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
451 mResponse(JSVAL_VOID), mLoaded(aLoaded), mTotal(aTotal),
452 mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
453 mUploadEvent(aUploadEvent), mProgressEvent(true),
454 mLengthComputable(aLengthComputable), mResponseTextResult(NS_OK),
455 mStatusResult(NS_OK), mResponseResult(NS_OK)
456 { }
458 EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
459 : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
460 mResponse(JSVAL_VOID), mLoaded(0), mTotal(0),
461 mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
462 mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
463 mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK)
464 { }
466 private:
467 ~EventRunnable()
468 { }
470 virtual bool
471 PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
473 virtual bool
474 WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
475 };
477 class WorkerThreadProxySyncRunnable : public nsRunnable
478 {
479 protected:
480 WorkerPrivate* mWorkerPrivate;
481 nsRefPtr<Proxy> mProxy;
482 nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
484 private:
485 class ResponseRunnable MOZ_FINAL: public MainThreadStopSyncLoopRunnable
486 {
487 nsRefPtr<Proxy> mProxy;
488 nsresult mErrorCode;
490 public:
491 ResponseRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
492 nsresult aErrorCode)
493 : MainThreadStopSyncLoopRunnable(aWorkerPrivate, aProxy->GetEventTarget(),
494 NS_SUCCEEDED(aErrorCode)),
495 mProxy(aProxy), mErrorCode(aErrorCode)
496 {
497 MOZ_ASSERT(aProxy);
498 }
500 private:
501 ~ResponseRunnable()
502 { }
504 virtual void
505 MaybeSetException(JSContext* aCx) MOZ_OVERRIDE
506 {
507 MOZ_ASSERT(NS_FAILED(mErrorCode));
509 Throw(aCx, mErrorCode);
510 }
511 };
513 public:
514 WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
515 : mWorkerPrivate(aWorkerPrivate), mProxy(aProxy)
516 {
517 MOZ_ASSERT(aWorkerPrivate);
518 MOZ_ASSERT(aProxy);
519 aWorkerPrivate->AssertIsOnWorkerThread();
520 }
522 NS_DECL_ISUPPORTS_INHERITED
524 bool
525 Dispatch(JSContext* aCx)
526 {
527 mWorkerPrivate->AssertIsOnWorkerThread();
529 AutoSyncLoopHolder syncLoop(mWorkerPrivate);
530 mSyncLoopTarget = syncLoop.EventTarget();
532 if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
533 JS_ReportError(aCx, "Failed to dispatch to main thread!");
534 return false;
535 }
537 return syncLoop.Run();
538 }
540 protected:
541 virtual ~WorkerThreadProxySyncRunnable()
542 { }
544 virtual nsresult
545 MainThreadRun() = 0;
547 private:
548 NS_DECL_NSIRUNNABLE
549 };
551 class SyncTeardownRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
552 {
553 public:
554 SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
555 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
556 { }
558 private:
559 ~SyncTeardownRunnable()
560 { }
562 virtual nsresult
563 MainThreadRun() MOZ_OVERRIDE
564 {
565 mProxy->Teardown();
566 MOZ_ASSERT(!mProxy->mSyncLoopTarget);
567 return NS_OK;
568 }
569 };
571 class SetBackgroundRequestRunnable MOZ_FINAL :
572 public WorkerThreadProxySyncRunnable
573 {
574 bool mValue;
576 public:
577 SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
578 bool aValue)
579 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
580 { }
582 private:
583 ~SetBackgroundRequestRunnable()
584 { }
586 virtual nsresult
587 MainThreadRun() MOZ_OVERRIDE
588 {
589 return mProxy->mXHR->SetMozBackgroundRequest(mValue);
590 }
591 };
593 class SetWithCredentialsRunnable MOZ_FINAL :
594 public WorkerThreadProxySyncRunnable
595 {
596 bool mValue;
598 public:
599 SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
600 bool aValue)
601 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
602 { }
604 private:
605 ~SetWithCredentialsRunnable()
606 { }
608 virtual nsresult
609 MainThreadRun() MOZ_OVERRIDE
610 {
611 return mProxy->mXHR->SetWithCredentials(mValue);
612 }
613 };
615 class SetResponseTypeRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
616 {
617 nsString mResponseType;
619 public:
620 SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
621 const nsAString& aResponseType)
622 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
623 mResponseType(aResponseType)
624 { }
626 void
627 GetResponseType(nsAString& aResponseType)
628 {
629 aResponseType.Assign(mResponseType);
630 }
632 private:
633 ~SetResponseTypeRunnable()
634 { }
636 virtual nsresult
637 MainThreadRun() MOZ_OVERRIDE
638 {
639 nsresult rv = mProxy->mXHR->SetResponseType(mResponseType);
640 mResponseType.Truncate();
641 if (NS_SUCCEEDED(rv)) {
642 rv = mProxy->mXHR->GetResponseType(mResponseType);
643 }
644 return rv;
645 }
646 };
648 class SetTimeoutRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
649 {
650 uint32_t mTimeout;
652 public:
653 SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
654 uint32_t aTimeout)
655 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mTimeout(aTimeout)
656 { }
658 private:
659 ~SetTimeoutRunnable()
660 { }
662 virtual nsresult
663 MainThreadRun() MOZ_OVERRIDE
664 {
665 return mProxy->mXHR->SetTimeout(mTimeout);
666 }
667 };
669 class AbortRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
670 {
671 public:
672 AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
673 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
674 { }
676 private:
677 ~AbortRunnable()
678 { }
680 virtual nsresult
681 MainThreadRun() MOZ_OVERRIDE;
682 };
684 class GetAllResponseHeadersRunnable MOZ_FINAL :
685 public WorkerThreadProxySyncRunnable
686 {
687 nsCString& mResponseHeaders;
689 public:
690 GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
691 nsCString& aResponseHeaders)
692 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
693 mResponseHeaders(aResponseHeaders)
694 { }
696 private:
697 ~GetAllResponseHeadersRunnable()
698 { }
700 virtual nsresult
701 MainThreadRun() MOZ_OVERRIDE
702 {
703 mProxy->mXHR->GetAllResponseHeaders(mResponseHeaders);
704 return NS_OK;
705 }
706 };
708 class GetResponseHeaderRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
709 {
710 const nsCString mHeader;
711 nsCString& mValue;
713 public:
714 GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
715 const nsACString& aHeader, nsCString& aValue)
716 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mHeader(aHeader),
717 mValue(aValue)
718 { }
720 private:
721 ~GetResponseHeaderRunnable()
722 { }
724 virtual nsresult
725 MainThreadRun() MOZ_OVERRIDE
726 {
727 return mProxy->mXHR->GetResponseHeader(mHeader, mValue);
728 }
729 };
731 class OpenRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
732 {
733 nsCString mMethod;
734 nsString mURL;
735 Optional<nsAString> mUser;
736 nsString mUserStr;
737 Optional<nsAString> mPassword;
738 nsString mPasswordStr;
739 bool mBackgroundRequest;
740 bool mWithCredentials;
741 uint32_t mTimeout;
743 public:
744 OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
745 const nsACString& aMethod, const nsAString& aURL,
746 const Optional<nsAString>& aUser,
747 const Optional<nsAString>& aPassword,
748 bool aBackgroundRequest, bool aWithCredentials,
749 uint32_t aTimeout)
750 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMethod(aMethod),
751 mURL(aURL), mBackgroundRequest(aBackgroundRequest),
752 mWithCredentials(aWithCredentials), mTimeout(aTimeout)
753 {
754 if (aUser.WasPassed()) {
755 mUserStr = aUser.Value();
756 mUser = &mUserStr;
757 }
758 if (aPassword.WasPassed()) {
759 mPasswordStr = aPassword.Value();
760 mPassword = &mPasswordStr;
761 }
762 }
764 private:
765 ~OpenRunnable()
766 { }
768 virtual nsresult
769 MainThreadRun() MOZ_OVERRIDE
770 {
771 WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
772 mProxy->mWorkerPrivate = mWorkerPrivate;
774 nsresult rv = MainThreadRunInternal();
776 mProxy->mWorkerPrivate = oldWorker;
777 return rv;
778 }
780 nsresult
781 MainThreadRunInternal();
782 };
784 class SendRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
785 {
786 nsString mStringBody;
787 JSAutoStructuredCloneBuffer mBody;
788 nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
789 nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
790 bool mHasUploadListeners;
792 public:
793 SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
794 const nsAString& aStringBody, JSAutoStructuredCloneBuffer&& aBody,
795 nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects,
796 nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
797 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
798 , mStringBody(aStringBody)
799 , mBody(Move(aBody))
800 , mSyncLoopTarget(aSyncLoopTarget)
801 , mHasUploadListeners(aHasUploadListeners)
802 {
803 mClonedObjects.SwapElements(aClonedObjects);
804 }
806 private:
807 ~SendRunnable()
808 { }
810 virtual nsresult
811 MainThreadRun() MOZ_OVERRIDE;
812 };
814 class SetRequestHeaderRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
815 {
816 nsCString mHeader;
817 nsCString mValue;
819 public:
820 SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
821 const nsACString& aHeader, const nsACString& aValue)
822 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mHeader(aHeader),
823 mValue(aValue)
824 { }
826 private:
827 ~SetRequestHeaderRunnable()
828 { }
830 virtual nsresult
831 MainThreadRun() MOZ_OVERRIDE
832 {
833 return mProxy->mXHR->SetRequestHeader(mHeader, mValue);
834 }
835 };
837 class OverrideMimeTypeRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
838 {
839 nsString mMimeType;
841 public:
842 OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
843 const nsAString& aMimeType)
844 : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMimeType(aMimeType)
845 { }
847 private:
848 ~OverrideMimeTypeRunnable()
849 { }
851 virtual nsresult
852 MainThreadRun() MOZ_OVERRIDE
853 {
854 mProxy->mXHR->OverrideMimeType(mMimeType);
855 return NS_OK;
856 }
857 };
859 class AutoUnpinXHR
860 {
861 XMLHttpRequest* mXMLHttpRequestPrivate;
863 public:
864 AutoUnpinXHR(XMLHttpRequest* aXMLHttpRequestPrivate)
865 : mXMLHttpRequestPrivate(aXMLHttpRequestPrivate)
866 {
867 MOZ_ASSERT(aXMLHttpRequestPrivate);
868 }
870 ~AutoUnpinXHR()
871 {
872 if (mXMLHttpRequestPrivate) {
873 mXMLHttpRequestPrivate->Unpin();
874 }
875 }
877 void Clear()
878 {
879 mXMLHttpRequestPrivate = nullptr;
880 }
881 };
883 } // anonymous namespace
885 bool
886 Proxy::Init()
887 {
888 AssertIsOnMainThread();
889 MOZ_ASSERT(mWorkerPrivate);
891 if (mXHR) {
892 return true;
893 }
895 nsPIDOMWindow* ownerWindow = mWorkerPrivate->GetWindow();
896 if (ownerWindow) {
897 ownerWindow = ownerWindow->GetOuterWindow();
898 if (!ownerWindow) {
899 NS_ERROR("No outer window?!");
900 return false;
901 }
903 nsPIDOMWindow* innerWindow = ownerWindow->GetCurrentInnerWindow();
904 if (mWorkerPrivate->GetWindow() != innerWindow) {
905 NS_WARNING("Window has navigated, cannot create XHR here.");
906 return false;
907 }
908 }
910 mXHR = new nsXMLHttpRequest();
912 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(ownerWindow);
913 if (NS_FAILED(mXHR->Init(mWorkerPrivate->GetPrincipal(),
914 mWorkerPrivate->GetScriptContext(),
915 global, mWorkerPrivate->GetBaseURI()))) {
916 mXHR = nullptr;
917 return false;
918 }
920 mXHR->SetParameters(mMozAnon, mMozSystem);
922 if (NS_FAILED(mXHR->GetUpload(getter_AddRefs(mXHRUpload)))) {
923 mXHR = nullptr;
924 return false;
925 }
927 if (!AddRemoveEventListeners(false, true)) {
928 mXHRUpload = nullptr;
929 mXHR = nullptr;
930 return false;
931 }
933 return true;
934 }
936 void
937 Proxy::Teardown()
938 {
939 AssertIsOnMainThread();
941 if (mXHR) {
942 Reset();
944 // NB: We are intentionally dropping events coming from xhr.abort on the
945 // floor.
946 AddRemoveEventListeners(false, false);
947 mXHR->Abort();
949 if (mOutstandingSendCount) {
950 nsRefPtr<XHRUnpinRunnable> runnable =
951 new XHRUnpinRunnable(mWorkerPrivate, mXMLHttpRequestPrivate);
952 if (!runnable->Dispatch(nullptr)) {
953 NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
954 }
956 if (mSyncLoopTarget) {
957 // We have an unclosed sync loop. Fix that now.
958 nsRefPtr<MainThreadStopSyncLoopRunnable> runnable =
959 new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
960 mSyncLoopTarget.forget(),
961 false);
962 if (!runnable->Dispatch(nullptr)) {
963 NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
964 }
965 }
967 mWorkerPrivate = nullptr;
968 mOutstandingSendCount = 0;
969 }
971 mXHRUpload = nullptr;
972 mXHR = nullptr;
973 }
975 MOZ_ASSERT(!mWorkerPrivate);
976 MOZ_ASSERT(!mSyncLoopTarget);
977 }
979 bool
980 Proxy::AddRemoveEventListeners(bool aUpload, bool aAdd)
981 {
982 AssertIsOnMainThread();
984 NS_ASSERTION(!aUpload ||
985 (mUploadEventListenersAttached && !aAdd) ||
986 (!mUploadEventListenersAttached && aAdd),
987 "Messed up logic for upload listeners!");
989 nsCOMPtr<nsIDOMEventTarget> target =
990 aUpload ?
991 do_QueryInterface(mXHRUpload) :
992 do_QueryInterface(static_cast<nsIXMLHttpRequest*>(mXHR.get()));
993 NS_ASSERTION(target, "This should never fail!");
995 uint32_t lastEventType = aUpload ? STRING_LAST_EVENTTARGET : STRING_LAST_XHR;
997 nsAutoString eventType;
998 for (uint32_t index = 0; index <= lastEventType; index++) {
999 eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
1000 if (aAdd) {
1001 if (NS_FAILED(target->AddEventListener(eventType, this, false))) {
1002 return false;
1003 }
1004 }
1005 else if (NS_FAILED(target->RemoveEventListener(eventType, this, false))) {
1006 return false;
1007 }
1008 }
1010 if (aUpload) {
1011 mUploadEventListenersAttached = aAdd;
1012 }
1014 return true;
1015 }
1017 NS_IMPL_ISUPPORTS(Proxy, nsIDOMEventListener)
1019 NS_IMETHODIMP
1020 Proxy::HandleEvent(nsIDOMEvent* aEvent)
1021 {
1022 AssertIsOnMainThread();
1024 if (!mWorkerPrivate || !mXMLHttpRequestPrivate) {
1025 NS_ERROR("Shouldn't get here!");
1026 return NS_OK;
1027 }
1029 nsString type;
1030 if (NS_FAILED(aEvent->GetType(type))) {
1031 NS_WARNING("Failed to get event type!");
1032 return NS_ERROR_FAILURE;
1033 }
1035 nsCOMPtr<nsIDOMEventTarget> target;
1036 if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) {
1037 NS_WARNING("Failed to get target!");
1038 return NS_ERROR_FAILURE;
1039 }
1041 nsCOMPtr<nsIXMLHttpRequestUpload> uploadTarget = do_QueryInterface(target);
1042 nsCOMPtr<nsIDOMProgressEvent> progressEvent = do_QueryInterface(aEvent);
1044 nsRefPtr<EventRunnable> runnable;
1046 if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
1047 uint16_t readyState = 0;
1048 if (NS_SUCCEEDED(mXHR->GetReadyState(&readyState)) &&
1049 readyState == nsIXMLHttpRequest::OPENED) {
1050 mInnerEventStreamId++;
1051 }
1052 }
1054 if (progressEvent) {
1055 bool lengthComputable;
1056 uint64_t loaded, total;
1057 if (NS_FAILED(progressEvent->GetLengthComputable(&lengthComputable)) ||
1058 NS_FAILED(progressEvent->GetLoaded(&loaded)) ||
1059 NS_FAILED(progressEvent->GetTotal(&total))) {
1060 NS_WARNING("Bad progress event!");
1061 return NS_ERROR_FAILURE;
1062 }
1063 runnable = new EventRunnable(this, !!uploadTarget, type, lengthComputable,
1064 loaded, total);
1065 }
1066 else {
1067 runnable = new EventRunnable(this, !!uploadTarget, type);
1068 }
1070 {
1071 AutoSafeJSContext cx;
1072 JSAutoRequest ar(cx);
1073 runnable->Dispatch(cx);
1074 }
1076 if (!uploadTarget) {
1077 if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
1078 NS_ASSERTION(!mMainThreadSeenLoadStart, "Huh?!");
1079 mMainThreadSeenLoadStart = true;
1080 }
1081 else if (mMainThreadSeenLoadStart &&
1082 type.EqualsASCII(sEventStrings[STRING_loadend])) {
1083 mMainThreadSeenLoadStart = false;
1085 nsRefPtr<LoadStartDetectionRunnable> runnable =
1086 new LoadStartDetectionRunnable(this, mXMLHttpRequestPrivate);
1087 if (!runnable->RegisterAndDispatch()) {
1088 NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
1089 }
1090 }
1091 }
1093 return NS_OK;
1094 }
1096 NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadProxySyncRunnable, nsRunnable)
1098 NS_IMPL_ISUPPORTS_INHERITED0(AsyncTeardownRunnable, nsRunnable)
1100 NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable, nsRunnable,
1101 nsIDOMEventListener)
1103 NS_IMETHODIMP
1104 LoadStartDetectionRunnable::Run()
1105 {
1106 AssertIsOnMainThread();
1108 if (NS_FAILED(mXHR->RemoveEventListener(mEventType, this, false))) {
1109 NS_WARNING("Failed to remove event listener!");
1110 }
1112 if (!mReceivedLoadStart) {
1113 if (mProxy->mOutstandingSendCount > 1) {
1114 mProxy->mOutstandingSendCount--;
1115 } else if (mProxy->mOutstandingSendCount == 1) {
1116 mProxy->Reset();
1118 nsRefPtr<ProxyCompleteRunnable> runnable =
1119 new ProxyCompleteRunnable(mWorkerPrivate, mProxy,
1120 mXMLHttpRequestPrivate, mChannelId);
1121 if (runnable->Dispatch(nullptr)) {
1122 mProxy->mWorkerPrivate = nullptr;
1123 mProxy->mSyncLoopTarget = nullptr;
1124 mProxy->mOutstandingSendCount--;
1125 }
1126 }
1127 }
1129 mProxy = nullptr;
1130 mXHR = nullptr;
1131 mXMLHttpRequestPrivate = nullptr;
1132 return NS_OK;
1133 }
1135 NS_IMETHODIMP
1136 LoadStartDetectionRunnable::HandleEvent(nsIDOMEvent* aEvent)
1137 {
1138 AssertIsOnMainThread();
1140 #ifdef DEBUG
1141 {
1142 nsString type;
1143 if (NS_SUCCEEDED(aEvent->GetType(type))) {
1144 MOZ_ASSERT(type == mEventType);
1145 }
1146 else {
1147 NS_WARNING("Failed to get event type!");
1148 }
1149 }
1150 #endif
1152 mReceivedLoadStart = true;
1153 return NS_OK;
1154 }
1156 bool
1157 EventRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1158 {
1159 AssertIsOnMainThread();
1161 nsRefPtr<nsXMLHttpRequest>& xhr = mProxy->mXHR;
1162 MOZ_ASSERT(xhr);
1164 if (NS_FAILED(xhr->GetResponseType(mResponseType))) {
1165 MOZ_ASSERT(false, "This should never fail!");
1166 }
1168 mResponseTextResult = xhr->GetResponseText(mResponseText);
1169 if (NS_SUCCEEDED(mResponseTextResult)) {
1170 mResponseResult = mResponseTextResult;
1171 if (mResponseText.IsVoid()) {
1172 mResponse = JSVAL_NULL;
1173 }
1174 }
1175 else {
1176 JS::Rooted<JS::Value> response(aCx);
1177 mResponseResult = xhr->GetResponse(aCx, &response);
1178 if (NS_SUCCEEDED(mResponseResult)) {
1179 if (JSVAL_IS_UNIVERSAL(response)) {
1180 mResponse = response;
1181 }
1182 else {
1183 // Anything subject to GC must be cloned.
1184 JSStructuredCloneCallbacks* callbacks =
1185 aWorkerPrivate->IsChromeWorker() ?
1186 ChromeWorkerStructuredCloneCallbacks(true) :
1187 WorkerStructuredCloneCallbacks(true);
1189 nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
1191 if (mResponseBuffer.write(aCx, response, callbacks, &clonedObjects)) {
1192 mClonedObjects.SwapElements(clonedObjects);
1193 }
1194 else {
1195 NS_WARNING("Failed to clone response!");
1196 mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
1197 }
1198 }
1199 }
1200 }
1202 mStatusResult = xhr->GetStatus(&mStatus);
1204 xhr->GetStatusText(mStatusText);
1206 mReadyState = xhr->ReadyState();
1208 return true;
1209 }
1211 bool
1212 EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
1213 {
1214 if (mEventStreamId != mProxy->mOuterEventStreamId) {
1215 // Threads raced, this event is now obsolete.
1216 return true;
1217 }
1219 if (!mProxy->mXMLHttpRequestPrivate) {
1220 // Object was finalized, bail.
1221 return true;
1222 }
1224 if (mType.EqualsASCII(sEventStrings[STRING_loadstart])) {
1225 if (mUploadEvent) {
1226 mProxy->mSeenUploadLoadStart = true;
1227 }
1228 else {
1229 mProxy->mSeenLoadStart = true;
1230 }
1231 }
1232 else if (mType.EqualsASCII(sEventStrings[STRING_loadend])) {
1233 if (mUploadEvent) {
1234 mProxy->mSeenUploadLoadStart = false;
1235 }
1236 else {
1237 mProxy->mSeenLoadStart = false;
1238 }
1239 }
1240 else if (mType.EqualsASCII(sEventStrings[STRING_abort])) {
1241 if ((mUploadEvent && !mProxy->mSeenUploadLoadStart) ||
1242 (!mUploadEvent && !mProxy->mSeenLoadStart)) {
1243 // We've already dispatched premature abort events.
1244 return true;
1245 }
1246 }
1247 else if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
1248 if (mReadyState == 4 && !mUploadEvent && !mProxy->mSeenLoadStart) {
1249 // We've already dispatched premature abort events.
1250 return true;
1251 }
1252 }
1254 if (mProgressEvent) {
1255 // Cache these for premature abort events.
1256 if (mUploadEvent) {
1257 mProxy->mLastUploadLengthComputable = mLengthComputable;
1258 mProxy->mLastUploadLoaded = mLoaded;
1259 mProxy->mLastUploadTotal = mTotal;
1260 }
1261 else {
1262 mProxy->mLastLengthComputable = mLengthComputable;
1263 mProxy->mLastLoaded = mLoaded;
1264 mProxy->mLastTotal = mTotal;
1265 }
1266 }
1268 nsAutoPtr<XMLHttpRequest::StateData> state(new XMLHttpRequest::StateData());
1269 StateDataAutoRooter rooter(aCx, state);
1271 state->mResponseTextResult = mResponseTextResult;
1272 state->mResponseText = mResponseText;
1274 if (NS_SUCCEEDED(mResponseTextResult)) {
1275 MOZ_ASSERT(JSVAL_IS_VOID(mResponse) || JSVAL_IS_NULL(mResponse));
1276 state->mResponseResult = mResponseTextResult;
1277 state->mResponse = mResponse;
1278 }
1279 else {
1280 state->mResponseResult = mResponseResult;
1282 if (NS_SUCCEEDED(mResponseResult)) {
1283 if (mResponseBuffer.data()) {
1284 MOZ_ASSERT(JSVAL_IS_VOID(mResponse));
1286 JSAutoStructuredCloneBuffer responseBuffer(Move(mResponseBuffer));
1288 JSStructuredCloneCallbacks* callbacks =
1289 aWorkerPrivate->IsChromeWorker() ?
1290 ChromeWorkerStructuredCloneCallbacks(false) :
1291 WorkerStructuredCloneCallbacks(false);
1293 nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
1294 clonedObjects.SwapElements(mClonedObjects);
1296 JS::Rooted<JS::Value> response(aCx);
1297 if (!responseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
1298 return false;
1299 }
1301 state->mResponse = response;
1302 }
1303 else {
1304 state->mResponse = mResponse;
1305 }
1306 }
1307 }
1309 state->mStatusResult = mStatusResult;
1310 state->mStatus = mStatus;
1312 state->mStatusText = mStatusText;
1314 state->mReadyState = mReadyState;
1316 XMLHttpRequest* xhr = mProxy->mXMLHttpRequestPrivate;
1317 xhr->UpdateState(*state);
1319 if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
1320 return true;
1321 }
1323 JS::Rooted<JSString*> type(aCx,
1324 JS_NewUCStringCopyN(aCx, mType.get(), mType.Length()));
1325 if (!type) {
1326 return false;
1327 }
1329 nsXHREventTarget* target;
1330 if (mUploadEvent) {
1331 target = xhr->GetUploadObjectNoCreate();
1332 }
1333 else {
1334 target = xhr;
1335 }
1337 MOZ_ASSERT(target);
1339 nsCOMPtr<nsIDOMEvent> event;
1340 if (mProgressEvent) {
1341 NS_NewDOMProgressEvent(getter_AddRefs(event), target, nullptr, nullptr);
1342 nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
1344 if (progress) {
1345 progress->InitProgressEvent(mType, false, false, mLengthComputable,
1346 mLoaded, mTotal);
1347 }
1348 }
1349 else {
1350 NS_NewDOMEvent(getter_AddRefs(event), target, nullptr, nullptr);
1352 if (event) {
1353 event->InitEvent(mType, false, false);
1354 }
1355 }
1357 if (!event) {
1358 return false;
1359 }
1361 event->SetTrusted(true);
1363 target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
1365 // After firing the event set mResponse to JSVAL_NULL for chunked response
1366 // types.
1367 if (StringBeginsWith(mResponseType, NS_LITERAL_STRING("moz-chunked-"))) {
1368 xhr->NullResponseText();
1369 }
1371 return true;
1372 }
1374 NS_IMETHODIMP
1375 WorkerThreadProxySyncRunnable::Run()
1376 {
1377 AssertIsOnMainThread();
1379 nsCOMPtr<nsIEventTarget> tempTarget;
1380 mSyncLoopTarget.swap(tempTarget);
1382 mProxy->mSyncEventResponseTarget.swap(tempTarget);
1384 nsresult rv = MainThreadRun();
1386 nsRefPtr<ResponseRunnable> response =
1387 new ResponseRunnable(mWorkerPrivate, mProxy, rv);
1388 if (!response->Dispatch(nullptr)) {
1389 MOZ_ASSERT(false, "Failed to dispatch response!");
1390 }
1392 mProxy->mSyncEventResponseTarget.swap(tempTarget);
1394 return NS_OK;
1395 }
1397 nsresult
1398 AbortRunnable::MainThreadRun()
1399 {
1400 mProxy->mInnerEventStreamId++;
1402 WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
1403 mProxy->mWorkerPrivate = mWorkerPrivate;
1405 mProxy->mXHR->Abort();
1407 mProxy->mWorkerPrivate = oldWorker;
1409 mProxy->Reset();
1411 return NS_OK;
1412 }
1414 nsresult
1415 OpenRunnable::MainThreadRunInternal()
1416 {
1417 if (!mProxy->Init()) {
1418 return NS_ERROR_DOM_INVALID_STATE_ERR;
1419 }
1421 nsresult rv;
1423 if (mBackgroundRequest) {
1424 rv = mProxy->mXHR->SetMozBackgroundRequest(mBackgroundRequest);
1425 NS_ENSURE_SUCCESS(rv, rv);
1426 }
1428 if (mWithCredentials) {
1429 rv = mProxy->mXHR->SetWithCredentials(mWithCredentials);
1430 NS_ENSURE_SUCCESS(rv, rv);
1431 }
1433 if (mTimeout) {
1434 rv = mProxy->mXHR->SetTimeout(mTimeout);
1435 NS_ENSURE_SUCCESS(rv, rv);
1436 }
1438 MOZ_ASSERT(!mProxy->mInOpen);
1439 mProxy->mInOpen = true;
1441 ErrorResult rv2;
1442 mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, rv2);
1444 MOZ_ASSERT(mProxy->mInOpen);
1445 mProxy->mInOpen = false;
1447 if (rv2.Failed()) {
1448 return rv2.ErrorCode();
1449 }
1451 return mProxy->mXHR->SetResponseType(NS_LITERAL_STRING("text"));
1452 }
1455 nsresult
1456 SendRunnable::MainThreadRun()
1457 {
1458 nsCOMPtr<nsIVariant> variant;
1460 if (mBody.data()) {
1461 AutoSafeJSContext cx;
1462 JSAutoRequest ar(cx);
1464 nsIXPConnect* xpc = nsContentUtils::XPConnect();
1465 MOZ_ASSERT(xpc);
1467 nsresult rv = NS_OK;
1469 JSStructuredCloneCallbacks* callbacks =
1470 mWorkerPrivate->IsChromeWorker() ?
1471 ChromeWorkerStructuredCloneCallbacks(true) :
1472 WorkerStructuredCloneCallbacks(true);
1474 JS::Rooted<JS::Value> body(cx);
1475 if (mBody.read(cx, &body, callbacks, &mClonedObjects)) {
1476 if (NS_FAILED(xpc->JSValToVariant(cx, body, getter_AddRefs(variant)))) {
1477 rv = NS_ERROR_DOM_INVALID_STATE_ERR;
1478 }
1479 }
1480 else {
1481 rv = NS_ERROR_DOM_DATA_CLONE_ERR;
1482 }
1484 mBody.clear();
1485 mClonedObjects.Clear();
1487 NS_ENSURE_SUCCESS(rv, rv);
1488 }
1489 else {
1490 nsCOMPtr<nsIWritableVariant> wvariant =
1491 do_CreateInstance(NS_VARIANT_CONTRACTID);
1492 NS_ENSURE_TRUE(wvariant, NS_ERROR_UNEXPECTED);
1494 if (NS_FAILED(wvariant->SetAsAString(mStringBody))) {
1495 MOZ_ASSERT(false, "This should never fail!");
1496 }
1498 variant = wvariant;
1499 }
1501 MOZ_ASSERT(!mProxy->mWorkerPrivate);
1502 mProxy->mWorkerPrivate = mWorkerPrivate;
1504 MOZ_ASSERT(!mProxy->mSyncLoopTarget);
1505 mProxy->mSyncLoopTarget.swap(mSyncLoopTarget);
1507 if (mHasUploadListeners) {
1508 NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
1509 if (!mProxy->AddRemoveEventListeners(true, true)) {
1510 MOZ_ASSERT(false, "This should never fail!");
1511 }
1512 }
1514 mProxy->mInnerChannelId++;
1516 nsresult rv = mProxy->mXHR->Send(variant);
1518 if (NS_SUCCEEDED(rv)) {
1519 mProxy->mOutstandingSendCount++;
1521 if (!mHasUploadListeners) {
1522 NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
1523 if (!mProxy->AddRemoveEventListeners(true, true)) {
1524 MOZ_ASSERT(false, "This should never fail!");
1525 }
1526 }
1527 }
1529 return rv;
1530 }
1532 XMLHttpRequest::XMLHttpRequest(WorkerPrivate* aWorkerPrivate)
1533 : mWorkerPrivate(aWorkerPrivate),
1534 mResponseType(XMLHttpRequestResponseType::Text), mTimeout(0),
1535 mRooted(false), mBackgroundRequest(false), mWithCredentials(false),
1536 mCanceled(false), mMozAnon(false), mMozSystem(false)
1537 {
1538 mWorkerPrivate->AssertIsOnWorkerThread();
1540 SetIsDOMBinding();
1542 mozilla::HoldJSObjects(this);
1543 }
1545 XMLHttpRequest::~XMLHttpRequest()
1546 {
1547 mWorkerPrivate->AssertIsOnWorkerThread();
1549 ReleaseProxy(XHRIsGoingAway);
1551 MOZ_ASSERT(!mRooted);
1553 mozilla::DropJSObjects(this);
1554 }
1556 NS_IMPL_ADDREF_INHERITED(XMLHttpRequest, nsXHREventTarget)
1557 NS_IMPL_RELEASE_INHERITED(XMLHttpRequest, nsXHREventTarget)
1559 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XMLHttpRequest)
1560 NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
1562 NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequest)
1564 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequest,
1565 nsXHREventTarget)
1566 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
1567 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1569 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequest,
1570 nsXHREventTarget)
1571 tmp->ReleaseProxy(XHRIsGoingAway);
1572 NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
1573 tmp->mStateData.mResponse.setUndefined();
1574 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1576 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequest,
1577 nsXHREventTarget)
1578 NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mStateData.mResponse)
1579 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1581 JSObject*
1582 XMLHttpRequest::WrapObject(JSContext* aCx)
1583 {
1584 return XMLHttpRequestBinding_workers::Wrap(aCx, this);
1585 }
1587 // static
1588 already_AddRefed<XMLHttpRequest>
1589 XMLHttpRequest::Constructor(const GlobalObject& aGlobal,
1590 const MozXMLHttpRequestParameters& aParams,
1591 ErrorResult& aRv)
1592 {
1593 JSContext* cx = aGlobal.GetContext();
1594 WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
1595 MOZ_ASSERT(workerPrivate);
1597 nsRefPtr<XMLHttpRequest> xhr = new XMLHttpRequest(workerPrivate);
1599 if (workerPrivate->XHRParamsAllowed()) {
1600 if (aParams.mMozSystem)
1601 xhr->mMozAnon = true;
1602 else
1603 xhr->mMozAnon = aParams.mMozAnon;
1604 xhr->mMozSystem = aParams.mMozSystem;
1605 }
1607 return xhr.forget();
1608 }
1610 void
1611 XMLHttpRequest::ReleaseProxy(ReleaseType aType)
1612 {
1613 // Can't assert that we're on the worker thread here because mWorkerPrivate
1614 // may be gone.
1616 if (mProxy) {
1617 if (aType == XHRIsGoingAway) {
1618 // We're in a GC finalizer, so we can't do a sync call here (and we don't
1619 // need to).
1620 nsRefPtr<AsyncTeardownRunnable> runnable =
1621 new AsyncTeardownRunnable(mProxy);
1622 mProxy = nullptr;
1624 if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
1625 NS_ERROR("Failed to dispatch teardown runnable!");
1626 }
1627 } else {
1628 // This isn't necessary if the worker is going away or the XHR is going
1629 // away.
1630 if (aType == Default) {
1631 // Don't let any more events run.
1632 mProxy->mOuterEventStreamId++;
1633 }
1635 // We need to make a sync call here.
1636 nsRefPtr<SyncTeardownRunnable> runnable =
1637 new SyncTeardownRunnable(mWorkerPrivate, mProxy);
1638 mProxy = nullptr;
1640 if (!runnable->Dispatch(nullptr)) {
1641 NS_ERROR("Failed to dispatch teardown runnable!");
1642 }
1643 }
1644 }
1645 }
1647 void
1648 XMLHttpRequest::MaybePin(ErrorResult& aRv)
1649 {
1650 mWorkerPrivate->AssertIsOnWorkerThread();
1652 if (mRooted) {
1653 return;
1654 }
1656 JSContext* cx = GetCurrentThreadJSContext();
1658 if (!mWorkerPrivate->AddFeature(cx, this)) {
1659 aRv.Throw(NS_ERROR_FAILURE);
1660 return;
1661 }
1663 NS_ADDREF_THIS();
1665 mRooted = true;
1666 }
1668 void
1669 XMLHttpRequest::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
1670 {
1671 mWorkerPrivate->AssertIsOnWorkerThread();
1672 MOZ_ASSERT(mProxy);
1674 mStateData.mReadyState = 4;
1676 if (mProxy->mSeenUploadLoadStart) {
1677 MOZ_ASSERT(mUpload);
1679 DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true,
1680 aRv);
1681 if (aRv.Failed()) {
1682 return;
1683 }
1685 DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("loadend"), true,
1686 aRv);
1687 if (aRv.Failed()) {
1688 return;
1689 }
1691 mProxy->mSeenUploadLoadStart = false;
1692 }
1694 if (mProxy->mSeenLoadStart) {
1695 DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("readystatechange"),
1696 false, aRv);
1697 if (aRv.Failed()) {
1698 return;
1699 }
1701 DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("abort"), false, aRv);
1702 if (aRv.Failed()) {
1703 return;
1704 }
1706 DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("loadend"), false,
1707 aRv);
1708 if (aRv.Failed()) {
1709 return;
1710 }
1712 mProxy->mSeenLoadStart = false;
1713 }
1714 }
1716 void
1717 XMLHttpRequest::DispatchPrematureAbortEvent(EventTarget* aTarget,
1718 const nsAString& aEventType,
1719 bool aUploadTarget,
1720 ErrorResult& aRv)
1721 {
1722 mWorkerPrivate->AssertIsOnWorkerThread();
1723 MOZ_ASSERT(aTarget);
1725 if (!mProxy) {
1726 aRv.Throw(NS_ERROR_FAILURE);
1727 return;
1728 }
1730 nsCOMPtr<nsIDOMEvent> event;
1731 if (aEventType.EqualsLiteral("readystatechange")) {
1732 NS_NewDOMEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
1734 if (event) {
1735 event->InitEvent(aEventType, false, false);
1736 }
1737 }
1738 else {
1739 NS_NewDOMProgressEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
1741 nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
1742 if (progress) {
1743 if (aUploadTarget) {
1744 progress->InitProgressEvent(aEventType, false, false,
1745 mProxy->mLastUploadLengthComputable,
1746 mProxy->mLastUploadLoaded,
1747 mProxy->mLastUploadTotal);
1748 }
1749 else {
1750 progress->InitProgressEvent(aEventType, false, false,
1751 mProxy->mLastLengthComputable,
1752 mProxy->mLastLoaded,
1753 mProxy->mLastTotal);
1754 }
1755 }
1756 }
1758 if (!event) {
1759 aRv.Throw(NS_ERROR_FAILURE);
1760 return;
1761 }
1763 event->SetTrusted(true);
1765 aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
1766 }
1768 void
1769 XMLHttpRequest::Unpin()
1770 {
1771 mWorkerPrivate->AssertIsOnWorkerThread();
1773 MOZ_ASSERT(mRooted, "Mismatched calls to Unpin!");
1775 JSContext* cx = GetCurrentThreadJSContext();
1777 mWorkerPrivate->RemoveFeature(cx, this);
1779 mRooted = false;
1781 NS_RELEASE_THIS();
1782 }
1784 void
1785 XMLHttpRequest::SendInternal(const nsAString& aStringBody,
1786 JSAutoStructuredCloneBuffer&& aBody,
1787 nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
1788 ErrorResult& aRv)
1789 {
1790 mWorkerPrivate->AssertIsOnWorkerThread();
1792 bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
1794 MaybePin(aRv);
1795 if (aRv.Failed()) {
1796 return;
1797 }
1799 AutoUnpinXHR autoUnpin(this);
1800 Maybe<AutoSyncLoopHolder> autoSyncLoop;
1802 nsCOMPtr<nsIEventTarget> syncLoopTarget;
1803 bool isSyncXHR = mProxy->mIsSyncXHR;
1804 if (isSyncXHR) {
1805 autoSyncLoop.construct(mWorkerPrivate);
1806 syncLoopTarget = autoSyncLoop.ref().EventTarget();
1807 }
1809 mProxy->mOuterChannelId++;
1811 JSContext* cx = mWorkerPrivate->GetJSContext();
1813 nsRefPtr<SendRunnable> runnable =
1814 new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
1815 aClonedObjects, syncLoopTarget, hasUploadListeners);
1816 if (!runnable->Dispatch(cx)) {
1817 aRv.Throw(NS_ERROR_FAILURE);
1818 return;
1819 }
1821 if (!isSyncXHR) {
1822 autoUnpin.Clear();
1823 MOZ_ASSERT(autoSyncLoop.empty());
1824 return;
1825 }
1827 autoUnpin.Clear();
1829 if (!autoSyncLoop.ref().Run()) {
1830 aRv.Throw(NS_ERROR_FAILURE);
1831 }
1832 }
1834 bool
1835 XMLHttpRequest::Notify(JSContext* aCx, Status aStatus)
1836 {
1837 mWorkerPrivate->AssertIsOnWorkerThread();
1838 MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
1840 if (aStatus >= Canceling && !mCanceled) {
1841 mCanceled = true;
1842 ReleaseProxy(WorkerIsGoingAway);
1843 }
1845 return true;
1846 }
1848 void
1849 XMLHttpRequest::Open(const nsACString& aMethod, const nsAString& aUrl,
1850 bool aAsync, const Optional<nsAString>& aUser,
1851 const Optional<nsAString>& aPassword, ErrorResult& aRv)
1852 {
1853 mWorkerPrivate->AssertIsOnWorkerThread();
1855 if (mCanceled) {
1856 aRv.Throw(UNCATCHABLE_EXCEPTION);
1857 return;
1858 }
1860 if (mProxy) {
1861 MaybeDispatchPrematureAbortEvents(aRv);
1862 if (aRv.Failed()) {
1863 return;
1864 }
1865 }
1866 else {
1867 mProxy = new Proxy(this, mMozAnon, mMozSystem);
1868 }
1870 mProxy->mOuterEventStreamId++;
1872 nsRefPtr<OpenRunnable> runnable =
1873 new OpenRunnable(mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
1874 mBackgroundRequest, mWithCredentials,
1875 mTimeout);
1877 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
1878 ReleaseProxy();
1879 aRv.Throw(NS_ERROR_FAILURE);
1880 return;
1881 }
1883 mProxy->mIsSyncXHR = !aAsync;
1884 }
1886 void
1887 XMLHttpRequest::SetRequestHeader(const nsACString& aHeader,
1888 const nsACString& aValue, ErrorResult& aRv)
1889 {
1890 mWorkerPrivate->AssertIsOnWorkerThread();
1892 if (mCanceled) {
1893 aRv.Throw(UNCATCHABLE_EXCEPTION);
1894 return;
1895 }
1897 if (!mProxy) {
1898 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1899 return;
1900 }
1902 nsRefPtr<SetRequestHeaderRunnable> runnable =
1903 new SetRequestHeaderRunnable(mWorkerPrivate, mProxy, aHeader, aValue);
1904 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
1905 aRv.Throw(NS_ERROR_FAILURE);
1906 return;
1907 }
1908 }
1910 void
1911 XMLHttpRequest::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
1912 {
1913 mWorkerPrivate->AssertIsOnWorkerThread();
1915 if (mCanceled) {
1916 aRv.Throw(UNCATCHABLE_EXCEPTION);
1917 return;
1918 }
1920 mTimeout = aTimeout;
1922 if (!mProxy) {
1923 // Open may not have been called yet, in which case we'll handle the
1924 // timeout in OpenRunnable.
1925 return;
1926 }
1928 nsRefPtr<SetTimeoutRunnable> runnable =
1929 new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout);
1930 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
1931 aRv.Throw(NS_ERROR_FAILURE);
1932 return;
1933 }
1934 }
1936 void
1937 XMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
1938 {
1939 mWorkerPrivate->AssertIsOnWorkerThread();
1941 if (mCanceled) {
1942 aRv.Throw(UNCATCHABLE_EXCEPTION);
1943 return;
1944 }
1946 mWithCredentials = aWithCredentials;
1948 if (!mProxy) {
1949 // Open may not have been called yet, in which case we'll handle the
1950 // credentials in OpenRunnable.
1951 return;
1952 }
1954 nsRefPtr<SetWithCredentialsRunnable> runnable =
1955 new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials);
1956 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
1957 aRv.Throw(NS_ERROR_FAILURE);
1958 return;
1959 }
1960 }
1962 void
1963 XMLHttpRequest::SetMozBackgroundRequest(bool aBackgroundRequest,
1964 ErrorResult& aRv)
1965 {
1966 mWorkerPrivate->AssertIsOnWorkerThread();
1968 if (mCanceled) {
1969 aRv.Throw(UNCATCHABLE_EXCEPTION);
1970 return;
1971 }
1973 mBackgroundRequest = aBackgroundRequest;
1975 if (!mProxy) {
1976 // Open may not have been called yet, in which case we'll handle the
1977 // background request in OpenRunnable.
1978 return;
1979 }
1981 nsRefPtr<SetBackgroundRequestRunnable> runnable =
1982 new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy,
1983 aBackgroundRequest);
1984 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
1985 aRv.Throw(NS_ERROR_FAILURE);
1986 return;
1987 }
1988 }
1990 XMLHttpRequestUpload*
1991 XMLHttpRequest::GetUpload(ErrorResult& aRv)
1992 {
1993 mWorkerPrivate->AssertIsOnWorkerThread();
1995 if (mCanceled) {
1996 aRv.Throw(UNCATCHABLE_EXCEPTION);
1997 return nullptr;
1998 }
2000 if (!mUpload) {
2001 mUpload = XMLHttpRequestUpload::Create(this);
2003 if (!mUpload) {
2004 aRv.Throw(NS_ERROR_FAILURE);
2005 return nullptr;
2006 }
2007 }
2009 return mUpload;
2010 }
2012 void
2013 XMLHttpRequest::Send(ErrorResult& aRv)
2014 {
2015 mWorkerPrivate->AssertIsOnWorkerThread();
2017 if (mCanceled) {
2018 aRv.Throw(UNCATCHABLE_EXCEPTION);
2019 return;
2020 }
2022 if (!mProxy) {
2023 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2024 return;
2025 }
2027 // Nothing to clone.
2028 JSAutoStructuredCloneBuffer buffer;
2029 nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
2031 SendInternal(NullString(), Move(buffer), clonedObjects, aRv);
2032 }
2034 void
2035 XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
2036 {
2037 mWorkerPrivate->AssertIsOnWorkerThread();
2039 if (mCanceled) {
2040 aRv.Throw(UNCATCHABLE_EXCEPTION);
2041 return;
2042 }
2044 if (!mProxy) {
2045 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2046 return;
2047 }
2049 // Nothing to clone.
2050 JSAutoStructuredCloneBuffer buffer;
2051 nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
2053 SendInternal(aBody, Move(buffer), clonedObjects, aRv);
2054 }
2056 void
2057 XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
2058 {
2059 JSContext* cx = mWorkerPrivate->GetJSContext();
2061 MOZ_ASSERT(aBody);
2063 mWorkerPrivate->AssertIsOnWorkerThread();
2065 if (mCanceled) {
2066 aRv.Throw(UNCATCHABLE_EXCEPTION);
2067 return;
2068 }
2070 if (!mProxy) {
2071 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2072 return;
2073 }
2075 JS::Rooted<JS::Value> valToClone(cx);
2076 if (JS_IsArrayBufferObject(aBody) || JS_IsArrayBufferViewObject(aBody) ||
2077 file::GetDOMBlobFromJSObject(aBody)) {
2078 valToClone.setObject(*aBody);
2079 }
2080 else {
2081 JS::Rooted<JS::Value> obj(cx, JS::ObjectValue(*aBody));
2082 JSString* bodyStr = JS::ToString(cx, obj);
2083 if (!bodyStr) {
2084 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2085 return;
2086 }
2087 valToClone.setString(bodyStr);
2088 }
2090 JSStructuredCloneCallbacks* callbacks =
2091 mWorkerPrivate->IsChromeWorker() ?
2092 ChromeWorkerStructuredCloneCallbacks(false) :
2093 WorkerStructuredCloneCallbacks(false);
2095 nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
2097 JSAutoStructuredCloneBuffer buffer;
2098 if (!buffer.write(cx, valToClone, callbacks, &clonedObjects)) {
2099 aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
2100 return;
2101 }
2103 SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
2104 }
2106 void
2107 XMLHttpRequest::Send(const ArrayBuffer& aBody, ErrorResult& aRv)
2108 {
2109 JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
2110 return Send(obj, aRv);
2111 }
2113 void
2114 XMLHttpRequest::Send(const ArrayBufferView& aBody, ErrorResult& aRv)
2115 {
2116 JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
2117 return Send(obj, aRv);
2118 }
2120 void
2121 XMLHttpRequest::SendAsBinary(const nsAString& aBody, ErrorResult& aRv)
2122 {
2123 NS_NOTYETIMPLEMENTED("Implement me!");
2124 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
2125 return;
2126 }
2128 void
2129 XMLHttpRequest::Abort(ErrorResult& aRv)
2130 {
2131 mWorkerPrivate->AssertIsOnWorkerThread();
2133 if (mCanceled) {
2134 aRv.Throw(UNCATCHABLE_EXCEPTION);
2135 }
2137 if (!mProxy) {
2138 return;
2139 }
2141 MaybeDispatchPrematureAbortEvents(aRv);
2142 if (aRv.Failed()) {
2143 return;
2144 }
2146 mProxy->mOuterEventStreamId++;
2148 nsRefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
2149 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
2150 aRv.Throw(NS_ERROR_FAILURE);
2151 return;
2152 }
2153 }
2155 void
2156 XMLHttpRequest::GetResponseHeader(const nsACString& aHeader,
2157 nsACString& aResponseHeader, ErrorResult& aRv)
2158 {
2159 mWorkerPrivate->AssertIsOnWorkerThread();
2161 if (mCanceled) {
2162 aRv.Throw(UNCATCHABLE_EXCEPTION);
2163 return;
2164 }
2166 if (!mProxy) {
2167 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2168 return;
2169 }
2171 nsCString responseHeader;
2172 nsRefPtr<GetResponseHeaderRunnable> runnable =
2173 new GetResponseHeaderRunnable(mWorkerPrivate, mProxy, aHeader,
2174 responseHeader);
2175 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
2176 aRv.Throw(NS_ERROR_FAILURE);
2177 return;
2178 }
2179 aResponseHeader = responseHeader;
2180 }
2182 void
2183 XMLHttpRequest::GetAllResponseHeaders(nsACString& aResponseHeaders,
2184 ErrorResult& aRv)
2185 {
2186 mWorkerPrivate->AssertIsOnWorkerThread();
2188 if (mCanceled) {
2189 aRv.Throw(UNCATCHABLE_EXCEPTION);
2190 return;
2191 }
2193 if (!mProxy) {
2194 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2195 return;
2196 }
2198 nsCString responseHeaders;
2199 nsRefPtr<GetAllResponseHeadersRunnable> runnable =
2200 new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy, responseHeaders);
2201 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
2202 aRv.Throw(NS_ERROR_FAILURE);
2203 return;
2204 }
2206 aResponseHeaders = responseHeaders;
2207 }
2209 void
2210 XMLHttpRequest::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
2211 {
2212 mWorkerPrivate->AssertIsOnWorkerThread();
2214 if (mCanceled) {
2215 aRv.Throw(UNCATCHABLE_EXCEPTION);
2216 return;
2217 }
2219 // We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
2220 // can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
2221 // non-racy way until the XHR state machine actually runs on this thread
2222 // (bug 671047). For now we're going to let this work only if the Send()
2223 // method has not been called.
2224 if (!mProxy || SendInProgress()) {
2225 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2226 return;
2227 }
2229 nsRefPtr<OverrideMimeTypeRunnable> runnable =
2230 new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
2231 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
2232 aRv.Throw(NS_ERROR_FAILURE);
2233 return;
2234 }
2235 }
2237 void
2238 XMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aResponseType,
2239 ErrorResult& aRv)
2240 {
2241 mWorkerPrivate->AssertIsOnWorkerThread();
2243 if (mCanceled) {
2244 aRv.Throw(UNCATCHABLE_EXCEPTION);
2245 return;
2246 }
2248 if (!mProxy || SendInProgress()) {
2249 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
2250 return;
2251 }
2253 // "document" is fine for the main thread but not for a worker. Short-circuit
2254 // that here.
2255 if (aResponseType == XMLHttpRequestResponseType::Document) {
2256 return;
2257 }
2259 nsString responseType;
2260 ConvertResponseTypeToString(aResponseType, responseType);
2262 nsRefPtr<SetResponseTypeRunnable> runnable =
2263 new SetResponseTypeRunnable(mWorkerPrivate, mProxy, responseType);
2264 if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
2265 aRv.Throw(NS_ERROR_FAILURE);
2266 return;
2267 }
2269 nsString acceptedResponseTypeString;
2270 runnable->GetResponseType(acceptedResponseTypeString);
2272 mResponseType = ConvertStringToResponseType(acceptedResponseTypeString);
2273 }
2275 void
2276 XMLHttpRequest::GetResponse(JSContext* /* unused */,
2277 JS::MutableHandle<JS::Value> aResponse,
2278 ErrorResult& aRv)
2279 {
2280 if (NS_SUCCEEDED(mStateData.mResponseTextResult) &&
2281 JSVAL_IS_VOID(mStateData.mResponse)) {
2282 MOZ_ASSERT(mStateData.mResponseText.Length());
2283 MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult));
2285 JSString* str =
2286 JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(),
2287 mStateData.mResponseText.get(),
2288 mStateData.mResponseText.Length());
2289 if (!str) {
2290 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
2291 return;
2292 }
2294 mStateData.mResponse = STRING_TO_JSVAL(str);
2295 }
2297 JS::ExposeValueToActiveJS(mStateData.mResponse);
2298 aRv = mStateData.mResponseResult;
2299 aResponse.set(mStateData.mResponse);
2300 }
2302 void
2303 XMLHttpRequest::GetResponseText(nsAString& aResponseText, ErrorResult& aRv)
2304 {
2305 aRv = mStateData.mResponseTextResult;
2306 aResponseText = mStateData.mResponseText;
2307 }
2309 void
2310 XMLHttpRequest::UpdateState(const StateData& aStateData)
2311 {
2312 mStateData = aStateData;
2313 }