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