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