Coverage Report

Created: 2018-09-25 14:53

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