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