LCOV - code coverage report
Current view: directory - dom/workers - XMLHttpRequestPrivate.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 888 0 0.0 %
Date: 2012-06-02 Functions: 142 0 0.0 %

       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                 : }

Generated by: LCOV version 1.7