dom/workers/XMLHttpRequest.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial