dom/workers/XMLHttpRequest.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/workers/XMLHttpRequest.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2313 @@
     1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "XMLHttpRequest.h"
    1.10 +
    1.11 +#include "nsIDOMEvent.h"
    1.12 +#include "nsIDOMEventListener.h"
    1.13 +#include "nsIDOMProgressEvent.h"
    1.14 +#include "nsIRunnable.h"
    1.15 +#include "nsIVariant.h"
    1.16 +#include "nsIXMLHttpRequest.h"
    1.17 +#include "nsIXPConnect.h"
    1.18 +
    1.19 +#include "jsfriendapi.h"
    1.20 +#include "mozilla/ArrayUtils.h"
    1.21 +#include "mozilla/dom/Exceptions.h"
    1.22 +#include "nsComponentManagerUtils.h"
    1.23 +#include "nsContentUtils.h"
    1.24 +#include "nsCxPusher.h"
    1.25 +#include "nsJSUtils.h"
    1.26 +#include "nsThreadUtils.h"
    1.27 +
    1.28 +#include "File.h"
    1.29 +#include "RuntimeService.h"
    1.30 +#include "WorkerPrivate.h"
    1.31 +#include "WorkerRunnable.h"
    1.32 +#include "XMLHttpRequestUpload.h"
    1.33 +
    1.34 +using namespace mozilla;
    1.35 +
    1.36 +using namespace mozilla::dom;
    1.37 +USING_WORKERS_NAMESPACE
    1.38 +
    1.39 +// XXX Need to figure this out...
    1.40 +#define UNCATCHABLE_EXCEPTION NS_ERROR_OUT_OF_MEMORY
    1.41 +
    1.42 +/**
    1.43 + *  XMLHttpRequest in workers
    1.44 + *
    1.45 + *  XHR in workers is implemented by proxying calls/events/etc between the
    1.46 + *  worker thread and an nsXMLHttpRequest on the main thread.  The glue
    1.47 + *  object here is the Proxy, which lives on both threads.  All other objects
    1.48 + *  live on either the main thread (the nsXMLHttpRequest) or the worker thread
    1.49 + *  (the worker and XHR private objects).
    1.50 + *
    1.51 + *  The main thread XHR is always operated in async mode, even for sync XHR
    1.52 + *  in workers.  Calls made on the worker thread are proxied to the main thread
    1.53 + *  synchronously (meaning the worker thread is blocked until the call
    1.54 + *  returns).  Each proxied call spins up a sync queue, which captures any
    1.55 + *  synchronously dispatched events and ensures that they run synchronously
    1.56 + *  on the worker as well.  Asynchronously dispatched events are posted to the
    1.57 + *  worker thread to run asynchronously.  Some of the XHR state is mirrored on
    1.58 + *  the worker thread to avoid needing a cross-thread call on every property
    1.59 + *  access.
    1.60 + *
    1.61 + *  The XHR private is stored in the private slot of the XHR JSObject on the
    1.62 + *  worker thread.  It is destroyed when that JSObject is GCd.  The private
    1.63 + *  roots its JSObject while network activity is in progress.  It also
    1.64 + *  adds itself as a feature to the worker to give itself a chance to clean up
    1.65 + *  if the worker goes away during an XHR call.  It is important that the
    1.66 + *  rooting and feature registration (collectively called pinning) happens at
    1.67 + *  the proper times.  If we pin for too long we can cause memory leaks or even
    1.68 + *  shutdown hangs.  If we don't pin for long enough we introduce a GC hazard.
    1.69 + *
    1.70 + *  The XHR is pinned from the time Send is called to roughly the time loadend
    1.71 + *  is received.  There are some complications involved with Abort and XHR
    1.72 + *  reuse.  We maintain a counter on the main thread of how many times Send was
    1.73 + *  called on this XHR, and we decrement the counter every time we receive a
    1.74 + *  loadend event.  When the counter reaches zero we dispatch a runnable to the
    1.75 + *  worker thread to unpin the XHR.  We only decrement the counter if the 
    1.76 + *  dispatch was successful, because the worker may no longer be accepting
    1.77 + *  regular runnables.  In the event that we reach Proxy::Teardown and there
    1.78 + *  the outstanding Send count is still non-zero, we dispatch a control
    1.79 + *  runnable which is guaranteed to run.
    1.80 + *
    1.81 + *  NB: Some of this could probably be simplified now that we have the
    1.82 + *  inner/outer channel ids.
    1.83 + */
    1.84 +
    1.85 +BEGIN_WORKERS_NAMESPACE
    1.86 +
    1.87 +class Proxy MOZ_FINAL : public nsIDOMEventListener
    1.88 +{
    1.89 +public:
    1.90 +  // Read on multiple threads.
    1.91 +  WorkerPrivate* mWorkerPrivate;
    1.92 +  XMLHttpRequest* mXMLHttpRequestPrivate;
    1.93 +
    1.94 +  // XHR Params:
    1.95 +  bool mMozAnon;
    1.96 +  bool mMozSystem;
    1.97 +
    1.98 +  // Only touched on the main thread.
    1.99 +  nsRefPtr<nsXMLHttpRequest> mXHR;
   1.100 +  nsCOMPtr<nsIXMLHttpRequestUpload> mXHRUpload;
   1.101 +  nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   1.102 +  nsCOMPtr<nsIEventTarget> mSyncEventResponseTarget;
   1.103 +  uint32_t mInnerEventStreamId;
   1.104 +  uint32_t mInnerChannelId;
   1.105 +  uint32_t mOutstandingSendCount;
   1.106 +
   1.107 +  // Only touched on the worker thread.
   1.108 +  uint32_t mOuterEventStreamId;
   1.109 +  uint32_t mOuterChannelId;
   1.110 +  uint64_t mLastLoaded;
   1.111 +  uint64_t mLastTotal;
   1.112 +  uint64_t mLastUploadLoaded;
   1.113 +  uint64_t mLastUploadTotal;
   1.114 +  bool mIsSyncXHR;
   1.115 +  bool mLastLengthComputable;
   1.116 +  bool mLastUploadLengthComputable;
   1.117 +  bool mSeenLoadStart;
   1.118 +  bool mSeenUploadLoadStart;
   1.119 +
   1.120 +  // Only touched on the main thread.
   1.121 +  bool mUploadEventListenersAttached;
   1.122 +  bool mMainThreadSeenLoadStart;
   1.123 +  bool mInOpen;
   1.124 +
   1.125 +public:
   1.126 +  Proxy(XMLHttpRequest* aXHRPrivate, bool aMozAnon, bool aMozSystem)
   1.127 +  : mWorkerPrivate(nullptr), mXMLHttpRequestPrivate(aXHRPrivate),
   1.128 +    mMozAnon(aMozAnon), mMozSystem(aMozSystem),
   1.129 +    mInnerEventStreamId(0), mInnerChannelId(0), mOutstandingSendCount(0),
   1.130 +    mOuterEventStreamId(0), mOuterChannelId(0), mLastLoaded(0), mLastTotal(0),
   1.131 +    mLastUploadLoaded(0), mLastUploadTotal(0), mIsSyncXHR(false),
   1.132 +    mLastLengthComputable(false), mLastUploadLengthComputable(false),
   1.133 +    mSeenLoadStart(false), mSeenUploadLoadStart(false),
   1.134 +    mUploadEventListenersAttached(false), mMainThreadSeenLoadStart(false),
   1.135 +    mInOpen(false)
   1.136 +  { }
   1.137 +
   1.138 +  NS_DECL_THREADSAFE_ISUPPORTS
   1.139 +  NS_DECL_NSIDOMEVENTLISTENER
   1.140 +
   1.141 +  bool
   1.142 +  Init();
   1.143 +
   1.144 +  void
   1.145 +  Teardown();
   1.146 +
   1.147 +  bool
   1.148 +  AddRemoveEventListeners(bool aUpload, bool aAdd);
   1.149 +
   1.150 +  void
   1.151 +  Reset()
   1.152 +  {
   1.153 +    AssertIsOnMainThread();
   1.154 +
   1.155 +    if (mUploadEventListenersAttached) {
   1.156 +      AddRemoveEventListeners(true, false);
   1.157 +    }
   1.158 +  }
   1.159 +
   1.160 +  already_AddRefed<nsIEventTarget>
   1.161 +  GetEventTarget()
   1.162 +  {
   1.163 +    AssertIsOnMainThread();
   1.164 +
   1.165 +    nsCOMPtr<nsIEventTarget> target = mSyncEventResponseTarget ?
   1.166 +                                      mSyncEventResponseTarget :
   1.167 +                                      mSyncLoopTarget;
   1.168 +    return target.forget();
   1.169 +  }
   1.170 +
   1.171 +private:
   1.172 +  ~Proxy()
   1.173 +  {
   1.174 +    MOZ_ASSERT(!mXHR);
   1.175 +    MOZ_ASSERT(!mXHRUpload);
   1.176 +    MOZ_ASSERT(!mOutstandingSendCount);
   1.177 +  }
   1.178 +};
   1.179 +
   1.180 +END_WORKERS_NAMESPACE
   1.181 +
   1.182 +namespace {
   1.183 +
   1.184 +inline void
   1.185 +ConvertResponseTypeToString(XMLHttpRequestResponseType aType,
   1.186 +                            nsString& aString)
   1.187 +{
   1.188 +  using namespace
   1.189 +    mozilla::dom::XMLHttpRequestResponseTypeValues;
   1.190 +
   1.191 +  size_t index = static_cast<size_t>(aType);
   1.192 +  MOZ_ASSERT(index < ArrayLength(strings), "Codegen gave us a bad value!");
   1.193 +
   1.194 +  aString.AssignASCII(strings[index].value, strings[index].length);
   1.195 +}
   1.196 +
   1.197 +inline XMLHttpRequestResponseType
   1.198 +ConvertStringToResponseType(const nsAString& aString)
   1.199 +{
   1.200 +  using namespace
   1.201 +    mozilla::dom::XMLHttpRequestResponseTypeValues;
   1.202 +
   1.203 +  for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
   1.204 +    if (aString.EqualsASCII(strings[index].value, strings[index].length)) {
   1.205 +      return static_cast<XMLHttpRequestResponseType>(index);
   1.206 +    }
   1.207 +  }
   1.208 +
   1.209 +  MOZ_ASSUME_UNREACHABLE("Don't know anything about this response type!");
   1.210 +}
   1.211 +
   1.212 +enum
   1.213 +{
   1.214 +  STRING_abort = 0,
   1.215 +  STRING_error,
   1.216 +  STRING_load,
   1.217 +  STRING_loadstart,
   1.218 +  STRING_progress,
   1.219 +  STRING_timeout,
   1.220 +  STRING_readystatechange,
   1.221 +  STRING_loadend,
   1.222 +
   1.223 +  STRING_COUNT,
   1.224 +
   1.225 +  STRING_LAST_XHR = STRING_loadend,
   1.226 +  STRING_LAST_EVENTTARGET = STRING_timeout
   1.227 +};
   1.228 +
   1.229 +static_assert(STRING_LAST_XHR >= STRING_LAST_EVENTTARGET, "Bad string setup!");
   1.230 +static_assert(STRING_LAST_XHR == STRING_COUNT - 1, "Bad string setup!");
   1.231 +
   1.232 +const char* const sEventStrings[] = {
   1.233 +  // nsIXMLHttpRequestEventTarget event types, supported by both XHR and Upload.
   1.234 +  "abort",
   1.235 +  "error",
   1.236 +  "load",
   1.237 +  "loadstart",
   1.238 +  "progress",
   1.239 +  "timeout",
   1.240 +
   1.241 +  // nsIXMLHttpRequest event types, supported only by XHR.
   1.242 +  "readystatechange",
   1.243 +  "loadend",
   1.244 +};
   1.245 +
   1.246 +static_assert(MOZ_ARRAY_LENGTH(sEventStrings) == STRING_COUNT,
   1.247 +              "Bad string count!");
   1.248 +
   1.249 +class MainThreadProxyRunnable : public MainThreadWorkerSyncRunnable
   1.250 +{
   1.251 +protected:
   1.252 +  nsRefPtr<Proxy> mProxy;
   1.253 +
   1.254 +  MainThreadProxyRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
   1.255 +  : MainThreadWorkerSyncRunnable(aWorkerPrivate, aProxy->GetEventTarget()),
   1.256 +    mProxy(aProxy)
   1.257 +  {
   1.258 +    MOZ_ASSERT(aProxy);
   1.259 +  }
   1.260 +
   1.261 +  virtual ~MainThreadProxyRunnable()
   1.262 +  { }
   1.263 +};
   1.264 +
   1.265 +class XHRUnpinRunnable MOZ_FINAL : public MainThreadWorkerControlRunnable
   1.266 +{
   1.267 +  XMLHttpRequest* mXMLHttpRequestPrivate;
   1.268 +
   1.269 +public:
   1.270 +  XHRUnpinRunnable(WorkerPrivate* aWorkerPrivate,
   1.271 +                   XMLHttpRequest* aXHRPrivate)
   1.272 +  : MainThreadWorkerControlRunnable(aWorkerPrivate),
   1.273 +    mXMLHttpRequestPrivate(aXHRPrivate)
   1.274 +  {
   1.275 +    MOZ_ASSERT(aXHRPrivate);
   1.276 +  }
   1.277 +
   1.278 +private:
   1.279 +  ~XHRUnpinRunnable()
   1.280 +  { }
   1.281 +
   1.282 +  bool
   1.283 +  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   1.284 +  {
   1.285 +    mXMLHttpRequestPrivate->Unpin();
   1.286 +
   1.287 +    return true;
   1.288 +  }
   1.289 +};
   1.290 +
   1.291 +class AsyncTeardownRunnable MOZ_FINAL : public nsRunnable
   1.292 +{
   1.293 +  nsRefPtr<Proxy> mProxy;
   1.294 +
   1.295 +public:
   1.296 +  AsyncTeardownRunnable(Proxy* aProxy)
   1.297 +  : mProxy(aProxy)
   1.298 +  {
   1.299 +    MOZ_ASSERT(aProxy);
   1.300 +  }
   1.301 +
   1.302 +  NS_DECL_ISUPPORTS_INHERITED
   1.303 +
   1.304 +private:
   1.305 +  ~AsyncTeardownRunnable()
   1.306 +  { }
   1.307 +
   1.308 +  NS_IMETHOD
   1.309 +  Run() MOZ_OVERRIDE
   1.310 +  {
   1.311 +    AssertIsOnMainThread();
   1.312 +
   1.313 +    mProxy->Teardown();
   1.314 +    mProxy = nullptr;
   1.315 +
   1.316 +    return NS_OK;
   1.317 +  }
   1.318 +};
   1.319 +
   1.320 +class LoadStartDetectionRunnable MOZ_FINAL : public nsRunnable,
   1.321 +                                             public nsIDOMEventListener
   1.322 +{
   1.323 +  WorkerPrivate* mWorkerPrivate;
   1.324 +  nsRefPtr<Proxy> mProxy;
   1.325 +  nsRefPtr<nsXMLHttpRequest> mXHR;
   1.326 +  XMLHttpRequest* mXMLHttpRequestPrivate;
   1.327 +  nsString mEventType;
   1.328 +  uint32_t mChannelId;
   1.329 +  bool mReceivedLoadStart;
   1.330 +
   1.331 +  class ProxyCompleteRunnable MOZ_FINAL : public MainThreadProxyRunnable
   1.332 +  {
   1.333 +    XMLHttpRequest* mXMLHttpRequestPrivate;
   1.334 +    uint32_t mChannelId;
   1.335 +
   1.336 +  public:
   1.337 +    ProxyCompleteRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.338 +                          XMLHttpRequest* aXHRPrivate, uint32_t aChannelId)
   1.339 +    : MainThreadProxyRunnable(aWorkerPrivate, aProxy),
   1.340 +      mXMLHttpRequestPrivate(aXHRPrivate), mChannelId(aChannelId)
   1.341 +    { }
   1.342 +
   1.343 +  private:
   1.344 +    ~ProxyCompleteRunnable()
   1.345 +    { }
   1.346 +
   1.347 +    virtual bool
   1.348 +    WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   1.349 +    {
   1.350 +      if (mChannelId != mProxy->mOuterChannelId) {
   1.351 +        // Threads raced, this event is now obsolete.
   1.352 +        return true;
   1.353 +      }
   1.354 +
   1.355 +      if (mSyncLoopTarget) {
   1.356 +        aWorkerPrivate->StopSyncLoop(mSyncLoopTarget, true);
   1.357 +      }
   1.358 +
   1.359 +      mXMLHttpRequestPrivate->Unpin();
   1.360 +
   1.361 +      return true;
   1.362 +    }
   1.363 +
   1.364 +    NS_IMETHOD
   1.365 +    Cancel() MOZ_OVERRIDE
   1.366 +    {
   1.367 +      // This must run!
   1.368 +      nsresult rv = MainThreadProxyRunnable::Cancel();
   1.369 +      nsresult rv2 = Run();
   1.370 +      return NS_FAILED(rv) ? rv : rv2;
   1.371 +    }
   1.372 +  };
   1.373 +
   1.374 +public:
   1.375 +  LoadStartDetectionRunnable(Proxy* aProxy, XMLHttpRequest* aXHRPrivate)
   1.376 +  : mWorkerPrivate(aProxy->mWorkerPrivate), mProxy(aProxy), mXHR(aProxy->mXHR),
   1.377 +    mXMLHttpRequestPrivate(aXHRPrivate), mChannelId(mProxy->mInnerChannelId),
   1.378 +    mReceivedLoadStart(false)
   1.379 +  {
   1.380 +    AssertIsOnMainThread();
   1.381 +    mEventType.AssignWithConversion(sEventStrings[STRING_loadstart]);
   1.382 +  }
   1.383 +
   1.384 +  NS_DECL_ISUPPORTS_INHERITED
   1.385 +  NS_DECL_NSIRUNNABLE
   1.386 +  NS_DECL_NSIDOMEVENTLISTENER
   1.387 +
   1.388 +  bool
   1.389 +  RegisterAndDispatch()
   1.390 +  {
   1.391 +    AssertIsOnMainThread();
   1.392 +
   1.393 +    if (NS_FAILED(mXHR->AddEventListener(mEventType, this, false, false, 2))) {
   1.394 +      NS_WARNING("Failed to add event listener!");
   1.395 +      return false;
   1.396 +    }
   1.397 +
   1.398 +    return NS_SUCCEEDED(NS_DispatchToCurrentThread(this));
   1.399 +  }
   1.400 +
   1.401 +private:
   1.402 +  ~LoadStartDetectionRunnable()
   1.403 +  {
   1.404 +    AssertIsOnMainThread();
   1.405 +    }
   1.406 +};
   1.407 +
   1.408 +class EventRunnable MOZ_FINAL : public MainThreadProxyRunnable
   1.409 +{
   1.410 +  nsString mType;
   1.411 +  nsString mResponseType;
   1.412 +  JSAutoStructuredCloneBuffer mResponseBuffer;
   1.413 +  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
   1.414 +  JS::Heap<JS::Value> mResponse;
   1.415 +  nsString mResponseText;
   1.416 +  nsCString mStatusText;
   1.417 +  uint64_t mLoaded;
   1.418 +  uint64_t mTotal;
   1.419 +  uint32_t mEventStreamId;
   1.420 +  uint32_t mStatus;
   1.421 +  uint16_t mReadyState;
   1.422 +  bool mUploadEvent;
   1.423 +  bool mProgressEvent;
   1.424 +  bool mLengthComputable;
   1.425 +  nsresult mResponseTextResult;
   1.426 +  nsresult mStatusResult;
   1.427 +  nsresult mResponseResult;
   1.428 +
   1.429 +public:
   1.430 +  class StateDataAutoRooter : private JS::CustomAutoRooter
   1.431 +  {
   1.432 +    XMLHttpRequest::StateData* mStateData;
   1.433 +    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   1.434 +
   1.435 +  public:
   1.436 +    explicit StateDataAutoRooter(JSContext* aCx, XMLHttpRequest::StateData* aData
   1.437 +                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   1.438 +    : CustomAutoRooter(aCx), mStateData(aData)
   1.439 +    {
   1.440 +      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   1.441 +    }
   1.442 +
   1.443 +  private:
   1.444 +    virtual void trace(JSTracer* aTrc)
   1.445 +    {
   1.446 +      JS_CallHeapValueTracer(aTrc, &mStateData->mResponse,
   1.447 +                             "XMLHttpRequest::StateData::mResponse");
   1.448 +    }
   1.449 +  };
   1.450 +
   1.451 +  EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType,
   1.452 +                bool aLengthComputable, uint64_t aLoaded, uint64_t aTotal)
   1.453 +  : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
   1.454 +    mResponse(JSVAL_VOID), mLoaded(aLoaded), mTotal(aTotal),
   1.455 +    mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
   1.456 +    mUploadEvent(aUploadEvent), mProgressEvent(true),
   1.457 +    mLengthComputable(aLengthComputable), mResponseTextResult(NS_OK),
   1.458 +    mStatusResult(NS_OK), mResponseResult(NS_OK)
   1.459 +  { }
   1.460 +
   1.461 +  EventRunnable(Proxy* aProxy, bool aUploadEvent, const nsString& aType)
   1.462 +  : MainThreadProxyRunnable(aProxy->mWorkerPrivate, aProxy), mType(aType),
   1.463 +    mResponse(JSVAL_VOID), mLoaded(0), mTotal(0),
   1.464 +    mEventStreamId(aProxy->mInnerEventStreamId), mStatus(0), mReadyState(0),
   1.465 +    mUploadEvent(aUploadEvent), mProgressEvent(false), mLengthComputable(0),
   1.466 +    mResponseTextResult(NS_OK), mStatusResult(NS_OK), mResponseResult(NS_OK)
   1.467 +  { }
   1.468 +
   1.469 +private:
   1.470 +  ~EventRunnable()
   1.471 +  { }
   1.472 +
   1.473 +  virtual bool
   1.474 +  PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
   1.475 +
   1.476 +  virtual bool
   1.477 +  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE;
   1.478 +};
   1.479 +
   1.480 +class WorkerThreadProxySyncRunnable : public nsRunnable
   1.481 +{
   1.482 +protected:
   1.483 +  WorkerPrivate* mWorkerPrivate;
   1.484 +  nsRefPtr<Proxy> mProxy;
   1.485 +  nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   1.486 +
   1.487 +private:
   1.488 +  class ResponseRunnable MOZ_FINAL: public MainThreadStopSyncLoopRunnable
   1.489 +  {
   1.490 +    nsRefPtr<Proxy> mProxy;
   1.491 +    nsresult mErrorCode;
   1.492 +
   1.493 +  public:
   1.494 +    ResponseRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.495 +                     nsresult aErrorCode)
   1.496 +    : MainThreadStopSyncLoopRunnable(aWorkerPrivate, aProxy->GetEventTarget(),
   1.497 +                                     NS_SUCCEEDED(aErrorCode)),
   1.498 +      mProxy(aProxy), mErrorCode(aErrorCode)
   1.499 +    {
   1.500 +      MOZ_ASSERT(aProxy);
   1.501 +    }
   1.502 +
   1.503 +  private:
   1.504 +    ~ResponseRunnable()
   1.505 +    { }
   1.506 +
   1.507 +    virtual void
   1.508 +    MaybeSetException(JSContext* aCx) MOZ_OVERRIDE
   1.509 +    {
   1.510 +      MOZ_ASSERT(NS_FAILED(mErrorCode));
   1.511 +
   1.512 +      Throw(aCx, mErrorCode);
   1.513 +    }
   1.514 +  };
   1.515 +
   1.516 +public:
   1.517 +  WorkerThreadProxySyncRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
   1.518 +  : mWorkerPrivate(aWorkerPrivate), mProxy(aProxy)
   1.519 +  {
   1.520 +    MOZ_ASSERT(aWorkerPrivate);
   1.521 +    MOZ_ASSERT(aProxy);
   1.522 +    aWorkerPrivate->AssertIsOnWorkerThread();
   1.523 +  }
   1.524 +
   1.525 +  NS_DECL_ISUPPORTS_INHERITED
   1.526 +
   1.527 +  bool
   1.528 +  Dispatch(JSContext* aCx)
   1.529 +  {
   1.530 +    mWorkerPrivate->AssertIsOnWorkerThread();
   1.531 +
   1.532 +    AutoSyncLoopHolder syncLoop(mWorkerPrivate);
   1.533 +    mSyncLoopTarget = syncLoop.EventTarget();
   1.534 +
   1.535 +    if (NS_FAILED(NS_DispatchToMainThread(this, NS_DISPATCH_NORMAL))) {
   1.536 +      JS_ReportError(aCx, "Failed to dispatch to main thread!");
   1.537 +      return false;
   1.538 +    }
   1.539 +
   1.540 +    return syncLoop.Run();
   1.541 +  }
   1.542 +
   1.543 +protected:
   1.544 +  virtual ~WorkerThreadProxySyncRunnable()
   1.545 +  { }
   1.546 +
   1.547 +  virtual nsresult
   1.548 +  MainThreadRun() = 0;
   1.549 +
   1.550 +private:
   1.551 +  NS_DECL_NSIRUNNABLE
   1.552 +};
   1.553 +
   1.554 +class SyncTeardownRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.555 +{
   1.556 +public:
   1.557 +  SyncTeardownRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
   1.558 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   1.559 +  { }
   1.560 +
   1.561 +private:
   1.562 +  ~SyncTeardownRunnable()
   1.563 +  { }
   1.564 +
   1.565 +  virtual nsresult
   1.566 +  MainThreadRun() MOZ_OVERRIDE
   1.567 +  {
   1.568 +    mProxy->Teardown();
   1.569 +    MOZ_ASSERT(!mProxy->mSyncLoopTarget);
   1.570 +    return NS_OK;
   1.571 +  }
   1.572 +};
   1.573 +
   1.574 +class SetBackgroundRequestRunnable MOZ_FINAL :
   1.575 +  public WorkerThreadProxySyncRunnable
   1.576 +{
   1.577 +  bool mValue;
   1.578 +
   1.579 +public:
   1.580 +  SetBackgroundRequestRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.581 +                               bool aValue)
   1.582 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
   1.583 +  { }
   1.584 +
   1.585 +private:
   1.586 +  ~SetBackgroundRequestRunnable()
   1.587 +  { }
   1.588 +
   1.589 +  virtual nsresult
   1.590 +  MainThreadRun() MOZ_OVERRIDE
   1.591 +  {
   1.592 +    return mProxy->mXHR->SetMozBackgroundRequest(mValue);
   1.593 +  }
   1.594 +};
   1.595 +
   1.596 +class SetWithCredentialsRunnable MOZ_FINAL :
   1.597 +  public WorkerThreadProxySyncRunnable
   1.598 +{
   1.599 +  bool mValue;
   1.600 +
   1.601 +public:
   1.602 +  SetWithCredentialsRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.603 +                             bool aValue)
   1.604 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mValue(aValue)
   1.605 +  { }
   1.606 +
   1.607 +private:
   1.608 +  ~SetWithCredentialsRunnable()
   1.609 +  { }
   1.610 +
   1.611 +  virtual nsresult
   1.612 +  MainThreadRun() MOZ_OVERRIDE
   1.613 +  {
   1.614 +    return mProxy->mXHR->SetWithCredentials(mValue);
   1.615 +  }
   1.616 +};
   1.617 +
   1.618 +class SetResponseTypeRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.619 +{
   1.620 +  nsString mResponseType;
   1.621 +
   1.622 +public:
   1.623 +  SetResponseTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.624 +                          const nsAString& aResponseType)
   1.625 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
   1.626 +    mResponseType(aResponseType)
   1.627 +  { }
   1.628 +
   1.629 +  void
   1.630 +  GetResponseType(nsAString& aResponseType)
   1.631 +  {
   1.632 +    aResponseType.Assign(mResponseType);
   1.633 +  }
   1.634 +
   1.635 +private:
   1.636 +  ~SetResponseTypeRunnable()
   1.637 +  { }
   1.638 +
   1.639 +  virtual nsresult
   1.640 +  MainThreadRun() MOZ_OVERRIDE
   1.641 +  {
   1.642 +    nsresult rv = mProxy->mXHR->SetResponseType(mResponseType);
   1.643 +    mResponseType.Truncate();
   1.644 +    if (NS_SUCCEEDED(rv)) {
   1.645 +      rv = mProxy->mXHR->GetResponseType(mResponseType);
   1.646 +    }
   1.647 +    return rv;
   1.648 +  }
   1.649 +};
   1.650 +
   1.651 +class SetTimeoutRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.652 +{
   1.653 +  uint32_t mTimeout;
   1.654 +
   1.655 +public:
   1.656 +  SetTimeoutRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.657 +                     uint32_t aTimeout)
   1.658 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mTimeout(aTimeout)
   1.659 +  { }
   1.660 +
   1.661 +private:
   1.662 +  ~SetTimeoutRunnable()
   1.663 +  { }
   1.664 +
   1.665 +  virtual nsresult
   1.666 +  MainThreadRun() MOZ_OVERRIDE
   1.667 +  {
   1.668 +    return mProxy->mXHR->SetTimeout(mTimeout);
   1.669 +  }
   1.670 +};
   1.671 +
   1.672 +class AbortRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.673 +{
   1.674 +public:
   1.675 +  AbortRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy)
   1.676 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   1.677 +  { }
   1.678 +
   1.679 +private:
   1.680 +  ~AbortRunnable()
   1.681 +  { }
   1.682 +
   1.683 +  virtual nsresult
   1.684 +  MainThreadRun() MOZ_OVERRIDE;
   1.685 +};
   1.686 +
   1.687 +class GetAllResponseHeadersRunnable MOZ_FINAL :
   1.688 +  public WorkerThreadProxySyncRunnable
   1.689 +{
   1.690 +  nsCString& mResponseHeaders;
   1.691 +
   1.692 +public:
   1.693 +  GetAllResponseHeadersRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.694 +                                nsCString& aResponseHeaders)
   1.695 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy),
   1.696 +    mResponseHeaders(aResponseHeaders)
   1.697 +  { }
   1.698 +
   1.699 +private:
   1.700 +  ~GetAllResponseHeadersRunnable()
   1.701 +  { }
   1.702 +
   1.703 +  virtual nsresult
   1.704 +  MainThreadRun() MOZ_OVERRIDE
   1.705 +  {
   1.706 +    mProxy->mXHR->GetAllResponseHeaders(mResponseHeaders);
   1.707 +    return NS_OK;
   1.708 +  }
   1.709 +};
   1.710 +
   1.711 +class GetResponseHeaderRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.712 +{
   1.713 +  const nsCString mHeader;
   1.714 +  nsCString& mValue;
   1.715 +
   1.716 +public:
   1.717 +  GetResponseHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.718 +                            const nsACString& aHeader, nsCString& aValue)
   1.719 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mHeader(aHeader),
   1.720 +    mValue(aValue)
   1.721 +  { }
   1.722 +
   1.723 +private:
   1.724 +  ~GetResponseHeaderRunnable()
   1.725 +  { }
   1.726 +
   1.727 +  virtual nsresult
   1.728 +  MainThreadRun() MOZ_OVERRIDE
   1.729 +  {
   1.730 +    return mProxy->mXHR->GetResponseHeader(mHeader, mValue);
   1.731 +  }
   1.732 +};
   1.733 +
   1.734 +class OpenRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.735 +{
   1.736 +  nsCString mMethod;
   1.737 +  nsString mURL;
   1.738 +  Optional<nsAString> mUser;
   1.739 +  nsString mUserStr;
   1.740 +  Optional<nsAString> mPassword;
   1.741 +  nsString mPasswordStr;
   1.742 +  bool mBackgroundRequest;
   1.743 +  bool mWithCredentials;
   1.744 +  uint32_t mTimeout;
   1.745 +
   1.746 +public:
   1.747 +  OpenRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.748 +               const nsACString& aMethod, const nsAString& aURL,
   1.749 +               const Optional<nsAString>& aUser,
   1.750 +               const Optional<nsAString>& aPassword,
   1.751 +               bool aBackgroundRequest, bool aWithCredentials,
   1.752 +               uint32_t aTimeout)
   1.753 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMethod(aMethod),
   1.754 +    mURL(aURL), mBackgroundRequest(aBackgroundRequest),
   1.755 +    mWithCredentials(aWithCredentials), mTimeout(aTimeout)
   1.756 +  {
   1.757 +    if (aUser.WasPassed()) {
   1.758 +      mUserStr = aUser.Value();
   1.759 +      mUser = &mUserStr;
   1.760 +    }
   1.761 +    if (aPassword.WasPassed()) {
   1.762 +      mPasswordStr = aPassword.Value();
   1.763 +      mPassword = &mPasswordStr;
   1.764 +    }
   1.765 +  }
   1.766 +
   1.767 +private:
   1.768 +  ~OpenRunnable()
   1.769 +  { }
   1.770 +
   1.771 +  virtual nsresult
   1.772 +  MainThreadRun() MOZ_OVERRIDE
   1.773 +  {
   1.774 +    WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
   1.775 +    mProxy->mWorkerPrivate = mWorkerPrivate;
   1.776 +
   1.777 +    nsresult rv = MainThreadRunInternal();
   1.778 +
   1.779 +    mProxy->mWorkerPrivate = oldWorker;
   1.780 +    return rv;
   1.781 +  }
   1.782 +
   1.783 +  nsresult
   1.784 +  MainThreadRunInternal();
   1.785 +};
   1.786 +
   1.787 +class SendRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.788 +{
   1.789 +  nsString mStringBody;
   1.790 +  JSAutoStructuredCloneBuffer mBody;
   1.791 +  nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
   1.792 +  nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
   1.793 +  bool mHasUploadListeners;
   1.794 +
   1.795 +public:
   1.796 +  SendRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.797 +               const nsAString& aStringBody, JSAutoStructuredCloneBuffer&& aBody,
   1.798 +               nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects,
   1.799 +               nsIEventTarget* aSyncLoopTarget, bool aHasUploadListeners)
   1.800 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy)
   1.801 +  , mStringBody(aStringBody)
   1.802 +  , mBody(Move(aBody))
   1.803 +  , mSyncLoopTarget(aSyncLoopTarget)
   1.804 +  , mHasUploadListeners(aHasUploadListeners)
   1.805 +  {
   1.806 +    mClonedObjects.SwapElements(aClonedObjects);
   1.807 +  }
   1.808 +
   1.809 +private:
   1.810 +  ~SendRunnable()
   1.811 +  { }
   1.812 +
   1.813 +  virtual nsresult
   1.814 +  MainThreadRun() MOZ_OVERRIDE;
   1.815 +};
   1.816 +
   1.817 +class SetRequestHeaderRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.818 +{
   1.819 +  nsCString mHeader;
   1.820 +  nsCString mValue;
   1.821 +
   1.822 +public:
   1.823 +  SetRequestHeaderRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.824 +                           const nsACString& aHeader, const nsACString& aValue)
   1.825 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mHeader(aHeader),
   1.826 +    mValue(aValue)
   1.827 +  { }
   1.828 +
   1.829 +private:
   1.830 +  ~SetRequestHeaderRunnable()
   1.831 +  { }
   1.832 +
   1.833 +  virtual nsresult
   1.834 +  MainThreadRun() MOZ_OVERRIDE
   1.835 +  {
   1.836 +    return mProxy->mXHR->SetRequestHeader(mHeader, mValue);
   1.837 +  }
   1.838 +};
   1.839 +
   1.840 +class OverrideMimeTypeRunnable MOZ_FINAL : public WorkerThreadProxySyncRunnable
   1.841 +{
   1.842 +  nsString mMimeType;
   1.843 +
   1.844 +public:
   1.845 +  OverrideMimeTypeRunnable(WorkerPrivate* aWorkerPrivate, Proxy* aProxy,
   1.846 +                           const nsAString& aMimeType)
   1.847 +  : WorkerThreadProxySyncRunnable(aWorkerPrivate, aProxy), mMimeType(aMimeType)
   1.848 +  { }
   1.849 +
   1.850 +private:
   1.851 +  ~OverrideMimeTypeRunnable()
   1.852 +  { }
   1.853 +
   1.854 +  virtual nsresult
   1.855 +  MainThreadRun() MOZ_OVERRIDE
   1.856 +  {
   1.857 +    mProxy->mXHR->OverrideMimeType(mMimeType);
   1.858 +    return NS_OK;
   1.859 +  }
   1.860 +};
   1.861 +
   1.862 +class AutoUnpinXHR
   1.863 +{
   1.864 +  XMLHttpRequest* mXMLHttpRequestPrivate;
   1.865 +
   1.866 +public:
   1.867 +  AutoUnpinXHR(XMLHttpRequest* aXMLHttpRequestPrivate)
   1.868 +  : mXMLHttpRequestPrivate(aXMLHttpRequestPrivate)
   1.869 +  {
   1.870 +    MOZ_ASSERT(aXMLHttpRequestPrivate);
   1.871 +  }
   1.872 +
   1.873 +  ~AutoUnpinXHR()
   1.874 +  {
   1.875 +    if (mXMLHttpRequestPrivate) {
   1.876 +      mXMLHttpRequestPrivate->Unpin();
   1.877 +    }
   1.878 +  }
   1.879 +
   1.880 +  void Clear()
   1.881 +  {
   1.882 +    mXMLHttpRequestPrivate = nullptr;
   1.883 +  }
   1.884 +};
   1.885 +
   1.886 +} // anonymous namespace
   1.887 +
   1.888 +bool
   1.889 +Proxy::Init()
   1.890 +{
   1.891 +  AssertIsOnMainThread();
   1.892 +  MOZ_ASSERT(mWorkerPrivate);
   1.893 +
   1.894 +  if (mXHR) {
   1.895 +    return true;
   1.896 +  }
   1.897 +
   1.898 +  nsPIDOMWindow* ownerWindow = mWorkerPrivate->GetWindow();
   1.899 +  if (ownerWindow) {
   1.900 +    ownerWindow = ownerWindow->GetOuterWindow();
   1.901 +    if (!ownerWindow) {
   1.902 +      NS_ERROR("No outer window?!");
   1.903 +      return false;
   1.904 +    }
   1.905 +
   1.906 +    nsPIDOMWindow* innerWindow = ownerWindow->GetCurrentInnerWindow();
   1.907 +    if (mWorkerPrivate->GetWindow() != innerWindow) {
   1.908 +      NS_WARNING("Window has navigated, cannot create XHR here.");
   1.909 +      return false;
   1.910 +    }
   1.911 +  }
   1.912 +
   1.913 +  mXHR = new nsXMLHttpRequest();
   1.914 +
   1.915 +  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(ownerWindow);
   1.916 +  if (NS_FAILED(mXHR->Init(mWorkerPrivate->GetPrincipal(),
   1.917 +                           mWorkerPrivate->GetScriptContext(),
   1.918 +                           global, mWorkerPrivate->GetBaseURI()))) {
   1.919 +    mXHR = nullptr;
   1.920 +    return false;
   1.921 +  }
   1.922 +
   1.923 +  mXHR->SetParameters(mMozAnon, mMozSystem);
   1.924 +
   1.925 +  if (NS_FAILED(mXHR->GetUpload(getter_AddRefs(mXHRUpload)))) {
   1.926 +    mXHR = nullptr;
   1.927 +    return false;
   1.928 +  }
   1.929 +
   1.930 +  if (!AddRemoveEventListeners(false, true)) {
   1.931 +    mXHRUpload = nullptr;
   1.932 +    mXHR = nullptr;
   1.933 +    return false;
   1.934 +  }
   1.935 +
   1.936 +  return true;
   1.937 +}
   1.938 +
   1.939 +void
   1.940 +Proxy::Teardown()
   1.941 +{
   1.942 +  AssertIsOnMainThread();
   1.943 +
   1.944 +  if (mXHR) {
   1.945 +    Reset();
   1.946 +
   1.947 +    // NB: We are intentionally dropping events coming from xhr.abort on the
   1.948 +    // floor.
   1.949 +    AddRemoveEventListeners(false, false);
   1.950 +    mXHR->Abort();
   1.951 +
   1.952 +    if (mOutstandingSendCount) {
   1.953 +      nsRefPtr<XHRUnpinRunnable> runnable =
   1.954 +        new XHRUnpinRunnable(mWorkerPrivate, mXMLHttpRequestPrivate);
   1.955 +      if (!runnable->Dispatch(nullptr)) {
   1.956 +        NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
   1.957 +      }
   1.958 +
   1.959 +      if (mSyncLoopTarget) {
   1.960 +        // We have an unclosed sync loop.  Fix that now.
   1.961 +        nsRefPtr<MainThreadStopSyncLoopRunnable> runnable =
   1.962 +          new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
   1.963 +                                             mSyncLoopTarget.forget(),
   1.964 +                                             false);
   1.965 +        if (!runnable->Dispatch(nullptr)) {
   1.966 +          NS_RUNTIMEABORT("We're going to hang at shutdown anyways.");
   1.967 +        }
   1.968 +      }
   1.969 +
   1.970 +      mWorkerPrivate = nullptr;
   1.971 +      mOutstandingSendCount = 0;
   1.972 +    }
   1.973 +
   1.974 +    mXHRUpload = nullptr;
   1.975 +    mXHR = nullptr;
   1.976 +  }
   1.977 +
   1.978 +  MOZ_ASSERT(!mWorkerPrivate);
   1.979 +  MOZ_ASSERT(!mSyncLoopTarget);
   1.980 +}
   1.981 +
   1.982 +bool
   1.983 +Proxy::AddRemoveEventListeners(bool aUpload, bool aAdd)
   1.984 +{
   1.985 +  AssertIsOnMainThread();
   1.986 +
   1.987 +  NS_ASSERTION(!aUpload ||
   1.988 +               (mUploadEventListenersAttached && !aAdd) ||
   1.989 +               (!mUploadEventListenersAttached && aAdd),
   1.990 +               "Messed up logic for upload listeners!");
   1.991 +
   1.992 +  nsCOMPtr<nsIDOMEventTarget> target =
   1.993 +    aUpload ?
   1.994 +    do_QueryInterface(mXHRUpload) :
   1.995 +    do_QueryInterface(static_cast<nsIXMLHttpRequest*>(mXHR.get()));
   1.996 +  NS_ASSERTION(target, "This should never fail!");
   1.997 +
   1.998 +  uint32_t lastEventType = aUpload ? STRING_LAST_EVENTTARGET : STRING_LAST_XHR;
   1.999 +
  1.1000 +  nsAutoString eventType;
  1.1001 +  for (uint32_t index = 0; index <= lastEventType; index++) {
  1.1002 +    eventType = NS_ConvertASCIItoUTF16(sEventStrings[index]);
  1.1003 +    if (aAdd) {
  1.1004 +      if (NS_FAILED(target->AddEventListener(eventType, this, false))) {
  1.1005 +        return false;
  1.1006 +      }
  1.1007 +    }
  1.1008 +    else if (NS_FAILED(target->RemoveEventListener(eventType, this, false))) {
  1.1009 +      return false;
  1.1010 +    }
  1.1011 +  }
  1.1012 +
  1.1013 +  if (aUpload) {
  1.1014 +    mUploadEventListenersAttached = aAdd;
  1.1015 +  }
  1.1016 +
  1.1017 +  return true;
  1.1018 +}
  1.1019 +
  1.1020 +NS_IMPL_ISUPPORTS(Proxy, nsIDOMEventListener)
  1.1021 +
  1.1022 +NS_IMETHODIMP
  1.1023 +Proxy::HandleEvent(nsIDOMEvent* aEvent)
  1.1024 +{
  1.1025 +  AssertIsOnMainThread();
  1.1026 +
  1.1027 +  if (!mWorkerPrivate || !mXMLHttpRequestPrivate) {
  1.1028 +    NS_ERROR("Shouldn't get here!");
  1.1029 +    return NS_OK;
  1.1030 +  }
  1.1031 +
  1.1032 +  nsString type;
  1.1033 +  if (NS_FAILED(aEvent->GetType(type))) {
  1.1034 +    NS_WARNING("Failed to get event type!");
  1.1035 +    return NS_ERROR_FAILURE;
  1.1036 +  }
  1.1037 +
  1.1038 +  nsCOMPtr<nsIDOMEventTarget> target;
  1.1039 +  if (NS_FAILED(aEvent->GetTarget(getter_AddRefs(target)))) {
  1.1040 +    NS_WARNING("Failed to get target!");
  1.1041 +    return NS_ERROR_FAILURE;
  1.1042 +  }
  1.1043 +
  1.1044 +  nsCOMPtr<nsIXMLHttpRequestUpload> uploadTarget = do_QueryInterface(target);
  1.1045 +  nsCOMPtr<nsIDOMProgressEvent> progressEvent = do_QueryInterface(aEvent);
  1.1046 +
  1.1047 +  nsRefPtr<EventRunnable> runnable;
  1.1048 +
  1.1049 +  if (mInOpen && type.EqualsASCII(sEventStrings[STRING_readystatechange])) {
  1.1050 +    uint16_t readyState = 0;
  1.1051 +    if (NS_SUCCEEDED(mXHR->GetReadyState(&readyState)) &&
  1.1052 +        readyState == nsIXMLHttpRequest::OPENED) {
  1.1053 +      mInnerEventStreamId++;
  1.1054 +    }
  1.1055 +  }
  1.1056 +
  1.1057 +  if (progressEvent) {
  1.1058 +    bool lengthComputable;
  1.1059 +    uint64_t loaded, total;
  1.1060 +    if (NS_FAILED(progressEvent->GetLengthComputable(&lengthComputable)) ||
  1.1061 +        NS_FAILED(progressEvent->GetLoaded(&loaded)) ||
  1.1062 +        NS_FAILED(progressEvent->GetTotal(&total))) {
  1.1063 +      NS_WARNING("Bad progress event!");
  1.1064 +      return NS_ERROR_FAILURE;
  1.1065 +    }
  1.1066 +    runnable = new EventRunnable(this, !!uploadTarget, type, lengthComputable,
  1.1067 +                                 loaded, total);
  1.1068 +  }
  1.1069 +  else {
  1.1070 +    runnable = new EventRunnable(this, !!uploadTarget, type);
  1.1071 +  }
  1.1072 +
  1.1073 +  {
  1.1074 +    AutoSafeJSContext cx;
  1.1075 +    JSAutoRequest ar(cx);
  1.1076 +    runnable->Dispatch(cx);
  1.1077 +  }
  1.1078 +
  1.1079 +  if (!uploadTarget) {
  1.1080 +    if (type.EqualsASCII(sEventStrings[STRING_loadstart])) {
  1.1081 +      NS_ASSERTION(!mMainThreadSeenLoadStart, "Huh?!");
  1.1082 +      mMainThreadSeenLoadStart = true;
  1.1083 +    }
  1.1084 +    else if (mMainThreadSeenLoadStart &&
  1.1085 +             type.EqualsASCII(sEventStrings[STRING_loadend])) {
  1.1086 +      mMainThreadSeenLoadStart = false;
  1.1087 +
  1.1088 +      nsRefPtr<LoadStartDetectionRunnable> runnable =
  1.1089 +        new LoadStartDetectionRunnable(this, mXMLHttpRequestPrivate);
  1.1090 +      if (!runnable->RegisterAndDispatch()) {
  1.1091 +        NS_WARNING("Failed to dispatch LoadStartDetectionRunnable!");
  1.1092 +      }
  1.1093 +    }
  1.1094 +  }
  1.1095 +
  1.1096 +  return NS_OK;
  1.1097 +}
  1.1098 +
  1.1099 +NS_IMPL_ISUPPORTS_INHERITED0(WorkerThreadProxySyncRunnable, nsRunnable)
  1.1100 +
  1.1101 +NS_IMPL_ISUPPORTS_INHERITED0(AsyncTeardownRunnable, nsRunnable)
  1.1102 +
  1.1103 +NS_IMPL_ISUPPORTS_INHERITED(LoadStartDetectionRunnable, nsRunnable,
  1.1104 +                                                        nsIDOMEventListener)
  1.1105 +
  1.1106 +NS_IMETHODIMP
  1.1107 +LoadStartDetectionRunnable::Run()
  1.1108 +{
  1.1109 +  AssertIsOnMainThread();
  1.1110 +
  1.1111 +  if (NS_FAILED(mXHR->RemoveEventListener(mEventType, this, false))) {
  1.1112 +    NS_WARNING("Failed to remove event listener!");
  1.1113 +  }
  1.1114 +
  1.1115 +  if (!mReceivedLoadStart) {
  1.1116 +    if (mProxy->mOutstandingSendCount > 1) {
  1.1117 +      mProxy->mOutstandingSendCount--;
  1.1118 +    } else if (mProxy->mOutstandingSendCount == 1) {
  1.1119 +      mProxy->Reset();
  1.1120 +
  1.1121 +      nsRefPtr<ProxyCompleteRunnable> runnable =
  1.1122 +        new ProxyCompleteRunnable(mWorkerPrivate, mProxy,
  1.1123 +                                  mXMLHttpRequestPrivate, mChannelId);
  1.1124 +      if (runnable->Dispatch(nullptr)) {
  1.1125 +        mProxy->mWorkerPrivate = nullptr;
  1.1126 +        mProxy->mSyncLoopTarget = nullptr;
  1.1127 +        mProxy->mOutstandingSendCount--;
  1.1128 +      }
  1.1129 +    }
  1.1130 +  }
  1.1131 +
  1.1132 +  mProxy = nullptr;
  1.1133 +  mXHR = nullptr;
  1.1134 +  mXMLHttpRequestPrivate = nullptr;
  1.1135 +  return NS_OK;
  1.1136 +}
  1.1137 +
  1.1138 +NS_IMETHODIMP
  1.1139 +LoadStartDetectionRunnable::HandleEvent(nsIDOMEvent* aEvent)
  1.1140 +{
  1.1141 +  AssertIsOnMainThread();
  1.1142 +
  1.1143 +#ifdef DEBUG
  1.1144 +  {
  1.1145 +    nsString type;
  1.1146 +    if (NS_SUCCEEDED(aEvent->GetType(type))) {
  1.1147 +      MOZ_ASSERT(type == mEventType);
  1.1148 +    }
  1.1149 +    else {
  1.1150 +      NS_WARNING("Failed to get event type!");
  1.1151 +    }
  1.1152 +  }
  1.1153 +#endif
  1.1154 +
  1.1155 +  mReceivedLoadStart = true;
  1.1156 +  return NS_OK;
  1.1157 +}
  1.1158 +
  1.1159 +bool
  1.1160 +EventRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
  1.1161 +{
  1.1162 +  AssertIsOnMainThread();
  1.1163 +
  1.1164 +  nsRefPtr<nsXMLHttpRequest>& xhr = mProxy->mXHR;
  1.1165 +  MOZ_ASSERT(xhr);
  1.1166 +
  1.1167 +  if (NS_FAILED(xhr->GetResponseType(mResponseType))) {
  1.1168 +    MOZ_ASSERT(false, "This should never fail!");
  1.1169 +  }
  1.1170 +
  1.1171 +  mResponseTextResult = xhr->GetResponseText(mResponseText);
  1.1172 +  if (NS_SUCCEEDED(mResponseTextResult)) {
  1.1173 +    mResponseResult = mResponseTextResult;
  1.1174 +    if (mResponseText.IsVoid()) {
  1.1175 +      mResponse = JSVAL_NULL;
  1.1176 +    }
  1.1177 +  }
  1.1178 +  else {
  1.1179 +    JS::Rooted<JS::Value> response(aCx);
  1.1180 +    mResponseResult = xhr->GetResponse(aCx, &response);
  1.1181 +    if (NS_SUCCEEDED(mResponseResult)) {
  1.1182 +      if (JSVAL_IS_UNIVERSAL(response)) {
  1.1183 +        mResponse = response;
  1.1184 +      }
  1.1185 +      else {
  1.1186 +        // Anything subject to GC must be cloned.
  1.1187 +        JSStructuredCloneCallbacks* callbacks =
  1.1188 +          aWorkerPrivate->IsChromeWorker() ?
  1.1189 +          ChromeWorkerStructuredCloneCallbacks(true) :
  1.1190 +          WorkerStructuredCloneCallbacks(true);
  1.1191 +
  1.1192 +        nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  1.1193 +
  1.1194 +        if (mResponseBuffer.write(aCx, response, callbacks, &clonedObjects)) {
  1.1195 +          mClonedObjects.SwapElements(clonedObjects);
  1.1196 +        }
  1.1197 +        else {
  1.1198 +          NS_WARNING("Failed to clone response!");
  1.1199 +          mResponseResult = NS_ERROR_DOM_DATA_CLONE_ERR;
  1.1200 +        }
  1.1201 +      }
  1.1202 +    }
  1.1203 +  }
  1.1204 +
  1.1205 +  mStatusResult = xhr->GetStatus(&mStatus);
  1.1206 +
  1.1207 +  xhr->GetStatusText(mStatusText);
  1.1208 +
  1.1209 +  mReadyState = xhr->ReadyState();
  1.1210 +
  1.1211 +  return true;
  1.1212 +}
  1.1213 +
  1.1214 +bool
  1.1215 +EventRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
  1.1216 +{
  1.1217 +  if (mEventStreamId != mProxy->mOuterEventStreamId) {
  1.1218 +    // Threads raced, this event is now obsolete.
  1.1219 +    return true;
  1.1220 +  }
  1.1221 +
  1.1222 +  if (!mProxy->mXMLHttpRequestPrivate) {
  1.1223 +    // Object was finalized, bail.
  1.1224 +    return true;
  1.1225 +  }
  1.1226 +
  1.1227 +  if (mType.EqualsASCII(sEventStrings[STRING_loadstart])) {
  1.1228 +    if (mUploadEvent) {
  1.1229 +      mProxy->mSeenUploadLoadStart = true;
  1.1230 +    }
  1.1231 +    else {
  1.1232 +      mProxy->mSeenLoadStart = true;
  1.1233 +    }
  1.1234 +  }
  1.1235 +  else if (mType.EqualsASCII(sEventStrings[STRING_loadend])) {
  1.1236 +    if (mUploadEvent) {
  1.1237 +      mProxy->mSeenUploadLoadStart = false;
  1.1238 +    }
  1.1239 +    else {
  1.1240 +      mProxy->mSeenLoadStart = false;
  1.1241 +    }
  1.1242 +  }
  1.1243 +  else if (mType.EqualsASCII(sEventStrings[STRING_abort])) {
  1.1244 +    if ((mUploadEvent && !mProxy->mSeenUploadLoadStart) ||
  1.1245 +        (!mUploadEvent && !mProxy->mSeenLoadStart)) {
  1.1246 +      // We've already dispatched premature abort events.
  1.1247 +      return true;
  1.1248 +    }
  1.1249 +  }
  1.1250 +  else if (mType.EqualsASCII(sEventStrings[STRING_readystatechange])) {
  1.1251 +    if (mReadyState == 4 && !mUploadEvent && !mProxy->mSeenLoadStart) {
  1.1252 +      // We've already dispatched premature abort events.
  1.1253 +      return true;
  1.1254 +    }
  1.1255 +  }
  1.1256 +
  1.1257 +  if (mProgressEvent) {
  1.1258 +    // Cache these for premature abort events.
  1.1259 +    if (mUploadEvent) {
  1.1260 +      mProxy->mLastUploadLengthComputable = mLengthComputable;
  1.1261 +      mProxy->mLastUploadLoaded = mLoaded;
  1.1262 +      mProxy->mLastUploadTotal = mTotal;
  1.1263 +    }
  1.1264 +    else {
  1.1265 +      mProxy->mLastLengthComputable = mLengthComputable;
  1.1266 +      mProxy->mLastLoaded = mLoaded;
  1.1267 +      mProxy->mLastTotal = mTotal;
  1.1268 +    }
  1.1269 +  }
  1.1270 +
  1.1271 +  nsAutoPtr<XMLHttpRequest::StateData> state(new XMLHttpRequest::StateData());
  1.1272 +  StateDataAutoRooter rooter(aCx, state);
  1.1273 +
  1.1274 +  state->mResponseTextResult = mResponseTextResult;
  1.1275 +  state->mResponseText = mResponseText;
  1.1276 +
  1.1277 +  if (NS_SUCCEEDED(mResponseTextResult)) {
  1.1278 +    MOZ_ASSERT(JSVAL_IS_VOID(mResponse) || JSVAL_IS_NULL(mResponse));
  1.1279 +    state->mResponseResult = mResponseTextResult;
  1.1280 +    state->mResponse = mResponse;
  1.1281 +  }
  1.1282 +  else {
  1.1283 +    state->mResponseResult = mResponseResult;
  1.1284 +
  1.1285 +    if (NS_SUCCEEDED(mResponseResult)) {
  1.1286 +      if (mResponseBuffer.data()) {
  1.1287 +        MOZ_ASSERT(JSVAL_IS_VOID(mResponse));
  1.1288 +
  1.1289 +        JSAutoStructuredCloneBuffer responseBuffer(Move(mResponseBuffer));
  1.1290 +
  1.1291 +        JSStructuredCloneCallbacks* callbacks =
  1.1292 +          aWorkerPrivate->IsChromeWorker() ?
  1.1293 +          ChromeWorkerStructuredCloneCallbacks(false) :
  1.1294 +          WorkerStructuredCloneCallbacks(false);
  1.1295 +
  1.1296 +        nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  1.1297 +        clonedObjects.SwapElements(mClonedObjects);
  1.1298 +
  1.1299 +        JS::Rooted<JS::Value> response(aCx);
  1.1300 +        if (!responseBuffer.read(aCx, &response, callbacks, &clonedObjects)) {
  1.1301 +          return false;
  1.1302 +        }
  1.1303 +
  1.1304 +        state->mResponse = response;
  1.1305 +      }
  1.1306 +      else {
  1.1307 +        state->mResponse = mResponse;
  1.1308 +      }
  1.1309 +    }
  1.1310 +  }
  1.1311 +
  1.1312 +  state->mStatusResult = mStatusResult;
  1.1313 +  state->mStatus = mStatus;
  1.1314 +
  1.1315 +  state->mStatusText = mStatusText;
  1.1316 +
  1.1317 +  state->mReadyState = mReadyState;
  1.1318 +
  1.1319 +  XMLHttpRequest* xhr = mProxy->mXMLHttpRequestPrivate;
  1.1320 +  xhr->UpdateState(*state);
  1.1321 +
  1.1322 +  if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
  1.1323 +    return true;
  1.1324 +  }
  1.1325 +
  1.1326 +  JS::Rooted<JSString*> type(aCx,
  1.1327 +    JS_NewUCStringCopyN(aCx, mType.get(), mType.Length()));
  1.1328 +  if (!type) {
  1.1329 +    return false;
  1.1330 +  }
  1.1331 +
  1.1332 +  nsXHREventTarget* target;
  1.1333 +  if (mUploadEvent) {
  1.1334 +    target = xhr->GetUploadObjectNoCreate();
  1.1335 +  }
  1.1336 +  else {
  1.1337 +    target = xhr;
  1.1338 +  }
  1.1339 +
  1.1340 +  MOZ_ASSERT(target);
  1.1341 +
  1.1342 +  nsCOMPtr<nsIDOMEvent> event;
  1.1343 +  if (mProgressEvent) {
  1.1344 +    NS_NewDOMProgressEvent(getter_AddRefs(event), target, nullptr, nullptr);
  1.1345 +    nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
  1.1346 +
  1.1347 +    if (progress) {
  1.1348 +      progress->InitProgressEvent(mType, false, false, mLengthComputable,
  1.1349 +                                  mLoaded, mTotal);
  1.1350 +    }
  1.1351 +  }
  1.1352 +  else {
  1.1353 +    NS_NewDOMEvent(getter_AddRefs(event), target, nullptr, nullptr);
  1.1354 +
  1.1355 +    if (event) {
  1.1356 +      event->InitEvent(mType, false, false);
  1.1357 +    }
  1.1358 +  }
  1.1359 +
  1.1360 +  if (!event) {
  1.1361 +    return false;
  1.1362 +  }
  1.1363 +
  1.1364 +  event->SetTrusted(true);
  1.1365 +
  1.1366 +  target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  1.1367 +
  1.1368 +  // After firing the event set mResponse to JSVAL_NULL for chunked response
  1.1369 +  // types.
  1.1370 +  if (StringBeginsWith(mResponseType, NS_LITERAL_STRING("moz-chunked-"))) {
  1.1371 +    xhr->NullResponseText();
  1.1372 +  }
  1.1373 +
  1.1374 +  return true;
  1.1375 +}
  1.1376 +
  1.1377 +NS_IMETHODIMP
  1.1378 +WorkerThreadProxySyncRunnable::Run()
  1.1379 +{
  1.1380 +  AssertIsOnMainThread();
  1.1381 +
  1.1382 +  nsCOMPtr<nsIEventTarget> tempTarget;
  1.1383 +  mSyncLoopTarget.swap(tempTarget);
  1.1384 +
  1.1385 +  mProxy->mSyncEventResponseTarget.swap(tempTarget);
  1.1386 +
  1.1387 +  nsresult rv = MainThreadRun();
  1.1388 +
  1.1389 +  nsRefPtr<ResponseRunnable> response =
  1.1390 +    new ResponseRunnable(mWorkerPrivate, mProxy, rv);
  1.1391 +  if (!response->Dispatch(nullptr)) {
  1.1392 +    MOZ_ASSERT(false, "Failed to dispatch response!");
  1.1393 +  }
  1.1394 +
  1.1395 +  mProxy->mSyncEventResponseTarget.swap(tempTarget);
  1.1396 +
  1.1397 +  return NS_OK;
  1.1398 +}
  1.1399 +
  1.1400 +nsresult
  1.1401 +AbortRunnable::MainThreadRun()
  1.1402 +{
  1.1403 +  mProxy->mInnerEventStreamId++;
  1.1404 +
  1.1405 +  WorkerPrivate* oldWorker = mProxy->mWorkerPrivate;
  1.1406 +  mProxy->mWorkerPrivate = mWorkerPrivate;
  1.1407 +
  1.1408 +  mProxy->mXHR->Abort();
  1.1409 +
  1.1410 +  mProxy->mWorkerPrivate = oldWorker;
  1.1411 +
  1.1412 +  mProxy->Reset();
  1.1413 +
  1.1414 +  return NS_OK;
  1.1415 +}
  1.1416 +
  1.1417 +nsresult
  1.1418 +OpenRunnable::MainThreadRunInternal()
  1.1419 +{
  1.1420 +  if (!mProxy->Init()) {
  1.1421 +    return NS_ERROR_DOM_INVALID_STATE_ERR;
  1.1422 +  }
  1.1423 +
  1.1424 +  nsresult rv;
  1.1425 +
  1.1426 +  if (mBackgroundRequest) {
  1.1427 +    rv = mProxy->mXHR->SetMozBackgroundRequest(mBackgroundRequest);
  1.1428 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1429 +  }
  1.1430 +
  1.1431 +  if (mWithCredentials) {
  1.1432 +    rv = mProxy->mXHR->SetWithCredentials(mWithCredentials);
  1.1433 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1434 +  }
  1.1435 +
  1.1436 +  if (mTimeout) {
  1.1437 +    rv = mProxy->mXHR->SetTimeout(mTimeout);
  1.1438 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1439 +  }
  1.1440 +
  1.1441 +  MOZ_ASSERT(!mProxy->mInOpen);
  1.1442 +  mProxy->mInOpen = true;
  1.1443 +
  1.1444 +  ErrorResult rv2;
  1.1445 +  mProxy->mXHR->Open(mMethod, mURL, true, mUser, mPassword, rv2);
  1.1446 +
  1.1447 +  MOZ_ASSERT(mProxy->mInOpen);
  1.1448 +  mProxy->mInOpen = false;
  1.1449 +
  1.1450 +  if (rv2.Failed()) {
  1.1451 +    return rv2.ErrorCode();
  1.1452 +  }
  1.1453 +
  1.1454 +  return mProxy->mXHR->SetResponseType(NS_LITERAL_STRING("text"));
  1.1455 +}
  1.1456 +
  1.1457 +
  1.1458 +nsresult
  1.1459 +SendRunnable::MainThreadRun()
  1.1460 +{
  1.1461 +  nsCOMPtr<nsIVariant> variant;
  1.1462 +
  1.1463 +  if (mBody.data()) {
  1.1464 +    AutoSafeJSContext cx;
  1.1465 +    JSAutoRequest ar(cx);
  1.1466 +
  1.1467 +    nsIXPConnect* xpc = nsContentUtils::XPConnect();
  1.1468 +    MOZ_ASSERT(xpc);
  1.1469 +
  1.1470 +    nsresult rv = NS_OK;
  1.1471 +
  1.1472 +    JSStructuredCloneCallbacks* callbacks =
  1.1473 +      mWorkerPrivate->IsChromeWorker() ?
  1.1474 +      ChromeWorkerStructuredCloneCallbacks(true) :
  1.1475 +      WorkerStructuredCloneCallbacks(true);
  1.1476 +
  1.1477 +    JS::Rooted<JS::Value> body(cx);
  1.1478 +    if (mBody.read(cx, &body, callbacks, &mClonedObjects)) {
  1.1479 +      if (NS_FAILED(xpc->JSValToVariant(cx, body, getter_AddRefs(variant)))) {
  1.1480 +        rv = NS_ERROR_DOM_INVALID_STATE_ERR;
  1.1481 +      }
  1.1482 +    }
  1.1483 +    else {
  1.1484 +      rv = NS_ERROR_DOM_DATA_CLONE_ERR;
  1.1485 +    }
  1.1486 +
  1.1487 +    mBody.clear();
  1.1488 +    mClonedObjects.Clear();
  1.1489 +
  1.1490 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1491 +  }
  1.1492 +  else {
  1.1493 +    nsCOMPtr<nsIWritableVariant> wvariant =
  1.1494 +      do_CreateInstance(NS_VARIANT_CONTRACTID);
  1.1495 +    NS_ENSURE_TRUE(wvariant, NS_ERROR_UNEXPECTED);
  1.1496 +
  1.1497 +    if (NS_FAILED(wvariant->SetAsAString(mStringBody))) {
  1.1498 +      MOZ_ASSERT(false, "This should never fail!");
  1.1499 +    }
  1.1500 +
  1.1501 +    variant = wvariant;
  1.1502 +  }
  1.1503 +
  1.1504 +  MOZ_ASSERT(!mProxy->mWorkerPrivate);
  1.1505 +  mProxy->mWorkerPrivate = mWorkerPrivate;
  1.1506 +
  1.1507 +  MOZ_ASSERT(!mProxy->mSyncLoopTarget);
  1.1508 +  mProxy->mSyncLoopTarget.swap(mSyncLoopTarget);
  1.1509 +
  1.1510 +  if (mHasUploadListeners) {
  1.1511 +    NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
  1.1512 +    if (!mProxy->AddRemoveEventListeners(true, true)) {
  1.1513 +      MOZ_ASSERT(false, "This should never fail!");
  1.1514 +    }
  1.1515 +  }
  1.1516 +
  1.1517 +  mProxy->mInnerChannelId++;
  1.1518 +
  1.1519 +  nsresult rv = mProxy->mXHR->Send(variant);
  1.1520 +
  1.1521 +  if (NS_SUCCEEDED(rv)) {
  1.1522 +    mProxy->mOutstandingSendCount++;
  1.1523 +
  1.1524 +    if (!mHasUploadListeners) {
  1.1525 +      NS_ASSERTION(!mProxy->mUploadEventListenersAttached, "Huh?!");
  1.1526 +      if (!mProxy->AddRemoveEventListeners(true, true)) {
  1.1527 +        MOZ_ASSERT(false, "This should never fail!");
  1.1528 +      }
  1.1529 +    }
  1.1530 +  }
  1.1531 +
  1.1532 +  return rv;
  1.1533 +}
  1.1534 +
  1.1535 +XMLHttpRequest::XMLHttpRequest(WorkerPrivate* aWorkerPrivate)
  1.1536 +: mWorkerPrivate(aWorkerPrivate),
  1.1537 +  mResponseType(XMLHttpRequestResponseType::Text), mTimeout(0),
  1.1538 +  mRooted(false), mBackgroundRequest(false), mWithCredentials(false),
  1.1539 +  mCanceled(false), mMozAnon(false), mMozSystem(false)
  1.1540 +{
  1.1541 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1542 +
  1.1543 +  SetIsDOMBinding();
  1.1544 +
  1.1545 +  mozilla::HoldJSObjects(this);
  1.1546 +}
  1.1547 +
  1.1548 +XMLHttpRequest::~XMLHttpRequest()
  1.1549 +{
  1.1550 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1551 +
  1.1552 +  ReleaseProxy(XHRIsGoingAway);
  1.1553 +
  1.1554 +  MOZ_ASSERT(!mRooted);
  1.1555 +
  1.1556 +  mozilla::DropJSObjects(this);
  1.1557 +}
  1.1558 +
  1.1559 +NS_IMPL_ADDREF_INHERITED(XMLHttpRequest, nsXHREventTarget)
  1.1560 +NS_IMPL_RELEASE_INHERITED(XMLHttpRequest, nsXHREventTarget)
  1.1561 +
  1.1562 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XMLHttpRequest)
  1.1563 +NS_INTERFACE_MAP_END_INHERITING(nsXHREventTarget)
  1.1564 +
  1.1565 +NS_IMPL_CYCLE_COLLECTION_CLASS(XMLHttpRequest)
  1.1566 +
  1.1567 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(XMLHttpRequest,
  1.1568 +                                                  nsXHREventTarget)
  1.1569 +  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUpload)
  1.1570 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
  1.1571 +
  1.1572 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XMLHttpRequest,
  1.1573 +                                                nsXHREventTarget)
  1.1574 +  tmp->ReleaseProxy(XHRIsGoingAway);
  1.1575 +  NS_IMPL_CYCLE_COLLECTION_UNLINK(mUpload)
  1.1576 +  tmp->mStateData.mResponse.setUndefined();
  1.1577 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
  1.1578 +
  1.1579 +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(XMLHttpRequest,
  1.1580 +                                               nsXHREventTarget)
  1.1581 +  NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mStateData.mResponse)
  1.1582 +NS_IMPL_CYCLE_COLLECTION_TRACE_END
  1.1583 +
  1.1584 +JSObject*
  1.1585 +XMLHttpRequest::WrapObject(JSContext* aCx)
  1.1586 +{
  1.1587 +  return XMLHttpRequestBinding_workers::Wrap(aCx, this);
  1.1588 +}
  1.1589 +
  1.1590 +// static
  1.1591 +already_AddRefed<XMLHttpRequest>
  1.1592 +XMLHttpRequest::Constructor(const GlobalObject& aGlobal,
  1.1593 +                            const MozXMLHttpRequestParameters& aParams,
  1.1594 +                            ErrorResult& aRv)
  1.1595 +{
  1.1596 +  JSContext* cx = aGlobal.GetContext();
  1.1597 +  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
  1.1598 +  MOZ_ASSERT(workerPrivate);
  1.1599 +
  1.1600 +  nsRefPtr<XMLHttpRequest> xhr = new XMLHttpRequest(workerPrivate);
  1.1601 +
  1.1602 +  if (workerPrivate->XHRParamsAllowed()) {
  1.1603 +    if (aParams.mMozSystem)
  1.1604 +      xhr->mMozAnon = true;
  1.1605 +    else
  1.1606 +      xhr->mMozAnon = aParams.mMozAnon;
  1.1607 +    xhr->mMozSystem = aParams.mMozSystem;
  1.1608 +  }
  1.1609 +
  1.1610 +  return xhr.forget();
  1.1611 +}
  1.1612 +
  1.1613 +void
  1.1614 +XMLHttpRequest::ReleaseProxy(ReleaseType aType)
  1.1615 +{
  1.1616 +  // Can't assert that we're on the worker thread here because mWorkerPrivate
  1.1617 +  // may be gone.
  1.1618 +
  1.1619 +  if (mProxy) {
  1.1620 +    if (aType == XHRIsGoingAway) {
  1.1621 +      // We're in a GC finalizer, so we can't do a sync call here (and we don't
  1.1622 +      // need to).
  1.1623 +      nsRefPtr<AsyncTeardownRunnable> runnable =
  1.1624 +        new AsyncTeardownRunnable(mProxy);
  1.1625 +      mProxy = nullptr;
  1.1626 +
  1.1627 +      if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
  1.1628 +        NS_ERROR("Failed to dispatch teardown runnable!");
  1.1629 +      }
  1.1630 +    } else {
  1.1631 +      // This isn't necessary if the worker is going away or the XHR is going
  1.1632 +      // away.
  1.1633 +      if (aType == Default) {
  1.1634 +        // Don't let any more events run.
  1.1635 +        mProxy->mOuterEventStreamId++;
  1.1636 +      }
  1.1637 +
  1.1638 +      // We need to make a sync call here.
  1.1639 +      nsRefPtr<SyncTeardownRunnable> runnable =
  1.1640 +        new SyncTeardownRunnable(mWorkerPrivate, mProxy);
  1.1641 +      mProxy = nullptr;
  1.1642 +
  1.1643 +      if (!runnable->Dispatch(nullptr)) {
  1.1644 +        NS_ERROR("Failed to dispatch teardown runnable!");
  1.1645 +      }
  1.1646 +    }
  1.1647 +  }
  1.1648 +}
  1.1649 +
  1.1650 +void
  1.1651 +XMLHttpRequest::MaybePin(ErrorResult& aRv)
  1.1652 +{
  1.1653 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1654 +
  1.1655 +  if (mRooted) {
  1.1656 +    return;
  1.1657 +  }
  1.1658 +
  1.1659 +  JSContext* cx = GetCurrentThreadJSContext();
  1.1660 +
  1.1661 +  if (!mWorkerPrivate->AddFeature(cx, this)) {
  1.1662 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1663 +    return;
  1.1664 +  }
  1.1665 +
  1.1666 +  NS_ADDREF_THIS();
  1.1667 +
  1.1668 +  mRooted = true;
  1.1669 +}
  1.1670 +
  1.1671 +void
  1.1672 +XMLHttpRequest::MaybeDispatchPrematureAbortEvents(ErrorResult& aRv)
  1.1673 +{
  1.1674 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1675 +  MOZ_ASSERT(mProxy);
  1.1676 +
  1.1677 +  mStateData.mReadyState = 4;
  1.1678 +
  1.1679 +  if (mProxy->mSeenUploadLoadStart) {
  1.1680 +    MOZ_ASSERT(mUpload);
  1.1681 +
  1.1682 +    DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("abort"), true,
  1.1683 +                                aRv);
  1.1684 +    if (aRv.Failed()) {
  1.1685 +      return;
  1.1686 +    }
  1.1687 +
  1.1688 +    DispatchPrematureAbortEvent(mUpload, NS_LITERAL_STRING("loadend"), true,
  1.1689 +                                aRv);
  1.1690 +    if (aRv.Failed()) {
  1.1691 +      return;
  1.1692 +    }
  1.1693 +
  1.1694 +    mProxy->mSeenUploadLoadStart = false;
  1.1695 +  }
  1.1696 +
  1.1697 +  if (mProxy->mSeenLoadStart) {
  1.1698 +    DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("readystatechange"),
  1.1699 +                                false, aRv);
  1.1700 +    if (aRv.Failed()) {
  1.1701 +      return;
  1.1702 +    }
  1.1703 +
  1.1704 +    DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("abort"), false, aRv);
  1.1705 +    if (aRv.Failed()) {
  1.1706 +      return;
  1.1707 +    }
  1.1708 +
  1.1709 +    DispatchPrematureAbortEvent(this, NS_LITERAL_STRING("loadend"), false,
  1.1710 +                                aRv);
  1.1711 +    if (aRv.Failed()) {
  1.1712 +      return;
  1.1713 +    }
  1.1714 +
  1.1715 +    mProxy->mSeenLoadStart = false;
  1.1716 +  }
  1.1717 +}
  1.1718 +
  1.1719 +void
  1.1720 +XMLHttpRequest::DispatchPrematureAbortEvent(EventTarget* aTarget,
  1.1721 +                                            const nsAString& aEventType,
  1.1722 +                                            bool aUploadTarget,
  1.1723 +                                            ErrorResult& aRv)
  1.1724 +{
  1.1725 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1726 +  MOZ_ASSERT(aTarget);
  1.1727 +
  1.1728 +  if (!mProxy) {
  1.1729 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1730 +    return;
  1.1731 +  }
  1.1732 +
  1.1733 +  nsCOMPtr<nsIDOMEvent> event;
  1.1734 +  if (aEventType.EqualsLiteral("readystatechange")) {
  1.1735 +    NS_NewDOMEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
  1.1736 +
  1.1737 +    if (event) {
  1.1738 +      event->InitEvent(aEventType, false, false);
  1.1739 +    }
  1.1740 +  }
  1.1741 +  else {
  1.1742 +    NS_NewDOMProgressEvent(getter_AddRefs(event), aTarget, nullptr, nullptr);
  1.1743 +
  1.1744 +    nsCOMPtr<nsIDOMProgressEvent> progress = do_QueryInterface(event);
  1.1745 +    if (progress) {
  1.1746 +      if (aUploadTarget) {
  1.1747 +        progress->InitProgressEvent(aEventType, false, false,
  1.1748 +                                    mProxy->mLastUploadLengthComputable,
  1.1749 +                                    mProxy->mLastUploadLoaded,
  1.1750 +                                    mProxy->mLastUploadTotal);
  1.1751 +      }
  1.1752 +      else {
  1.1753 +        progress->InitProgressEvent(aEventType, false, false,
  1.1754 +                                    mProxy->mLastLengthComputable,
  1.1755 +                                    mProxy->mLastLoaded,
  1.1756 +                                    mProxy->mLastTotal);
  1.1757 +      }
  1.1758 +    }
  1.1759 +  }
  1.1760 +
  1.1761 +  if (!event) {
  1.1762 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1763 +    return;
  1.1764 +  }
  1.1765 +
  1.1766 +  event->SetTrusted(true);
  1.1767 +
  1.1768 +  aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  1.1769 +}
  1.1770 +
  1.1771 +void
  1.1772 +XMLHttpRequest::Unpin()
  1.1773 +{
  1.1774 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1775 +
  1.1776 +  MOZ_ASSERT(mRooted, "Mismatched calls to Unpin!");
  1.1777 +
  1.1778 +  JSContext* cx = GetCurrentThreadJSContext();
  1.1779 +
  1.1780 +  mWorkerPrivate->RemoveFeature(cx, this);
  1.1781 +
  1.1782 +  mRooted = false;
  1.1783 +
  1.1784 +  NS_RELEASE_THIS();
  1.1785 +}
  1.1786 +
  1.1787 +void
  1.1788 +XMLHttpRequest::SendInternal(const nsAString& aStringBody,
  1.1789 +                             JSAutoStructuredCloneBuffer&& aBody,
  1.1790 +                             nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
  1.1791 +                             ErrorResult& aRv)
  1.1792 +{
  1.1793 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1794 +
  1.1795 +  bool hasUploadListeners = mUpload ? mUpload->HasListeners() : false;
  1.1796 +
  1.1797 +  MaybePin(aRv);
  1.1798 +  if (aRv.Failed()) {
  1.1799 +    return;
  1.1800 +  }
  1.1801 +
  1.1802 +  AutoUnpinXHR autoUnpin(this);
  1.1803 +  Maybe<AutoSyncLoopHolder> autoSyncLoop;
  1.1804 +
  1.1805 +  nsCOMPtr<nsIEventTarget> syncLoopTarget;
  1.1806 +  bool isSyncXHR = mProxy->mIsSyncXHR;
  1.1807 +  if (isSyncXHR) {
  1.1808 +    autoSyncLoop.construct(mWorkerPrivate);
  1.1809 +    syncLoopTarget = autoSyncLoop.ref().EventTarget();
  1.1810 +  }
  1.1811 +
  1.1812 +  mProxy->mOuterChannelId++;
  1.1813 +
  1.1814 +  JSContext* cx = mWorkerPrivate->GetJSContext();
  1.1815 +
  1.1816 +  nsRefPtr<SendRunnable> runnable =
  1.1817 +    new SendRunnable(mWorkerPrivate, mProxy, aStringBody, Move(aBody),
  1.1818 +                     aClonedObjects, syncLoopTarget, hasUploadListeners);
  1.1819 +  if (!runnable->Dispatch(cx)) {
  1.1820 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1821 +    return;
  1.1822 +  }
  1.1823 +
  1.1824 +  if (!isSyncXHR)  {
  1.1825 +    autoUnpin.Clear();
  1.1826 +    MOZ_ASSERT(autoSyncLoop.empty());
  1.1827 +    return;
  1.1828 +  }
  1.1829 +
  1.1830 +  autoUnpin.Clear();
  1.1831 +
  1.1832 +  if (!autoSyncLoop.ref().Run()) {
  1.1833 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1834 +  }
  1.1835 +}
  1.1836 +
  1.1837 +bool
  1.1838 +XMLHttpRequest::Notify(JSContext* aCx, Status aStatus)
  1.1839 +{
  1.1840 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1841 +  MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
  1.1842 +
  1.1843 +  if (aStatus >= Canceling && !mCanceled) {
  1.1844 +    mCanceled = true;
  1.1845 +    ReleaseProxy(WorkerIsGoingAway);
  1.1846 +  }
  1.1847 +
  1.1848 +  return true;
  1.1849 +}
  1.1850 +
  1.1851 +void
  1.1852 +XMLHttpRequest::Open(const nsACString& aMethod, const nsAString& aUrl,
  1.1853 +                     bool aAsync, const Optional<nsAString>& aUser,
  1.1854 +                     const Optional<nsAString>& aPassword, ErrorResult& aRv)
  1.1855 +{
  1.1856 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1857 +
  1.1858 +  if (mCanceled) {
  1.1859 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.1860 +    return;
  1.1861 +  }
  1.1862 +
  1.1863 +  if (mProxy) {
  1.1864 +    MaybeDispatchPrematureAbortEvents(aRv);
  1.1865 +    if (aRv.Failed()) {
  1.1866 +      return;
  1.1867 +    }
  1.1868 +  }
  1.1869 +  else {
  1.1870 +    mProxy = new Proxy(this, mMozAnon, mMozSystem);
  1.1871 +  }
  1.1872 +
  1.1873 +  mProxy->mOuterEventStreamId++;
  1.1874 +
  1.1875 +  nsRefPtr<OpenRunnable> runnable =
  1.1876 +    new OpenRunnable(mWorkerPrivate, mProxy, aMethod, aUrl, aUser, aPassword,
  1.1877 +                     mBackgroundRequest, mWithCredentials,
  1.1878 +                     mTimeout);
  1.1879 +
  1.1880 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.1881 +    ReleaseProxy();
  1.1882 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1883 +    return;
  1.1884 +  }
  1.1885 +
  1.1886 +  mProxy->mIsSyncXHR = !aAsync;
  1.1887 +}
  1.1888 +
  1.1889 +void
  1.1890 +XMLHttpRequest::SetRequestHeader(const nsACString& aHeader,
  1.1891 +                                 const nsACString& aValue, ErrorResult& aRv)
  1.1892 +{
  1.1893 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1894 +
  1.1895 +  if (mCanceled) {
  1.1896 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.1897 +    return;
  1.1898 +  }
  1.1899 +
  1.1900 +  if (!mProxy) {
  1.1901 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.1902 +    return;
  1.1903 +  }
  1.1904 +
  1.1905 +  nsRefPtr<SetRequestHeaderRunnable> runnable =
  1.1906 +    new SetRequestHeaderRunnable(mWorkerPrivate, mProxy, aHeader, aValue);
  1.1907 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.1908 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1909 +    return;
  1.1910 +  }
  1.1911 +}
  1.1912 +
  1.1913 +void
  1.1914 +XMLHttpRequest::SetTimeout(uint32_t aTimeout, ErrorResult& aRv)
  1.1915 +{
  1.1916 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1917 +
  1.1918 +  if (mCanceled) {
  1.1919 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.1920 +    return;
  1.1921 +  }
  1.1922 +
  1.1923 +  mTimeout = aTimeout;
  1.1924 +
  1.1925 +  if (!mProxy) {
  1.1926 +    // Open may not have been called yet, in which case we'll handle the
  1.1927 +    // timeout in OpenRunnable.
  1.1928 +    return;
  1.1929 +  }
  1.1930 +
  1.1931 +  nsRefPtr<SetTimeoutRunnable> runnable =
  1.1932 +    new SetTimeoutRunnable(mWorkerPrivate, mProxy, aTimeout);
  1.1933 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.1934 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1935 +    return;
  1.1936 +  }
  1.1937 +}
  1.1938 +
  1.1939 +void
  1.1940 +XMLHttpRequest::SetWithCredentials(bool aWithCredentials, ErrorResult& aRv)
  1.1941 +{
  1.1942 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1943 +
  1.1944 +  if (mCanceled) {
  1.1945 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.1946 +    return;
  1.1947 +  }
  1.1948 +
  1.1949 +  mWithCredentials = aWithCredentials;
  1.1950 +
  1.1951 +  if (!mProxy) {
  1.1952 +    // Open may not have been called yet, in which case we'll handle the
  1.1953 +    // credentials in OpenRunnable.
  1.1954 +    return;
  1.1955 +  }
  1.1956 +
  1.1957 +  nsRefPtr<SetWithCredentialsRunnable> runnable =
  1.1958 +    new SetWithCredentialsRunnable(mWorkerPrivate, mProxy, aWithCredentials);
  1.1959 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.1960 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1961 +    return;
  1.1962 +  }
  1.1963 +}
  1.1964 +
  1.1965 +void
  1.1966 +XMLHttpRequest::SetMozBackgroundRequest(bool aBackgroundRequest,
  1.1967 +                                        ErrorResult& aRv)
  1.1968 +{
  1.1969 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1970 +
  1.1971 +  if (mCanceled) {
  1.1972 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.1973 +    return;
  1.1974 +  }
  1.1975 +
  1.1976 +  mBackgroundRequest = aBackgroundRequest;
  1.1977 +
  1.1978 +  if (!mProxy) {
  1.1979 +    // Open may not have been called yet, in which case we'll handle the
  1.1980 +    // background request in OpenRunnable.
  1.1981 +    return;
  1.1982 +  }
  1.1983 +
  1.1984 +  nsRefPtr<SetBackgroundRequestRunnable> runnable =
  1.1985 +    new SetBackgroundRequestRunnable(mWorkerPrivate, mProxy,
  1.1986 +                                     aBackgroundRequest);
  1.1987 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.1988 +    aRv.Throw(NS_ERROR_FAILURE);
  1.1989 +    return;
  1.1990 +  }
  1.1991 +}
  1.1992 +
  1.1993 +XMLHttpRequestUpload*
  1.1994 +XMLHttpRequest::GetUpload(ErrorResult& aRv)
  1.1995 +{
  1.1996 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.1997 +
  1.1998 +  if (mCanceled) {
  1.1999 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2000 +    return nullptr;
  1.2001 +  }
  1.2002 +
  1.2003 +  if (!mUpload) {
  1.2004 +    mUpload = XMLHttpRequestUpload::Create(this);
  1.2005 +
  1.2006 +    if (!mUpload) {
  1.2007 +      aRv.Throw(NS_ERROR_FAILURE);
  1.2008 +      return nullptr;
  1.2009 +    }
  1.2010 +  }
  1.2011 +
  1.2012 +  return mUpload;
  1.2013 +}
  1.2014 +
  1.2015 +void
  1.2016 +XMLHttpRequest::Send(ErrorResult& aRv)
  1.2017 +{
  1.2018 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.2019 +
  1.2020 +  if (mCanceled) {
  1.2021 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2022 +    return;
  1.2023 +  }
  1.2024 +
  1.2025 +  if (!mProxy) {
  1.2026 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.2027 +    return;
  1.2028 +  }
  1.2029 +
  1.2030 +  // Nothing to clone.
  1.2031 +  JSAutoStructuredCloneBuffer buffer;
  1.2032 +  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  1.2033 +
  1.2034 +  SendInternal(NullString(), Move(buffer), clonedObjects, aRv);
  1.2035 +}
  1.2036 +
  1.2037 +void
  1.2038 +XMLHttpRequest::Send(const nsAString& aBody, ErrorResult& aRv)
  1.2039 +{
  1.2040 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.2041 +
  1.2042 +  if (mCanceled) {
  1.2043 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2044 +    return;
  1.2045 +  }
  1.2046 +
  1.2047 +  if (!mProxy) {
  1.2048 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.2049 +    return;
  1.2050 +  }
  1.2051 +
  1.2052 +  // Nothing to clone.
  1.2053 +  JSAutoStructuredCloneBuffer buffer;
  1.2054 +  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  1.2055 +
  1.2056 +  SendInternal(aBody, Move(buffer), clonedObjects, aRv);
  1.2057 +}
  1.2058 +
  1.2059 +void
  1.2060 +XMLHttpRequest::Send(JS::Handle<JSObject*> aBody, ErrorResult& aRv)
  1.2061 +{
  1.2062 +  JSContext* cx = mWorkerPrivate->GetJSContext();
  1.2063 +
  1.2064 +  MOZ_ASSERT(aBody);
  1.2065 +
  1.2066 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.2067 +
  1.2068 +  if (mCanceled) {
  1.2069 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2070 +    return;
  1.2071 +  }
  1.2072 +
  1.2073 +  if (!mProxy) {
  1.2074 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.2075 +    return;
  1.2076 +  }
  1.2077 +
  1.2078 +  JS::Rooted<JS::Value> valToClone(cx);
  1.2079 +  if (JS_IsArrayBufferObject(aBody) || JS_IsArrayBufferViewObject(aBody) ||
  1.2080 +      file::GetDOMBlobFromJSObject(aBody)) {
  1.2081 +    valToClone.setObject(*aBody);
  1.2082 +  }
  1.2083 +  else {
  1.2084 +    JS::Rooted<JS::Value> obj(cx, JS::ObjectValue(*aBody));
  1.2085 +    JSString* bodyStr = JS::ToString(cx, obj);
  1.2086 +    if (!bodyStr) {
  1.2087 +      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  1.2088 +      return;
  1.2089 +    }
  1.2090 +    valToClone.setString(bodyStr);
  1.2091 +  }
  1.2092 +
  1.2093 +  JSStructuredCloneCallbacks* callbacks =
  1.2094 +    mWorkerPrivate->IsChromeWorker() ?
  1.2095 +    ChromeWorkerStructuredCloneCallbacks(false) :
  1.2096 +    WorkerStructuredCloneCallbacks(false);
  1.2097 +
  1.2098 +  nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
  1.2099 +
  1.2100 +  JSAutoStructuredCloneBuffer buffer;
  1.2101 +  if (!buffer.write(cx, valToClone, callbacks, &clonedObjects)) {
  1.2102 +    aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
  1.2103 +    return;
  1.2104 +  }
  1.2105 +
  1.2106 +  SendInternal(EmptyString(), Move(buffer), clonedObjects, aRv);
  1.2107 +}
  1.2108 +
  1.2109 +void
  1.2110 +XMLHttpRequest::Send(const ArrayBuffer& aBody, ErrorResult& aRv)
  1.2111 +{
  1.2112 +  JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
  1.2113 +  return Send(obj, aRv);
  1.2114 +}
  1.2115 +
  1.2116 +void
  1.2117 +XMLHttpRequest::Send(const ArrayBufferView& aBody, ErrorResult& aRv)
  1.2118 +{
  1.2119 +  JS::Rooted<JSObject*> obj(mWorkerPrivate->GetJSContext(), aBody.Obj());
  1.2120 +  return Send(obj, aRv);
  1.2121 +}
  1.2122 +
  1.2123 +void
  1.2124 +XMLHttpRequest::SendAsBinary(const nsAString& aBody, ErrorResult& aRv)
  1.2125 +{
  1.2126 +  NS_NOTYETIMPLEMENTED("Implement me!");
  1.2127 +  aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
  1.2128 +  return;
  1.2129 +}
  1.2130 +
  1.2131 +void
  1.2132 +XMLHttpRequest::Abort(ErrorResult& aRv)
  1.2133 +{
  1.2134 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.2135 +
  1.2136 +  if (mCanceled) {
  1.2137 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2138 +  }
  1.2139 +
  1.2140 +  if (!mProxy) {
  1.2141 +    return;
  1.2142 +  }
  1.2143 +
  1.2144 +  MaybeDispatchPrematureAbortEvents(aRv);
  1.2145 +  if (aRv.Failed()) {
  1.2146 +    return;
  1.2147 +  }
  1.2148 +
  1.2149 +  mProxy->mOuterEventStreamId++;
  1.2150 +
  1.2151 +  nsRefPtr<AbortRunnable> runnable = new AbortRunnable(mWorkerPrivate, mProxy);
  1.2152 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.2153 +    aRv.Throw(NS_ERROR_FAILURE);
  1.2154 +    return;
  1.2155 +  }
  1.2156 +}
  1.2157 +
  1.2158 +void
  1.2159 +XMLHttpRequest::GetResponseHeader(const nsACString& aHeader,
  1.2160 +                                  nsACString& aResponseHeader, ErrorResult& aRv)
  1.2161 +{
  1.2162 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.2163 +
  1.2164 +  if (mCanceled) {
  1.2165 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2166 +    return;
  1.2167 +  }
  1.2168 +
  1.2169 +  if (!mProxy) {
  1.2170 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.2171 +    return;
  1.2172 +  }
  1.2173 +
  1.2174 +  nsCString responseHeader;
  1.2175 +  nsRefPtr<GetResponseHeaderRunnable> runnable =
  1.2176 +    new GetResponseHeaderRunnable(mWorkerPrivate, mProxy, aHeader,
  1.2177 +                                  responseHeader);
  1.2178 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.2179 +    aRv.Throw(NS_ERROR_FAILURE);
  1.2180 +    return;
  1.2181 +  }
  1.2182 +  aResponseHeader = responseHeader;
  1.2183 +}
  1.2184 +
  1.2185 +void
  1.2186 +XMLHttpRequest::GetAllResponseHeaders(nsACString& aResponseHeaders,
  1.2187 +                                      ErrorResult& aRv)
  1.2188 +{
  1.2189 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.2190 +
  1.2191 +  if (mCanceled) {
  1.2192 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2193 +    return;
  1.2194 +  }
  1.2195 +
  1.2196 +  if (!mProxy) {
  1.2197 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.2198 +    return;
  1.2199 +  }
  1.2200 +
  1.2201 +  nsCString responseHeaders;
  1.2202 +  nsRefPtr<GetAllResponseHeadersRunnable> runnable =
  1.2203 +    new GetAllResponseHeadersRunnable(mWorkerPrivate, mProxy, responseHeaders);
  1.2204 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.2205 +    aRv.Throw(NS_ERROR_FAILURE);
  1.2206 +    return;
  1.2207 +  }
  1.2208 +
  1.2209 +  aResponseHeaders = responseHeaders;
  1.2210 +}
  1.2211 +
  1.2212 +void
  1.2213 +XMLHttpRequest::OverrideMimeType(const nsAString& aMimeType, ErrorResult& aRv)
  1.2214 +{
  1.2215 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.2216 +
  1.2217 +  if (mCanceled) {
  1.2218 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2219 +    return;
  1.2220 +  }
  1.2221 +
  1.2222 +  // We're supposed to throw if the state is not OPENED or HEADERS_RECEIVED. We
  1.2223 +  // can detect OPENED really easily but we can't detect HEADERS_RECEIVED in a
  1.2224 +  // non-racy way until the XHR state machine actually runs on this thread
  1.2225 +  // (bug 671047). For now we're going to let this work only if the Send()
  1.2226 +  // method has not been called.
  1.2227 +  if (!mProxy || SendInProgress()) {
  1.2228 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.2229 +    return;
  1.2230 +  }
  1.2231 +
  1.2232 +  nsRefPtr<OverrideMimeTypeRunnable> runnable =
  1.2233 +    new OverrideMimeTypeRunnable(mWorkerPrivate, mProxy, aMimeType);
  1.2234 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.2235 +    aRv.Throw(NS_ERROR_FAILURE);
  1.2236 +    return;
  1.2237 +  }
  1.2238 +}
  1.2239 +
  1.2240 +void
  1.2241 +XMLHttpRequest::SetResponseType(XMLHttpRequestResponseType aResponseType,
  1.2242 +                                ErrorResult& aRv)
  1.2243 +{
  1.2244 +  mWorkerPrivate->AssertIsOnWorkerThread();
  1.2245 +
  1.2246 +  if (mCanceled) {
  1.2247 +    aRv.Throw(UNCATCHABLE_EXCEPTION);
  1.2248 +    return;
  1.2249 +  }
  1.2250 +
  1.2251 +  if (!mProxy || SendInProgress()) {
  1.2252 +    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1.2253 +    return;
  1.2254 +  }
  1.2255 +
  1.2256 +  // "document" is fine for the main thread but not for a worker. Short-circuit
  1.2257 +  // that here.
  1.2258 +  if (aResponseType == XMLHttpRequestResponseType::Document) {
  1.2259 +    return;
  1.2260 +  }
  1.2261 +
  1.2262 +  nsString responseType;
  1.2263 +  ConvertResponseTypeToString(aResponseType, responseType);
  1.2264 +
  1.2265 +  nsRefPtr<SetResponseTypeRunnable> runnable =
  1.2266 +    new SetResponseTypeRunnable(mWorkerPrivate, mProxy, responseType);
  1.2267 +  if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
  1.2268 +    aRv.Throw(NS_ERROR_FAILURE);
  1.2269 +    return;
  1.2270 +  }
  1.2271 +
  1.2272 +  nsString acceptedResponseTypeString;
  1.2273 +  runnable->GetResponseType(acceptedResponseTypeString);
  1.2274 +
  1.2275 +  mResponseType = ConvertStringToResponseType(acceptedResponseTypeString);
  1.2276 +}
  1.2277 +
  1.2278 +void
  1.2279 +XMLHttpRequest::GetResponse(JSContext* /* unused */,
  1.2280 +                            JS::MutableHandle<JS::Value> aResponse,
  1.2281 +                            ErrorResult& aRv)
  1.2282 +{
  1.2283 +  if (NS_SUCCEEDED(mStateData.mResponseTextResult) &&
  1.2284 +      JSVAL_IS_VOID(mStateData.mResponse)) {
  1.2285 +    MOZ_ASSERT(mStateData.mResponseText.Length());
  1.2286 +    MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult));
  1.2287 +
  1.2288 +    JSString* str =
  1.2289 +      JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(),
  1.2290 +                          mStateData.mResponseText.get(),
  1.2291 +                          mStateData.mResponseText.Length());
  1.2292 +    if (!str) {
  1.2293 +      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
  1.2294 +      return;
  1.2295 +    }
  1.2296 +
  1.2297 +    mStateData.mResponse = STRING_TO_JSVAL(str);
  1.2298 +  }
  1.2299 +
  1.2300 +  JS::ExposeValueToActiveJS(mStateData.mResponse);
  1.2301 +  aRv = mStateData.mResponseResult;
  1.2302 +  aResponse.set(mStateData.mResponse);
  1.2303 +}
  1.2304 +
  1.2305 +void
  1.2306 +XMLHttpRequest::GetResponseText(nsAString& aResponseText, ErrorResult& aRv)
  1.2307 +{
  1.2308 +  aRv = mStateData.mResponseTextResult;
  1.2309 +  aResponseText = mStateData.mResponseText;
  1.2310 +}
  1.2311 +
  1.2312 +void
  1.2313 +XMLHttpRequest::UpdateState(const StateData& aStateData)
  1.2314 +{
  1.2315 +  mStateData = aStateData;
  1.2316 +}

mercurial