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