LCOV - code coverage report
Current view: directory - dom/workers - WorkerPrivate.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1584 3 0.2 %
Date: 2012-06-02 Functions: 200 1 0.5 %

       1                 : /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
       2                 : /* vim: set ts=2 et sw=2 tw=80: */
       3                 : /* ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Web Workers.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  *   The Mozilla Foundation.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      28                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : #include "WorkerPrivate.h"
      41                 : 
      42                 : #include "mozIThirdPartyUtil.h"
      43                 : #include "nsIClassInfo.h"
      44                 : #include "nsIConsoleService.h"
      45                 : #include "nsIDOMFile.h"
      46                 : #include "nsIDocument.h"
      47                 : #include "nsIJSContextStack.h"
      48                 : #include "nsIMemoryReporter.h"
      49                 : #include "nsIScriptError.h"
      50                 : #include "nsIScriptGlobalObject.h"
      51                 : #include "nsIScriptSecurityManager.h"
      52                 : #include "nsPIDOMWindow.h"
      53                 : #include "nsITextToSubURI.h"
      54                 : #include "nsITimer.h"
      55                 : #include "nsIURI.h"
      56                 : #include "nsIURL.h"
      57                 : #include "nsIXPConnect.h"
      58                 : 
      59                 : #include "jsfriendapi.h"
      60                 : #include "jsdbgapi.h"
      61                 : #include "jsfriendapi.h"
      62                 : #include "jsprf.h"
      63                 : #include "js/MemoryMetrics.h"
      64                 : 
      65                 : #include "nsAlgorithm.h"
      66                 : #include "nsContentUtils.h"
      67                 : #include "nsDOMClassInfo.h"
      68                 : #include "nsDOMJSUtils.h"
      69                 : #include "nsGUIEvent.h"
      70                 : #include "nsJSEnvironment.h"
      71                 : #include "nsJSUtils.h"
      72                 : #include "nsNetUtil.h"
      73                 : #include "nsThreadUtils.h"
      74                 : #include "xpcpublic.h"
      75                 : 
      76                 : #include "Events.h"
      77                 : #include "Exceptions.h"
      78                 : #include "File.h"
      79                 : #include "Principal.h"
      80                 : #include "RuntimeService.h"
      81                 : #include "ScriptLoader.h"
      82                 : #include "Worker.h"
      83                 : #include "WorkerFeature.h"
      84                 : #include "WorkerScope.h"
      85                 : #ifdef ANDROID
      86                 : #include <android/log.h>
      87                 : #endif
      88                 : 
      89                 : #include "WorkerInlines.h"
      90                 : 
      91                 : #if 0 // Define to run GC more often.
      92                 : #define EXTRA_GC
      93                 : #endif
      94                 : 
      95                 : // GC will run once every thirty seconds during normal execution.
      96                 : #define NORMAL_GC_TIMER_DELAY_MS 30000
      97                 : 
      98                 : // GC will run five seconds after the last event is processed.
      99                 : #define IDLE_GC_TIMER_DELAY_MS 5000
     100                 : 
     101                 : using mozilla::MutexAutoLock;
     102                 : using mozilla::TimeDuration;
     103                 : using mozilla::TimeStamp;
     104                 : using mozilla::dom::workers::exceptions::ThrowDOMExceptionForCode;
     105                 : using mozilla::xpconnect::memory::ReportJSRuntimeExplicitTreeStats;
     106                 : 
     107                 : USING_WORKERS_NAMESPACE
     108                 : 
     109                 : namespace {
     110                 : 
     111                 : const char gErrorChars[] = "error";
     112                 : const char gMessageChars[] = "message";
     113                 : 
     114                 : template <class T>
     115                 : class AutoPtrComparator
     116                 : {
     117                 :   typedef nsAutoPtr<T> A;
     118                 :   typedef T* B;
     119                 : 
     120                 : public:
     121               0 :   bool Equals(const A& a, const B& b) const {
     122               0 :     return a && b ? *a == *b : !a && !b ? true : false;
     123                 :   }
     124               0 :   bool LessThan(const A& a, const B& b) const {
     125               0 :     return a && b ? *a < *b : b ? true : false;
     126                 :   }
     127                 : };
     128                 : 
     129                 : template <class T>
     130                 : inline AutoPtrComparator<T>
     131               0 : GetAutoPtrComparator(const nsTArray<nsAutoPtr<T> >&)
     132                 : {
     133               0 :   return AutoPtrComparator<T>();
     134                 : }
     135                 : 
     136                 : // Specialize this if there's some class that has multiple nsISupports bases.
     137                 : template <class T>
     138                 : struct ISupportsBaseInfo
     139                 : {
     140                 :   typedef T ISupportsBase;
     141                 : };
     142                 : 
     143                 : template <template <class> class SmartPtr, class T>
     144                 : inline void
     145               0 : SwapToISupportsArray(SmartPtr<T>& aSrc,
     146                 :                      nsTArray<nsCOMPtr<nsISupports> >& aDest)
     147                 : {
     148               0 :   nsCOMPtr<nsISupports>* dest = aDest.AppendElement();
     149                 : 
     150               0 :   T* raw = nsnull;
     151               0 :   aSrc.swap(raw);
     152                 : 
     153                 :   nsISupports* rawSupports =
     154               0 :     static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
     155               0 :   dest->swap(rawSupports);
     156               0 : }
     157                 : 
     158               0 : NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker")
     159                 : 
     160                 : struct WorkerJSRuntimeStats : public JS::RuntimeStats
     161               0 : {
     162               0 :   WorkerJSRuntimeStats()
     163               0 :    : JS::RuntimeStats(JsWorkerMallocSizeOf) { }
     164                 : 
     165               0 :   virtual void initExtraCompartmentStats(JSCompartment *c,
     166                 :                                          JS::CompartmentStats *cstats) MOZ_OVERRIDE
     167                 :   {
     168               0 :     MOZ_ASSERT(!cstats->extra);
     169                 :     
     170                 :     // ReportJSRuntimeExplicitTreeStats expects that cstats->extra is a char pointer
     171               0 :     const char *name = js::IsAtomsCompartment(c) ? "Web Worker Atoms" : "Web Worker";
     172               0 :     cstats->extra = const_cast<char *>(name);
     173               0 :   }
     174                 : };
     175                 :   
     176                 : class WorkerMemoryReporter : public nsIMemoryMultiReporter
     177               0 : {
     178                 :   WorkerPrivate* mWorkerPrivate;
     179                 :   nsCString mAddressString;
     180                 :   nsCString mPathPrefix;
     181                 : 
     182                 : public:
     183                 :   NS_DECL_ISUPPORTS
     184                 : 
     185               0 :   WorkerMemoryReporter(WorkerPrivate* aWorkerPrivate)
     186               0 :   : mWorkerPrivate(aWorkerPrivate)
     187                 :   {
     188               0 :     aWorkerPrivate->AssertIsOnWorkerThread();
     189                 : 
     190               0 :     nsCString escapedDomain(aWorkerPrivate->Domain());
     191               0 :     escapedDomain.ReplaceChar('/', '\\');
     192                 : 
     193               0 :     NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL());
     194               0 :     escapedURL.ReplaceChar('/', '\\');
     195                 : 
     196                 :     {
     197                 :       // 64bit address plus '0x' plus null terminator.
     198                 :       char address[21];
     199                 :       uint32_t addressSize =
     200               0 :         JS_snprintf(address, sizeof(address), "0x%llx", aWorkerPrivate);
     201               0 :       if (addressSize != uint32_t(-1)) {
     202               0 :         mAddressString.Assign(address, addressSize);
     203                 :       }
     204                 :       else {
     205               0 :         NS_WARNING("JS_snprintf failed!");
     206               0 :         mAddressString.AssignLiteral("<unknown address>");
     207                 :       }
     208                 :     }
     209                 : 
     210               0 :     mPathPrefix = NS_LITERAL_CSTRING("explicit/dom/workers(") +
     211               0 :                   escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
     212               0 :                   escapedURL + NS_LITERAL_CSTRING(", ") + mAddressString +
     213               0 :                   NS_LITERAL_CSTRING(")/");
     214               0 :   }
     215                 : 
     216                 :   nsresult
     217               0 :   CollectForRuntime(bool aIsQuick, void* aData)
     218                 :   {
     219               0 :     AssertIsOnMainThread();
     220                 : 
     221               0 :     if (mWorkerPrivate) {
     222                 :       bool disabled;
     223               0 :       if (!mWorkerPrivate->BlockAndCollectRuntimeStats(aIsQuick, aData, &disabled)) {
     224               0 :         return NS_ERROR_FAILURE;
     225                 :       }
     226                 : 
     227                 :       // Don't ever try to talk to the worker again.
     228               0 :       if (disabled) {
     229                 : #ifdef DEBUG
     230                 :         {
     231               0 :           nsCAutoString message("Unable to report memory for ");
     232               0 :           if (mWorkerPrivate->IsChromeWorker()) {
     233               0 :             message.AppendLiteral("Chrome");
     234                 :           }
     235               0 :           message += NS_LITERAL_CSTRING("Worker (") + mAddressString +
     236               0 :                      NS_LITERAL_CSTRING(")! It is either using ctypes or is in "
     237               0 :                                         "the process of being destroyed");
     238               0 :           NS_WARNING(message.get());
     239                 :         }
     240                 : #endif
     241               0 :         mWorkerPrivate = nsnull;
     242                 :       }
     243                 :     }
     244               0 :     return NS_OK;
     245                 :   }
     246                 : 
     247               0 :   NS_IMETHOD GetName(nsACString &aName)
     248                 :   {
     249               0 :       aName.AssignLiteral("workers");
     250               0 :       return NS_OK;
     251                 :   }
     252                 : 
     253                 :   NS_IMETHOD
     254               0 :   CollectReports(nsIMemoryMultiReporterCallback* aCallback,
     255                 :                  nsISupports* aClosure)
     256                 :   {
     257               0 :     AssertIsOnMainThread();
     258                 : 
     259               0 :     WorkerJSRuntimeStats rtStats;
     260               0 :     nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
     261               0 :     if (NS_FAILED(rv)) {
     262               0 :       return rv;
     263                 :     }
     264                 : 
     265                 :     // Always report, even if we're disabled, so that we at least get an entry
     266                 :     // in about::memory.
     267               0 :     rv = ReportJSRuntimeExplicitTreeStats(rtStats, mPathPrefix, aCallback, aClosure);
     268               0 :     if (NS_FAILED(rv)) {
     269               0 :       return rv;
     270                 :     }
     271                 : 
     272               0 :     return NS_OK;
     273                 :   }
     274                 : 
     275                 :   NS_IMETHOD
     276               0 :   GetExplicitNonHeap(PRInt64 *aAmount)
     277                 :   {
     278               0 :     AssertIsOnMainThread();
     279                 : 
     280               0 :     return CollectForRuntime(/* isQuick = */true, aAmount);
     281                 :   }
     282                 : };
     283                 : 
     284               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter)
     285                 : 
     286                 : struct WorkerStructuredCloneCallbacks
     287                 : {
     288                 :   static JSObject*
     289               0 :   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
     290                 :        uint32_t aData, void* aClosure)
     291                 :   {
     292                 :     // See if object is a nsIDOMFile pointer.
     293               0 :     if (aTag == DOMWORKER_SCTAG_FILE) {
     294               0 :       JS_ASSERT(!aData);
     295                 : 
     296                 :       nsIDOMFile* file;
     297               0 :       if (JS_ReadBytes(aReader, &file, sizeof(file))) {
     298               0 :         JS_ASSERT(file);
     299                 : 
     300                 : #ifdef DEBUG
     301                 :         {
     302                 :           // File should not be mutable.
     303               0 :           nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
     304                 :           bool isMutable;
     305               0 :           NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
     306                 :                        !isMutable,
     307                 :                        "Only immutable file should be passed to worker");
     308                 :         }
     309                 : #endif
     310                 : 
     311                 :         // nsIDOMFiles should be threadsafe, thus we will use the same instance
     312                 :         // in the worker.
     313               0 :         JSObject* jsFile = file::CreateFile(aCx, file);
     314               0 :         return jsFile;
     315                 :       }
     316                 :     }
     317                 :     // See if object is a nsIDOMBlob pointer.
     318               0 :     else if (aTag == DOMWORKER_SCTAG_BLOB) {
     319               0 :       JS_ASSERT(!aData);
     320                 : 
     321                 :       nsIDOMBlob* blob;
     322               0 :       if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
     323               0 :         JS_ASSERT(blob);
     324                 : 
     325                 : #ifdef DEBUG
     326                 :         {
     327                 :           // Blob should not be mutable.
     328               0 :           nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
     329                 :           bool isMutable;
     330               0 :           NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
     331                 :                        !isMutable,
     332                 :                        "Only immutable blob should be passed to worker");
     333                 :         }
     334                 : #endif
     335                 : 
     336                 :         // nsIDOMBlob should be threadsafe, thus we will use the same instance
     337                 :         // in the worker.
     338               0 :         JSObject* jsBlob = file::CreateBlob(aCx, blob);
     339               0 :         return jsBlob;
     340                 :       }
     341                 :     }
     342                 : 
     343               0 :     Error(aCx, 0);
     344               0 :     return nsnull;
     345                 :   }
     346                 : 
     347                 :   static JSBool
     348               0 :   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
     349                 :         void* aClosure)
     350                 :   {
     351               0 :     NS_ASSERTION(aClosure, "Null pointer!");
     352                 : 
     353                 :     // We'll stash any nsISupports pointers that need to be AddRef'd here.
     354                 :     nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
     355               0 :       static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
     356                 : 
     357                 :     // See if this is a File object.
     358                 :     {
     359               0 :       nsIDOMFile* file = file::GetDOMFileFromJSObject(aObj);
     360               0 :       if (file) {
     361               0 :         if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
     362               0 :             JS_WriteBytes(aWriter, &file, sizeof(file))) {
     363               0 :           clonedObjects->AppendElement(file);
     364               0 :           return true;
     365                 :         }
     366                 :       }
     367                 :     }
     368                 : 
     369                 :     // See if this is a Blob object.
     370                 :     {
     371               0 :       nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aObj);
     372               0 :       if (blob) {
     373               0 :         nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
     374               0 :         if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false)) &&
     375               0 :             JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
     376               0 :             JS_WriteBytes(aWriter, &blob, sizeof(blob))) {
     377               0 :           clonedObjects->AppendElement(blob);
     378               0 :           return true;
     379                 :         }
     380                 :       }
     381                 :     }
     382                 : 
     383               0 :     Error(aCx, 0);
     384               0 :     return false;
     385                 :   }
     386                 : 
     387                 :   static void
     388               0 :   Error(JSContext* aCx, uint32_t /* aErrorId */)
     389                 :   {
     390               0 :     ThrowDOMExceptionForCode(aCx, DATA_CLONE_ERR);
     391               0 :   }
     392                 : };
     393                 : 
     394                 : JSStructuredCloneCallbacks gWorkerStructuredCloneCallbacks = {
     395                 :   WorkerStructuredCloneCallbacks::Read,
     396                 :   WorkerStructuredCloneCallbacks::Write,
     397                 :   WorkerStructuredCloneCallbacks::Error
     398                 : };
     399                 : 
     400                 : struct MainThreadWorkerStructuredCloneCallbacks
     401                 : {
     402                 :   static JSObject*
     403               0 :   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
     404                 :        uint32_t aData, void* aClosure)
     405                 :   {
     406               0 :     AssertIsOnMainThread();
     407                 : 
     408                 :     // See if object is a nsIDOMFile pointer.
     409               0 :     if (aTag == DOMWORKER_SCTAG_FILE) {
     410               0 :       JS_ASSERT(!aData);
     411                 : 
     412                 :       nsIDOMFile* file;
     413               0 :       if (JS_ReadBytes(aReader, &file, sizeof(file))) {
     414               0 :         JS_ASSERT(file);
     415                 : 
     416                 : #ifdef DEBUG
     417                 :         {
     418                 :           // File should not be mutable.
     419               0 :           nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
     420                 :           bool isMutable;
     421               0 :           NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) &&
     422                 :                        !isMutable,
     423                 :                        "Only immutable file should be passed to worker");
     424                 :         }
     425                 : #endif
     426                 : 
     427                 :         // nsIDOMFiles should be threadsafe, thus we will use the same instance
     428                 :         // on the main thread.
     429                 :         jsval wrappedFile;
     430                 :         nsresult rv =
     431                 :           nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file,
     432               0 :                                      &NS_GET_IID(nsIDOMFile), &wrappedFile);
     433               0 :         if (NS_FAILED(rv)) {
     434               0 :           Error(aCx, DATA_CLONE_ERR);
     435               0 :           return nsnull;
     436                 :         }
     437                 : 
     438               0 :         return JSVAL_TO_OBJECT(wrappedFile);
     439                 :       }
     440                 :     }
     441                 :     // See if object is a nsIDOMBlob pointer.
     442               0 :     else if (aTag == DOMWORKER_SCTAG_BLOB) {
     443               0 :       JS_ASSERT(!aData);
     444                 : 
     445                 :       nsIDOMBlob* blob;
     446               0 :       if (JS_ReadBytes(aReader, &blob, sizeof(blob))) {
     447               0 :         JS_ASSERT(blob);
     448                 : 
     449                 : #ifdef DEBUG
     450                 :         {
     451                 :           // Blob should not be mutable.
     452               0 :           nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
     453                 :           bool isMutable;
     454               0 :           NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) &&
     455                 :                        !isMutable,
     456                 :                        "Only immutable blob should be passed to worker");
     457                 :         }
     458                 : #endif
     459                 : 
     460                 :         // nsIDOMBlobs should be threadsafe, thus we will use the same instance
     461                 :         // on the main thread.
     462                 :         jsval wrappedBlob;
     463                 :         nsresult rv =
     464                 :           nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob,
     465               0 :                                      &NS_GET_IID(nsIDOMBlob), &wrappedBlob);
     466               0 :         if (NS_FAILED(rv)) {
     467               0 :           Error(aCx, DATA_CLONE_ERR);
     468               0 :           return nsnull;
     469                 :         }
     470                 : 
     471               0 :         return JSVAL_TO_OBJECT(wrappedBlob);
     472                 :       }
     473                 :     }
     474                 : 
     475                 :     JSObject* clone =
     476               0 :       WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, aClosure);
     477               0 :     if (clone) {
     478               0 :       return clone;
     479                 :     }
     480                 : 
     481               0 :     JS_ClearPendingException(aCx);
     482               0 :     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nsnull);
     483                 :   }
     484                 : 
     485                 :   static JSBool
     486               0 :   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
     487                 :         void* aClosure)
     488                 :   {
     489               0 :     AssertIsOnMainThread();
     490                 : 
     491               0 :     NS_ASSERTION(aClosure, "Null pointer!");
     492                 : 
     493                 :     // We'll stash any nsISupports pointers that need to be AddRef'd here.
     494                 :     nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
     495               0 :       static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
     496                 : 
     497                 :     // See if this is a wrapped native.
     498               0 :     nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
     499               0 :     nsContentUtils::XPConnect()->
     500               0 :       GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative));
     501                 : 
     502               0 :     if (wrappedNative) {
     503                 :       // Get the raw nsISupports out of it.
     504               0 :       nsISupports* wrappedObject = wrappedNative->Native();
     505               0 :       NS_ASSERTION(wrappedObject, "Null pointer?!");
     506                 : 
     507                 :       // See if the wrapped native is a nsIDOMFile.
     508               0 :       nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject);
     509               0 :       if (file) {
     510               0 :         nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file);
     511               0 :         if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(false))) {
     512               0 :           nsIDOMFile* filePtr = file;
     513               0 :           if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) &&
     514               0 :               JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) {
     515               0 :             clonedObjects->AppendElement(file);
     516               0 :             return true;
     517                 :           }
     518                 :         }
     519                 :       }
     520                 : 
     521                 :       // See if the wrapped native is a nsIDOMBlob.
     522               0 :       nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject);
     523               0 :       if (blob) {
     524               0 :         nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob);
     525               0 :         if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(false))) {
     526               0 :           nsIDOMBlob* blobPtr = blob;
     527               0 :           if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
     528               0 :               JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) {
     529               0 :             clonedObjects->AppendElement(blob);
     530               0 :             return true;
     531                 :           }
     532                 :         }
     533                 :       }
     534                 :     }
     535                 : 
     536                 :     JSBool ok =
     537               0 :       WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
     538               0 :     if (ok) {
     539               0 :       return ok;
     540                 :     }
     541                 : 
     542               0 :     JS_ClearPendingException(aCx);
     543               0 :     return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull);
     544                 :   }
     545                 : 
     546                 :   static void
     547               0 :   Error(JSContext* aCx, uint32_t aErrorId)
     548                 :   {
     549               0 :     AssertIsOnMainThread();
     550                 : 
     551               0 :     NS_DOMStructuredCloneError(aCx, aErrorId);
     552               0 :   }
     553                 : };
     554                 : 
     555                 : JSStructuredCloneCallbacks gMainThreadWorkerStructuredCloneCallbacks = {
     556                 :   MainThreadWorkerStructuredCloneCallbacks::Read,
     557                 :   MainThreadWorkerStructuredCloneCallbacks::Write,
     558                 :   MainThreadWorkerStructuredCloneCallbacks::Error
     559                 : };
     560                 : 
     561                 : struct ChromeWorkerStructuredCloneCallbacks
     562                 : {
     563                 :   static JSObject*
     564               0 :   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
     565                 :        uint32_t aData, void* aClosure)
     566                 :   {
     567                 :     return WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
     568               0 :                                                 aClosure);
     569                 :   }
     570                 : 
     571                 :   static JSBool
     572               0 :   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
     573                 :         void* aClosure)
     574                 :   {
     575               0 :     return WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
     576                 :   }
     577                 : 
     578                 :   static void
     579               0 :   Error(JSContext* aCx, uint32_t aErrorId)
     580                 :   {
     581               0 :     return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId);
     582                 :   }
     583                 : };
     584                 : 
     585                 : JSStructuredCloneCallbacks gChromeWorkerStructuredCloneCallbacks = {
     586                 :   ChromeWorkerStructuredCloneCallbacks::Read,
     587                 :   ChromeWorkerStructuredCloneCallbacks::Write,
     588                 :   ChromeWorkerStructuredCloneCallbacks::Error
     589                 : };
     590                 : 
     591                 : struct MainThreadChromeWorkerStructuredCloneCallbacks
     592                 : {
     593                 :   static JSObject*
     594               0 :   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
     595                 :        uint32_t aData, void* aClosure)
     596                 :   {
     597               0 :     AssertIsOnMainThread();
     598                 : 
     599                 :     JSObject* clone =
     600                 :       MainThreadWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
     601               0 :                                                      aClosure);
     602               0 :     if (clone) {
     603               0 :       return clone;
     604                 :     }
     605                 : 
     606                 :     clone =
     607                 :       ChromeWorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
     608               0 :                                                  aClosure);
     609               0 :     if (clone) {
     610               0 :       return clone;
     611                 :     }
     612                 : 
     613               0 :     JS_ClearPendingException(aCx);
     614               0 :     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nsnull);
     615                 :   }
     616                 : 
     617                 :   static JSBool
     618               0 :   Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
     619                 :         void* aClosure)
     620                 :   {
     621               0 :     AssertIsOnMainThread();
     622                 : 
     623               0 :     if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
     624               0 :                                                         aClosure) ||
     625                 :         ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
     626               0 :                                                     aClosure) ||
     627               0 :         NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull)) {
     628               0 :       return true;
     629                 :     }
     630                 : 
     631               0 :     return false;
     632                 :   }
     633                 : 
     634                 :   static void
     635               0 :   Error(JSContext* aCx, uint32_t aErrorId)
     636                 :   {
     637               0 :     AssertIsOnMainThread();
     638                 : 
     639               0 :     NS_DOMStructuredCloneError(aCx, aErrorId);
     640               0 :   }
     641                 : };
     642                 : 
     643                 : JSStructuredCloneCallbacks gMainThreadChromeWorkerStructuredCloneCallbacks = {
     644                 :   MainThreadChromeWorkerStructuredCloneCallbacks::Read,
     645                 :   MainThreadChromeWorkerStructuredCloneCallbacks::Write,
     646                 :   MainThreadChromeWorkerStructuredCloneCallbacks::Error
     647                 : };
     648                 : 
     649                 : class WorkerFinishedRunnable : public WorkerControlRunnable
     650               0 : {
     651                 :   WorkerPrivate* mFinishedWorker;
     652                 :   nsCOMPtr<nsIThread> mThread;
     653                 : 
     654                 :   class MainThreadReleaseRunnable : public nsRunnable
     655               0 :   {
     656                 :     nsCOMPtr<nsIThread> mThread;
     657                 :     nsTArray<nsCOMPtr<nsISupports> > mDoomed;
     658                 : 
     659                 :   public:
     660               0 :     MainThreadReleaseRunnable(nsCOMPtr<nsIThread>& aThread,
     661                 :                               nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
     662               0 :     {
     663               0 :       mThread.swap(aThread);
     664               0 :       mDoomed.SwapElements(aDoomed);
     665               0 :     }
     666                 : 
     667                 :     NS_IMETHOD
     668               0 :     Run()
     669                 :     {
     670               0 :       mDoomed.Clear();
     671                 : 
     672               0 :       if (mThread) {
     673               0 :         RuntimeService* runtime = RuntimeService::GetService();
     674               0 :         NS_ASSERTION(runtime, "This should never be null!");
     675                 : 
     676               0 :         runtime->NoteIdleThread(mThread);
     677                 :       }
     678                 : 
     679               0 :       return NS_OK;
     680                 :     }
     681                 :   };
     682                 : 
     683                 : public:
     684               0 :   WorkerFinishedRunnable(WorkerPrivate* aWorkerPrivate,
     685                 :                          WorkerPrivate* aFinishedWorker,
     686                 :                          nsIThread* aFinishedThread)
     687                 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
     688               0 :     mFinishedWorker(aFinishedWorker), mThread(aFinishedThread)
     689               0 :   { }
     690                 : 
     691                 :   bool
     692               0 :   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     693                 :   {
     694                 :     // Silence bad assertions.
     695               0 :     return true;
     696                 :   }
     697                 : 
     698                 :   void
     699               0 :   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
     700                 :                bool aDispatchResult)
     701                 :   {
     702                 :     // Silence bad assertions.
     703               0 :   }
     704                 : 
     705                 :   bool
     706               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     707                 :   {
     708               0 :     nsTArray<nsCOMPtr<nsISupports> > doomed;
     709               0 :     mFinishedWorker->ForgetMainThreadObjects(doomed);
     710                 : 
     711                 :     nsRefPtr<MainThreadReleaseRunnable> runnable =
     712               0 :       new MainThreadReleaseRunnable(mThread, doomed);
     713               0 :     if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
     714               0 :       NS_WARNING("Failed to dispatch, going to leak!");
     715                 :     }
     716                 : 
     717               0 :     mFinishedWorker->FinalizeInstance(aCx, false);
     718                 : 
     719               0 :     RuntimeService* runtime = RuntimeService::GetService();
     720               0 :     NS_ASSERTION(runtime, "This should never be null!");
     721                 : 
     722               0 :     runtime->UnregisterWorker(aCx, mFinishedWorker);
     723                 : 
     724               0 :     delete mFinishedWorker;
     725               0 :     return true;
     726                 :   }
     727                 : };
     728                 : 
     729                 : class TopLevelWorkerFinishedRunnable : public nsRunnable
     730               0 : {
     731                 :   WorkerPrivate* mFinishedWorker;
     732                 :   nsCOMPtr<nsIThread> mThread;
     733                 : 
     734                 : public:
     735               0 :   TopLevelWorkerFinishedRunnable(WorkerPrivate* aFinishedWorker,
     736                 :                                  nsIThread* aFinishedThread)
     737               0 :   : mFinishedWorker(aFinishedWorker), mThread(aFinishedThread)
     738                 :   {
     739               0 :     aFinishedWorker->AssertIsOnWorkerThread();
     740               0 :   }
     741                 : 
     742                 :   NS_IMETHOD
     743               0 :   Run()
     744                 :   {
     745               0 :     AssertIsOnMainThread();
     746                 : 
     747               0 :     RuntimeService::AutoSafeJSContext cx;
     748                 : 
     749               0 :     mFinishedWorker->FinalizeInstance(cx, false);
     750                 : 
     751               0 :     RuntimeService* runtime = RuntimeService::GetService();
     752               0 :     NS_ASSERTION(runtime, "This should never be null!");
     753                 : 
     754               0 :     runtime->UnregisterWorker(cx, mFinishedWorker);
     755                 : 
     756               0 :     if (mThread) {
     757               0 :       runtime->NoteIdleThread(mThread);
     758                 :     }
     759                 : 
     760               0 :     delete mFinishedWorker;
     761                 : 
     762               0 :     return NS_OK;
     763                 :   }
     764                 : };
     765                 : 
     766                 : class ModifyBusyCountRunnable : public WorkerControlRunnable
     767               0 : {
     768                 :   bool mIncrease;
     769                 : 
     770                 : public:
     771               0 :   ModifyBusyCountRunnable(WorkerPrivate* aWorkerPrivate, bool aIncrease)
     772                 :   : WorkerControlRunnable(aWorkerPrivate, ParentThread, UnchangedBusyCount),
     773               0 :     mIncrease(aIncrease)
     774               0 :   { }
     775                 : 
     776                 :   bool
     777               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     778                 :   {
     779               0 :     return aWorkerPrivate->ModifyBusyCount(aCx, mIncrease);
     780                 :   }
     781                 : 
     782                 :   void
     783               0 :   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
     784                 :   {
     785               0 :     if (mIncrease) {
     786               0 :       WorkerControlRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
     787               0 :       return;
     788                 :     }
     789                 :     // Don't do anything here as it's possible that aWorkerPrivate has been
     790                 :     // deleted.
     791                 :   }
     792                 : };
     793                 : 
     794                 : class CompileScriptRunnable : public WorkerRunnable
     795               0 : {
     796                 : public:
     797               0 :   CompileScriptRunnable(WorkerPrivate* aWorkerPrivate)
     798                 :   : WorkerRunnable(aWorkerPrivate, WorkerThread, ModifyBusyCount,
     799               0 :                    SkipWhenClearing)
     800               0 :   { }
     801                 : 
     802                 :   bool
     803               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     804                 :   {
     805               0 :     JSObject* global = CreateDedicatedWorkerGlobalScope(aCx);
     806               0 :     if (!global) {
     807               0 :       NS_WARNING("Failed to make global!");
     808               0 :       return false;
     809                 :     }
     810                 : 
     811               0 :     JSAutoEnterCompartment ac;
     812               0 :     if (!ac.enter(aCx, global)) {
     813               0 :       NS_WARNING("Failed to enter compartment!");
     814               0 :       return false;
     815                 :     }
     816                 : 
     817               0 :     JS_SetGlobalObject(aCx, global);
     818                 : 
     819               0 :     return scriptloader::LoadWorkerScript(aCx);
     820                 :   }
     821                 : };
     822                 : 
     823                 : class CloseEventRunnable : public WorkerRunnable
     824               0 : {
     825                 : public:
     826               0 :   CloseEventRunnable(WorkerPrivate* aWorkerPrivate)
     827                 :   : WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
     828               0 :                    SkipWhenClearing)
     829               0 :   { }
     830                 : 
     831                 :   bool
     832               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     833                 :   {
     834               0 :     JSObject* target = JS_GetGlobalObject(aCx);
     835               0 :     NS_ASSERTION(target, "This must never be null!");
     836                 : 
     837               0 :     aWorkerPrivate->CloseHandlerStarted();
     838                 : 
     839               0 :     JSString* type = JS_InternString(aCx, "close");
     840               0 :     if (!type) {
     841               0 :       return false;
     842                 :     }
     843                 : 
     844                 :     JSObject* event = events::CreateGenericEvent(aCx, type, false, false,
     845               0 :                                                  false);
     846               0 :     if (!event) {
     847               0 :       return false;
     848                 :     }
     849                 : 
     850                 :     bool ignored;
     851               0 :     return events::DispatchEventToTarget(aCx, target, event, &ignored);
     852                 :   }
     853                 : 
     854                 :   void
     855               0 :   PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult)
     856                 :   {
     857                 :     // Report errors.
     858               0 :     WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
     859                 : 
     860                 :     // Match the busy count increase from NotifyRunnable.
     861               0 :     if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
     862               0 :       JS_ReportPendingException(aCx);
     863                 :     }
     864                 : 
     865               0 :     aWorkerPrivate->CloseHandlerFinished();
     866               0 :   }
     867                 : };
     868                 : 
     869                 : class MessageEventRunnable : public WorkerRunnable
     870               0 : {
     871                 :   uint64_t* mData;
     872                 :   size_t mDataByteCount;
     873                 :   nsTArray<nsCOMPtr<nsISupports> > mClonedObjects;
     874                 : 
     875                 : public:
     876               0 :   MessageEventRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
     877                 :                        JSAutoStructuredCloneBuffer& aData,
     878                 :                        nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects)
     879                 :   : WorkerRunnable(aWorkerPrivate, aTarget, aTarget == WorkerThread ?
     880                 :                                                        ModifyBusyCount :
     881                 :                                                        UnchangedBusyCount,
     882               0 :                    SkipWhenClearing)
     883                 :   {
     884               0 :     aData.steal(&mData, &mDataByteCount);
     885                 : 
     886               0 :     if (!mClonedObjects.SwapElements(aClonedObjects)) {
     887               0 :       NS_ERROR("This should never fail!");
     888                 :     }
     889               0 :   }
     890                 : 
     891                 :   bool
     892               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     893                 :   {
     894               0 :     JSAutoStructuredCloneBuffer buffer;
     895               0 :     buffer.adopt(mData, mDataByteCount);
     896                 : 
     897               0 :     mData = nsnull;
     898               0 :     mDataByteCount = 0;
     899                 : 
     900                 :     bool mainRuntime;
     901                 :     JSObject* target;
     902               0 :     if (mTarget == ParentThread) {
     903               0 :       mainRuntime = !aWorkerPrivate->GetParent();
     904                 : 
     905               0 :       target = aWorkerPrivate->GetJSObject();
     906                 : 
     907                 :       // Don't fire this event if the JS object has ben disconnected from the
     908                 :       // private object.
     909               0 :       if (!target) {
     910               0 :         return true;
     911                 :       }
     912                 : 
     913               0 :       if (aWorkerPrivate->IsSuspended()) {
     914               0 :         aWorkerPrivate->QueueRunnable(this);
     915               0 :         buffer.steal(&mData, &mDataByteCount);
     916               0 :         return true;
     917                 :       }
     918                 : 
     919               0 :       aWorkerPrivate->AssertInnerWindowIsCorrect();
     920                 :     }
     921                 :     else {
     922               0 :       NS_ASSERTION(aWorkerPrivate == GetWorkerPrivateFromContext(aCx),
     923                 :                    "Badness!");
     924               0 :       mainRuntime = false;
     925               0 :       target = JS_GetGlobalObject(aCx);
     926                 :     }
     927                 : 
     928               0 :     NS_ASSERTION(target, "This should never be null!");
     929                 : 
     930                 :     JSObject* event = events::CreateMessageEvent(aCx, buffer, mClonedObjects,
     931               0 :                                                  mainRuntime);
     932               0 :     if (!event) {
     933               0 :       return false;
     934                 :     }
     935                 : 
     936                 :     bool dummy;
     937               0 :     return events::DispatchEventToTarget(aCx, target, event, &dummy);
     938                 :   }
     939                 : };
     940                 : 
     941                 : class NotifyRunnable : public WorkerControlRunnable
     942               0 : {
     943                 :   bool mFromJSObjectFinalizer;
     944                 :   Status mStatus;
     945                 : 
     946                 : public:
     947               0 :   NotifyRunnable(WorkerPrivate* aWorkerPrivate, bool aFromJSObjectFinalizer,
     948                 :                  Status aStatus)
     949                 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
     950               0 :     mFromJSObjectFinalizer(aFromJSObjectFinalizer), mStatus(aStatus)
     951                 :   {
     952               0 :     NS_ASSERTION(aStatus == Terminating || aStatus == Canceling ||
     953                 :                  aStatus == Killing, "Bad status!");
     954               0 :   }
     955                 : 
     956                 :   bool
     957               0 :   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     958                 :   {
     959                 :     // Modify here, but not in PostRun! This busy count addition will be matched
     960                 :     // by the CloseEventRunnable. If we're running from a finalizer there is no
     961                 :     // need to modify the count because future changes to the busy count will
     962                 :     // have no effect.
     963                 :     return mFromJSObjectFinalizer ?
     964                 :            true :
     965               0 :            aWorkerPrivate->ModifyBusyCount(aCx, true);
     966                 :   }
     967                 : 
     968                 :   bool
     969               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     970                 :   {
     971               0 :     return aWorkerPrivate->NotifyInternal(aCx, mStatus);
     972                 :   }
     973                 : };
     974                 : 
     975                 : class CloseRunnable : public WorkerControlRunnable
     976               0 : {
     977                 : public:
     978               0 :   CloseRunnable(WorkerPrivate* aWorkerPrivate)
     979               0 :   : WorkerControlRunnable(aWorkerPrivate, ParentThread, UnchangedBusyCount)
     980               0 :   { }
     981                 : 
     982                 :   bool
     983               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
     984                 :   {
     985                 :     // This busy count will be matched by the CloseEventRunnable.
     986               0 :     return aWorkerPrivate->ModifyBusyCount(aCx, true) &&
     987               0 :            aWorkerPrivate->Close(aCx);
     988                 :   }
     989                 : };
     990                 : 
     991                 : class SuspendRunnable : public WorkerControlRunnable
     992               0 : {
     993                 : public:
     994               0 :   SuspendRunnable(WorkerPrivate* aWorkerPrivate)
     995               0 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount)
     996               0 :   { }
     997                 : 
     998                 :   bool
     999               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1000                 :   {
    1001               0 :     return aWorkerPrivate->SuspendInternal(aCx);
    1002                 :   }
    1003                 : };
    1004                 : 
    1005                 : class ResumeRunnable : public WorkerControlRunnable
    1006               0 : {
    1007                 : public:
    1008               0 :   ResumeRunnable(WorkerPrivate* aWorkerPrivate)
    1009               0 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount)
    1010               0 :   { }
    1011                 : 
    1012                 :   bool
    1013               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1014                 :   {
    1015               0 :     return aWorkerPrivate->ResumeInternal(aCx);
    1016                 :   }
    1017                 : };
    1018                 : 
    1019                 : class ReportErrorRunnable : public WorkerRunnable
    1020               0 : {
    1021                 :   nsString mMessage;
    1022                 :   nsString mFilename;
    1023                 :   nsString mLine;
    1024                 :   PRUint32 mLineNumber;
    1025                 :   PRUint32 mColumnNumber;
    1026                 :   PRUint32 mFlags;
    1027                 :   PRUint32 mErrorNumber;
    1028                 : 
    1029                 : public:
    1030               0 :   ReportErrorRunnable(WorkerPrivate* aWorkerPrivate, const nsString& aMessage,
    1031                 :                       const nsString& aFilename, const nsString& aLine,
    1032                 :                       PRUint32 aLineNumber, PRUint32 aColumnNumber,
    1033                 :                       PRUint32 aFlags, PRUint32 aErrorNumber)
    1034                 :   : WorkerRunnable(aWorkerPrivate, ParentThread, UnchangedBusyCount,
    1035                 :                    SkipWhenClearing),
    1036                 :     mMessage(aMessage), mFilename(aFilename), mLine(aLine),
    1037                 :     mLineNumber(aLineNumber), mColumnNumber(aColumnNumber), mFlags(aFlags),
    1038               0 :     mErrorNumber(aErrorNumber)
    1039               0 :   { }
    1040                 : 
    1041                 :   void
    1042               0 :   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    1043                 :                bool aDispatchResult)
    1044                 :   {
    1045               0 :     aWorkerPrivate->AssertIsOnWorkerThread();
    1046                 : 
    1047                 :     // Dispatch may fail if the worker was canceled, no need to report that as
    1048                 :     // an error, so don't call base class PostDispatch.
    1049               0 :   }
    1050                 : 
    1051                 :   bool
    1052               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1053                 :   {
    1054               0 :     JSObject* target = aWorkerPrivate->GetJSObject();
    1055               0 :     if (target) {
    1056               0 :       aWorkerPrivate->AssertInnerWindowIsCorrect();
    1057                 :     }
    1058                 : 
    1059                 :     PRUint64 innerWindowId;
    1060                 : 
    1061               0 :     WorkerPrivate* parent = aWorkerPrivate->GetParent();
    1062               0 :     if (parent) {
    1063               0 :       innerWindowId = 0;
    1064                 :     }
    1065                 :     else {
    1066               0 :       AssertIsOnMainThread();
    1067                 : 
    1068               0 :       if (aWorkerPrivate->IsSuspended()) {
    1069               0 :         aWorkerPrivate->QueueRunnable(this);
    1070               0 :         return true;
    1071                 :       }
    1072                 : 
    1073               0 :       innerWindowId = aWorkerPrivate->GetInnerWindowId();
    1074                 :     }
    1075                 : 
    1076                 :     return ReportErrorRunnable::ReportError(aCx, parent, true, target, mMessage,
    1077                 :                                             mFilename, mLine, mLineNumber,
    1078                 :                                             mColumnNumber, mFlags,
    1079               0 :                                             mErrorNumber, innerWindowId);
    1080                 :   }
    1081                 : 
    1082                 :   static bool
    1083               0 :   ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    1084                 :               bool aFireAtScope, JSObject* aTarget, const nsString& aMessage,
    1085                 :               const nsString& aFilename, const nsString& aLine,
    1086                 :               PRUint32 aLineNumber, PRUint32 aColumnNumber, PRUint32 aFlags,
    1087                 :               PRUint32 aErrorNumber, PRUint64 aInnerWindowId)
    1088                 :   {
    1089               0 :     if (aWorkerPrivate) {
    1090               0 :       aWorkerPrivate->AssertIsOnWorkerThread();
    1091                 :     }
    1092                 :     else {
    1093               0 :       AssertIsOnMainThread();
    1094                 :     }
    1095                 : 
    1096                 :     JSString* message = JS_NewUCStringCopyN(aCx, aMessage.get(),
    1097               0 :                                             aMessage.Length());
    1098               0 :     if (!message) {
    1099               0 :       return false;
    1100                 :     }
    1101                 : 
    1102                 :     JSString* filename = JS_NewUCStringCopyN(aCx, aFilename.get(),
    1103               0 :                                              aFilename.Length());
    1104               0 :     if (!filename) {
    1105               0 :       return false;
    1106                 :     }
    1107                 : 
    1108                 :     // First fire an ErrorEvent at the worker.
    1109               0 :     if (aTarget) {
    1110                 :       JSObject* event = events::CreateErrorEvent(aCx, message, filename,
    1111               0 :                                                  aLineNumber, !aWorkerPrivate);
    1112               0 :       if (!event) {
    1113               0 :         return false;
    1114                 :       }
    1115                 : 
    1116                 :       bool preventDefaultCalled;
    1117               0 :       if (!events::DispatchEventToTarget(aCx, aTarget, event,
    1118               0 :                                          &preventDefaultCalled)) {
    1119               0 :         return false;
    1120                 :       }
    1121                 : 
    1122               0 :       if (preventDefaultCalled) {
    1123               0 :         return true;
    1124                 :       }
    1125                 :     }
    1126                 : 
    1127                 :     // Now fire an event at the global object, but don't do that if the error
    1128                 :     // code is too much recursion and this is the same script threw the error.
    1129               0 :     if (aFireAtScope && (aTarget || aErrorNumber != JSMSG_OVER_RECURSED)) {
    1130               0 :       aTarget = JS_GetGlobalForScopeChain(aCx);
    1131               0 :       NS_ASSERTION(aTarget, "This should never be null!");
    1132                 : 
    1133                 :       bool preventDefaultCalled;
    1134                 :       nsIScriptGlobalObject* sgo;
    1135                 : 
    1136               0 :       if (aWorkerPrivate ||
    1137                 :           !(sgo = nsJSUtils::GetStaticScriptGlobal(aCx, aTarget))) {
    1138                 :         // Fire a normal ErrorEvent if we're running on a worker thread.
    1139                 :         JSObject* event = events::CreateErrorEvent(aCx, message, filename,
    1140               0 :                                                    aLineNumber, false);
    1141               0 :         if (!event) {
    1142               0 :           return false;
    1143                 :         }
    1144                 : 
    1145               0 :         if (!events::DispatchEventToTarget(aCx, aTarget, event,
    1146               0 :                                            &preventDefaultCalled)) {
    1147               0 :           return false;
    1148                 :         }
    1149                 :       }
    1150                 :       else {
    1151                 :         // Icky, we have to fire an nsScriptErrorEvent...
    1152               0 :         nsScriptErrorEvent event(true, NS_LOAD_ERROR);
    1153               0 :         event.lineNr = aLineNumber;
    1154               0 :         event.errorMsg = aMessage.get();
    1155               0 :         event.fileName = aFilename.get();
    1156                 : 
    1157                 :         nsEventStatus status;
    1158               0 :         if (NS_FAILED(sgo->HandleScriptError(&event, &status))) {
    1159               0 :           NS_WARNING("Failed to dispatch main thread error event!");
    1160               0 :           status = nsEventStatus_eIgnore;
    1161                 :         }
    1162                 : 
    1163               0 :         preventDefaultCalled = status == nsEventStatus_eConsumeNoDefault;
    1164                 :       }
    1165                 : 
    1166               0 :       if (preventDefaultCalled) {
    1167               0 :         return true;
    1168                 :       }
    1169                 :     }
    1170                 : 
    1171                 :     // Now fire a runnable to do the same on the parent's thread if we can.
    1172               0 :     if (aWorkerPrivate) {
    1173                 :       nsRefPtr<ReportErrorRunnable> runnable =
    1174                 :         new ReportErrorRunnable(aWorkerPrivate, aMessage, aFilename, aLine,
    1175                 :                                 aLineNumber, aColumnNumber, aFlags,
    1176               0 :                                 aErrorNumber);
    1177               0 :       return runnable->Dispatch(aCx);
    1178                 :     }
    1179                 : 
    1180                 :     // Otherwise log an error to the error console.
    1181                 :     nsCOMPtr<nsIScriptError> scriptError =
    1182               0 :       do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
    1183               0 :     NS_WARN_IF_FALSE(scriptError, "Failed to create script error!");
    1184                 : 
    1185               0 :     if (scriptError) {
    1186               0 :       if (NS_FAILED(scriptError->InitWithWindowID(aMessage.get(),
    1187                 :                                                   aFilename.get(),
    1188                 :                                                   aLine.get(), aLineNumber,
    1189                 :                                                   aColumnNumber, aFlags,
    1190                 :                                                   "Web Worker",
    1191                 :                                                   aInnerWindowId))) {
    1192               0 :         NS_WARNING("Failed to init script error!");
    1193               0 :         scriptError = nsnull;
    1194                 :       }
    1195                 :     }
    1196                 : 
    1197                 :     nsCOMPtr<nsIConsoleService> consoleService =
    1198               0 :       do_GetService(NS_CONSOLESERVICE_CONTRACTID);
    1199               0 :     NS_WARN_IF_FALSE(consoleService, "Failed to get console service!");
    1200                 : 
    1201               0 :     bool logged = false;
    1202                 : 
    1203               0 :     if (consoleService) {
    1204               0 :       if (scriptError) {
    1205               0 :         if (NS_SUCCEEDED(consoleService->LogMessage(scriptError))) {
    1206               0 :           logged = true;
    1207                 :         }
    1208                 :         else {
    1209               0 :           NS_WARNING("Failed to log script error!");
    1210                 :         }
    1211                 :       }
    1212               0 :       else if (NS_SUCCEEDED(consoleService->LogStringMessage(aMessage.get()))) {
    1213               0 :         logged = true;
    1214                 :       }
    1215                 :       else {
    1216               0 :         NS_WARNING("Failed to log script error!");
    1217                 :       }
    1218                 :     }
    1219                 : 
    1220               0 :     if (!logged) {
    1221               0 :       NS_ConvertUTF16toUTF8 msg(aMessage);
    1222                 : #ifdef ANDROID
    1223                 :       __android_log_print(ANDROID_LOG_INFO, "Gecko", msg.get());
    1224                 : #endif
    1225               0 :       fputs(msg.get(), stderr);
    1226               0 :       fflush(stderr);
    1227                 :     }
    1228                 : 
    1229               0 :     return true;
    1230                 :   }
    1231                 : };
    1232                 : 
    1233                 : class TimerRunnable : public WorkerRunnable
    1234               0 : {
    1235                 : public:
    1236               0 :   TimerRunnable(WorkerPrivate* aWorkerPrivate)
    1237                 :   : WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
    1238               0 :                    SkipWhenClearing)
    1239               0 :   { }
    1240                 : 
    1241                 :   bool
    1242               0 :   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1243                 :   {
    1244                 :     // Silence bad assertions.
    1245               0 :     return true;
    1246                 :   }
    1247                 : 
    1248                 :   void
    1249               0 :   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    1250                 :                bool aDispatchResult)
    1251                 :   {
    1252                 :     // Silence bad assertions.
    1253               0 :   }
    1254                 : 
    1255                 :   bool
    1256               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1257                 :   {
    1258               0 :     return aWorkerPrivate->RunExpiredTimeouts(aCx);
    1259                 :   }
    1260                 : };
    1261                 : 
    1262                 : void
    1263               0 : DummyCallback(nsITimer* aTimer, void* aClosure)
    1264                 : {
    1265                 :   // Nothing!
    1266               0 : }
    1267                 : 
    1268                 : class WorkerRunnableEventTarget : public nsIEventTarget
    1269               0 : {
    1270                 : protected:
    1271                 :   nsRefPtr<WorkerRunnable> mWorkerRunnable;
    1272                 : 
    1273                 : public:
    1274               0 :   WorkerRunnableEventTarget(WorkerRunnable* aWorkerRunnable)
    1275               0 :   : mWorkerRunnable(aWorkerRunnable)
    1276               0 :   { }
    1277                 : 
    1278                 :   NS_DECL_ISUPPORTS
    1279                 : 
    1280                 :   NS_IMETHOD
    1281               0 :   Dispatch(nsIRunnable* aRunnable, PRUint32 aFlags)
    1282                 :   {
    1283               0 :     NS_ASSERTION(aFlags == nsIEventTarget::DISPATCH_NORMAL, "Don't call me!");
    1284                 : 
    1285               0 :     nsRefPtr<WorkerRunnableEventTarget> kungFuDeathGrip = this;
    1286                 : 
    1287                 :     // This can fail if we're racing to terminate or cancel, should be handled
    1288                 :     // by the terminate or cancel code.
    1289               0 :     mWorkerRunnable->Dispatch(nsnull);
    1290                 : 
    1291                 :     // Run the runnable we're given now (should just call DummyCallback()),
    1292                 :     // otherwise the timer thread will leak it...
    1293               0 :     return aRunnable->Run();
    1294                 :   }
    1295                 : 
    1296                 :   NS_IMETHOD
    1297               0 :   IsOnCurrentThread(bool* aIsOnCurrentThread)
    1298                 :   {
    1299               0 :     *aIsOnCurrentThread = false;
    1300               0 :     return NS_OK;
    1301                 :   }
    1302                 : };
    1303                 : 
    1304               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerRunnableEventTarget, nsIEventTarget)
    1305                 : 
    1306                 : class KillCloseEventRunnable : public WorkerRunnable
    1307                 : {
    1308                 :   nsCOMPtr<nsITimer> mTimer;
    1309                 : 
    1310                 :   class KillScriptRunnable : public WorkerControlRunnable
    1311               0 :   {
    1312                 :   public:
    1313               0 :     KillScriptRunnable(WorkerPrivate* aWorkerPrivate)
    1314               0 :     : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount)
    1315               0 :     { }
    1316                 : 
    1317                 :     bool
    1318               0 :     PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1319                 :     {
    1320                 :       // Silence bad assertions.
    1321               0 :       return true;
    1322                 :     }
    1323                 : 
    1324                 :     void
    1325               0 :     PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    1326                 :                  bool aDispatchResult)
    1327                 :     {
    1328                 :       // Silence bad assertions.
    1329               0 :     }
    1330                 : 
    1331                 :     bool
    1332               0 :     WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1333                 :     {
    1334                 :       // Kill running script.
    1335               0 :       return false;
    1336                 :     }
    1337                 :   };
    1338                 : 
    1339                 : public:
    1340               0 :   KillCloseEventRunnable(WorkerPrivate* aWorkerPrivate)
    1341                 :   : WorkerRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount,
    1342               0 :                    SkipWhenClearing)
    1343               0 :   { }
    1344                 : 
    1345               0 :   ~KillCloseEventRunnable()
    1346               0 :   {
    1347               0 :     if (mTimer) {
    1348               0 :       mTimer->Cancel();
    1349                 :     }
    1350               0 :   }
    1351                 : 
    1352                 :   bool
    1353               0 :   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1354                 :   {
    1355               0 :     NS_NOTREACHED("Not meant to be dispatched!");
    1356               0 :     return false;
    1357                 :   }
    1358                 : 
    1359                 :   bool
    1360               0 :   SetTimeout(JSContext* aCx, PRUint32 aDelayMS)
    1361                 :   {
    1362               0 :     nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
    1363               0 :     if (!timer) {
    1364               0 :       JS_ReportError(aCx, "Failed to create timer!");
    1365               0 :       return false;
    1366                 :     }
    1367                 : 
    1368                 :     nsRefPtr<KillScriptRunnable> runnable =
    1369               0 :       new KillScriptRunnable(mWorkerPrivate);
    1370                 : 
    1371                 :     nsRefPtr<WorkerRunnableEventTarget> target =
    1372               0 :       new WorkerRunnableEventTarget(runnable);
    1373                 : 
    1374               0 :     if (NS_FAILED(timer->SetTarget(target))) {
    1375               0 :       JS_ReportError(aCx, "Failed to set timer's target!");
    1376               0 :       return false;
    1377                 :     }
    1378                 : 
    1379               0 :     if (NS_FAILED(timer->InitWithFuncCallback(DummyCallback, nsnull, aDelayMS,
    1380                 :                                               nsITimer::TYPE_ONE_SHOT))) {
    1381               0 :       JS_ReportError(aCx, "Failed to start timer!");
    1382               0 :       return false;
    1383                 :     }
    1384                 : 
    1385               0 :     mTimer.swap(timer);
    1386               0 :     return true;
    1387                 :   }
    1388                 : 
    1389                 :   bool
    1390               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1391                 :   {
    1392               0 :     if (mTimer) {
    1393               0 :       mTimer->Cancel();
    1394               0 :       mTimer = nsnull;
    1395                 :     }
    1396                 : 
    1397               0 :     return true;
    1398                 :   }
    1399                 : };
    1400                 : 
    1401                 : class UpdateJSContextOptionsRunnable : public WorkerControlRunnable
    1402               0 : {
    1403                 :   PRUint32 mOptions;
    1404                 : 
    1405                 : public:
    1406               0 :   UpdateJSContextOptionsRunnable(WorkerPrivate* aWorkerPrivate,
    1407                 :                                  PRUint32 aOptions)
    1408                 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
    1409               0 :     mOptions(aOptions)
    1410               0 :   { }
    1411                 : 
    1412                 :   bool
    1413               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1414                 :   {
    1415               0 :     aWorkerPrivate->UpdateJSContextOptionsInternal(aCx, mOptions);
    1416               0 :     return true;
    1417                 :   }
    1418                 : };
    1419                 : 
    1420                 : class UpdateJSRuntimeHeapSizeRunnable : public WorkerControlRunnable
    1421               0 : {
    1422                 :   PRUint32 mJSRuntimeHeapSize;
    1423                 : 
    1424                 : public:
    1425               0 :   UpdateJSRuntimeHeapSizeRunnable(WorkerPrivate* aWorkerPrivate,
    1426                 :                                   PRUint32 aJSRuntimeHeapSize)
    1427                 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
    1428               0 :     mJSRuntimeHeapSize(aJSRuntimeHeapSize)
    1429               0 :   { }
    1430                 : 
    1431                 :   bool
    1432               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1433                 :   {
    1434               0 :     aWorkerPrivate->UpdateJSRuntimeHeapSizeInternal(aCx, mJSRuntimeHeapSize);
    1435               0 :     return true;
    1436                 :   }
    1437                 : };
    1438                 : 
    1439                 : #ifdef JS_GC_ZEAL
    1440                 : class UpdateGCZealRunnable : public WorkerControlRunnable
    1441               0 : {
    1442                 :   PRUint8 mGCZeal;
    1443                 : 
    1444                 : public:
    1445               0 :   UpdateGCZealRunnable(WorkerPrivate* aWorkerPrivate,
    1446                 :                        PRUint8 aGCZeal)
    1447                 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
    1448               0 :     mGCZeal(aGCZeal)
    1449               0 :   { }
    1450                 : 
    1451                 :   bool
    1452               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1453                 :   {
    1454               0 :     aWorkerPrivate->UpdateGCZealInternal(aCx, mGCZeal);
    1455               0 :     return true;
    1456                 :   }
    1457                 : };
    1458                 : #endif
    1459                 : 
    1460                 : class GarbageCollectRunnable : public WorkerControlRunnable
    1461               0 : {
    1462                 : protected:
    1463                 :   bool mShrinking;
    1464                 :   bool mCollectChildren;
    1465                 : 
    1466                 : public:
    1467               0 :   GarbageCollectRunnable(WorkerPrivate* aWorkerPrivate, bool aShrinking,
    1468                 :                          bool aCollectChildren)
    1469                 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
    1470               0 :     mShrinking(aShrinking), mCollectChildren(aCollectChildren)
    1471               0 :   { }
    1472                 : 
    1473                 :   bool
    1474               0 :   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1475                 :   {
    1476                 :     // Silence bad assertions, this can be dispatched from either the main
    1477                 :     // thread or the timer thread..
    1478               0 :     return true;
    1479                 :   }
    1480                 : 
    1481                 :   void
    1482               0 :   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    1483                 :                 bool aDispatchResult)
    1484                 :   {
    1485                 :     // Silence bad assertions, this can be dispatched from either the main
    1486                 :     // thread or the timer thread..
    1487               0 :   }
    1488                 : 
    1489                 :   bool
    1490               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1491                 :   {
    1492               0 :     aWorkerPrivate->GarbageCollectInternal(aCx, mShrinking, mCollectChildren);
    1493               0 :     return true;
    1494                 :   }
    1495                 : };
    1496                 : 
    1497                 : class CollectRuntimeStatsRunnable : public WorkerControlRunnable
    1498               0 : {
    1499                 :   typedef mozilla::Mutex Mutex;
    1500                 :   typedef mozilla::CondVar CondVar;
    1501                 : 
    1502                 :   Mutex mMutex;
    1503                 :   CondVar mCondVar;
    1504                 :   volatile bool mDone;
    1505                 :   bool mIsQuick;
    1506                 :   void* mData;
    1507                 :   bool* mSucceeded;
    1508                 : 
    1509                 : public:
    1510               0 :   CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, bool aIsQuick,
    1511                 :                               void* aData, bool* aSucceeded)
    1512                 :   : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount),
    1513                 :     mMutex("CollectRuntimeStatsRunnable::mMutex"),
    1514                 :     mCondVar(mMutex, "CollectRuntimeStatsRunnable::mCondVar"), mDone(false),
    1515               0 :     mIsQuick(aIsQuick), mData(aData), mSucceeded(aSucceeded)
    1516               0 :   { }
    1517                 : 
    1518                 :   bool
    1519               0 :   PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1520                 :   {
    1521               0 :     AssertIsOnMainThread();
    1522               0 :     return true;
    1523                 :   }
    1524                 : 
    1525                 :   void
    1526               0 :   PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    1527                 :                bool aDispatchResult)
    1528                 :   {
    1529               0 :     AssertIsOnMainThread();
    1530               0 :   }
    1531                 : 
    1532                 :   bool
    1533               0 :   DispatchInternal()
    1534                 :   {
    1535               0 :     AssertIsOnMainThread();
    1536                 : 
    1537               0 :     if (!WorkerControlRunnable::DispatchInternal()) {
    1538               0 :       NS_WARNING("Failed to dispatch runnable!");
    1539               0 :       return false;
    1540                 :     }
    1541                 : 
    1542                 :     {
    1543               0 :       MutexAutoLock lock(mMutex);
    1544               0 :       while (!mDone) {
    1545               0 :         mCondVar.Wait();
    1546                 :       }
    1547                 :     }
    1548                 : 
    1549               0 :     return true;
    1550                 :   }
    1551                 : 
    1552                 :   bool
    1553               0 :   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1554                 :   {
    1555               0 :     JSRuntime *rt = JS_GetRuntime(aCx);
    1556               0 :     if (mIsQuick) {
    1557               0 :       *static_cast<int64_t*>(mData) = JS::GetExplicitNonHeapForRuntime(rt, JsWorkerMallocSizeOf);
    1558               0 :       *mSucceeded = true;
    1559                 :     } else {
    1560               0 :       *mSucceeded = JS::CollectRuntimeStats(rt, static_cast<JS::RuntimeStats*>(mData));
    1561                 :     }
    1562                 : 
    1563                 :     {
    1564               0 :       MutexAutoLock lock(mMutex);
    1565               0 :       mDone = true;
    1566               0 :       mCondVar.Notify();
    1567                 :     }
    1568                 : 
    1569               0 :     return true;
    1570                 :   }
    1571                 : };
    1572                 : 
    1573                 : } /* anonymous namespace */
    1574                 : 
    1575                 : #ifdef DEBUG
    1576                 : void
    1577          867573 : mozilla::dom::workers::AssertIsOnMainThread()
    1578                 : {
    1579          867573 :   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
    1580          867573 : }
    1581                 : 
    1582               0 : WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
    1583                 :                                BusyBehavior aBusyBehavior,
    1584                 :                                ClearingBehavior aClearingBehavior)
    1585                 : : mWorkerPrivate(aWorkerPrivate), mTarget(aTarget),
    1586               0 :   mBusyBehavior(aBusyBehavior), mClearingBehavior(aClearingBehavior)
    1587                 : {
    1588               0 :   NS_ASSERTION(aWorkerPrivate, "Null worker private!");
    1589               0 : }
    1590                 : #endif
    1591                 : 
    1592               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerRunnable, nsIRunnable)
    1593                 : 
    1594                 : bool
    1595               0 : WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
    1596                 : {
    1597                 : #ifdef DEBUG
    1598               0 :   if (mBusyBehavior == ModifyBusyCount) {
    1599               0 :     NS_ASSERTION(mTarget == WorkerThread,
    1600                 :                  "Don't set this option unless targeting the worker thread!");
    1601                 :   }
    1602               0 :   if (mTarget == ParentThread) {
    1603               0 :     aWorkerPrivate->AssertIsOnWorkerThread();
    1604                 :   }
    1605                 :   else {
    1606               0 :     aWorkerPrivate->AssertIsOnParentThread();
    1607                 :   }
    1608                 : #endif
    1609                 : 
    1610               0 :   if (mBusyBehavior == ModifyBusyCount && aCx) {
    1611               0 :     return aWorkerPrivate->ModifyBusyCount(aCx, true);
    1612                 :   }
    1613                 : 
    1614               0 :   return true;
    1615                 : }
    1616                 : 
    1617                 : bool
    1618               0 : WorkerRunnable::Dispatch(JSContext* aCx)
    1619                 : {
    1620                 :   bool ok;
    1621                 : 
    1622               0 :   if (!aCx) {
    1623               0 :     ok = PreDispatch(nsnull, mWorkerPrivate);
    1624               0 :     if (ok) {
    1625               0 :       ok = DispatchInternal();
    1626                 :     }
    1627               0 :     PostDispatch(nsnull, mWorkerPrivate, ok);
    1628               0 :     return ok;
    1629                 :   }
    1630                 : 
    1631               0 :   JSAutoRequest ar(aCx);
    1632                 : 
    1633               0 :   JSObject* global = JS_GetGlobalObject(aCx);
    1634                 : 
    1635               0 :   JSAutoEnterCompartment ac;
    1636               0 :   if (global && !ac.enter(aCx, global)) {
    1637               0 :     return false;
    1638                 :   }
    1639                 : 
    1640               0 :   ok = PreDispatch(aCx, mWorkerPrivate);
    1641                 : 
    1642               0 :   if (ok && !DispatchInternal()) {
    1643               0 :     ok = false;
    1644                 :   }
    1645                 : 
    1646               0 :   PostDispatch(aCx, mWorkerPrivate, ok);
    1647                 : 
    1648               0 :   return ok;
    1649                 : }
    1650                 : 
    1651                 : // static
    1652                 : bool
    1653               0 : WorkerRunnable::DispatchToMainThread(nsIRunnable* aRunnable)
    1654                 : {
    1655               0 :   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
    1656               0 :   NS_ASSERTION(mainThread, "This should never fail!");
    1657                 : 
    1658               0 :   return NS_SUCCEEDED(mainThread->Dispatch(aRunnable, NS_DISPATCH_NORMAL));
    1659                 : }
    1660                 : 
    1661                 : // These DispatchInternal functions look identical but carry important type
    1662                 : // informaton so they can't be consolidated...
    1663                 : 
    1664                 : #define IMPL_DISPATCH_INTERNAL(_class)                                         \
    1665                 :   bool                                                                         \
    1666                 :   _class ::DispatchInternal()                                                  \
    1667                 :   {                                                                            \
    1668                 :     if (mTarget == WorkerThread) {                                             \
    1669                 :       return mWorkerPrivate->Dispatch(this);                                   \
    1670                 :     }                                                                          \
    1671                 :                                                                                \
    1672                 :     if (mWorkerPrivate->GetParent()) {                                         \
    1673                 :       return mWorkerPrivate->GetParent()->Dispatch(this);                      \
    1674                 :     }                                                                          \
    1675                 :                                                                                \
    1676                 :     return DispatchToMainThread(this);                                         \
    1677                 :   }
    1678                 : 
    1679               0 : IMPL_DISPATCH_INTERNAL(WorkerRunnable)
    1680               0 : IMPL_DISPATCH_INTERNAL(WorkerSyncRunnable)
    1681               0 : IMPL_DISPATCH_INTERNAL(WorkerControlRunnable)
    1682                 : 
    1683                 : #undef IMPL_DISPATCH_INTERNAL
    1684                 : 
    1685                 : void
    1686               0 : WorkerRunnable::PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    1687                 :                              bool aDispatchResult)
    1688                 : {
    1689                 : #ifdef DEBUG
    1690               0 :   if (mTarget == ParentThread) {
    1691               0 :     aWorkerPrivate->AssertIsOnWorkerThread();
    1692                 :   }
    1693                 :   else {
    1694               0 :     aWorkerPrivate->AssertIsOnParentThread();
    1695                 :   }
    1696                 : #endif
    1697                 : 
    1698               0 :   if (!aDispatchResult && aCx) {
    1699               0 :     if (mBusyBehavior == ModifyBusyCount) {
    1700               0 :       aWorkerPrivate->ModifyBusyCount(aCx, false);
    1701                 :     }
    1702               0 :     JS_ReportPendingException(aCx);
    1703                 :   }
    1704               0 : }
    1705                 : 
    1706                 : NS_IMETHODIMP
    1707               0 : WorkerRunnable::Run()
    1708                 : {
    1709                 :   JSContext* cx;
    1710                 :   JSObject* targetCompartmentObject;
    1711               0 :   nsIThreadJSContextStack* contextStack = nsnull;
    1712                 : 
    1713               0 :   if (mTarget == WorkerThread) {
    1714               0 :     mWorkerPrivate->AssertIsOnWorkerThread();
    1715               0 :     cx = mWorkerPrivate->GetJSContext();
    1716               0 :     targetCompartmentObject = JS_GetGlobalObject(cx);
    1717                 :   } else {
    1718               0 :     mWorkerPrivate->AssertIsOnParentThread();
    1719               0 :     cx = mWorkerPrivate->ParentJSContext();
    1720               0 :     targetCompartmentObject = mWorkerPrivate->GetJSObject();
    1721                 : 
    1722               0 :     if (!mWorkerPrivate->GetParent()) {
    1723               0 :       AssertIsOnMainThread();
    1724                 : 
    1725               0 :       contextStack = nsContentUtils::ThreadJSContextStack();
    1726               0 :       NS_ASSERTION(contextStack, "This should never be null!");
    1727                 : 
    1728               0 :       if (NS_FAILED(contextStack->Push(cx))) {
    1729               0 :         NS_WARNING("Failed to push context!");
    1730               0 :         contextStack = nsnull;
    1731                 :       }
    1732                 :     }
    1733                 :   }
    1734                 : 
    1735               0 :   NS_ASSERTION(cx, "Must have a context!");
    1736                 : 
    1737               0 :   JSAutoRequest ar(cx);
    1738                 : 
    1739               0 :   JSAutoEnterCompartment ac;
    1740               0 :   if (targetCompartmentObject && !ac.enter(cx, targetCompartmentObject)) {
    1741               0 :     return false;
    1742                 :   }
    1743                 : 
    1744               0 :   bool result = WorkerRun(cx, mWorkerPrivate);
    1745                 : 
    1746               0 :   PostRun(cx, mWorkerPrivate, result);
    1747                 : 
    1748               0 :   if (contextStack) {
    1749                 :     JSContext* otherCx;
    1750               0 :     if (NS_FAILED(contextStack->Pop(&otherCx))) {
    1751               0 :       NS_WARNING("Failed to pop context!");
    1752                 :     }
    1753               0 :     else if (otherCx != cx) {
    1754               0 :       NS_WARNING("Popped a different context!");
    1755                 :     }
    1756                 :   }
    1757                 : 
    1758               0 :   return result ? NS_OK : NS_ERROR_FAILURE;
    1759                 : }
    1760                 : 
    1761                 : void
    1762               0 : WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
    1763                 :                         bool aRunResult)
    1764                 : {
    1765                 : #ifdef DEBUG
    1766               0 :   if (mTarget == ParentThread) {
    1767               0 :     mWorkerPrivate->AssertIsOnParentThread();
    1768                 :   }
    1769                 :   else {
    1770               0 :     mWorkerPrivate->AssertIsOnWorkerThread();
    1771                 :   }
    1772                 : #endif
    1773                 : 
    1774               0 :   if (mBusyBehavior == ModifyBusyCount) {
    1775               0 :     if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) {
    1776               0 :       aRunResult = false;
    1777                 :     }
    1778                 :   }
    1779                 : 
    1780               0 :   if (!aRunResult) {
    1781               0 :     JS_ReportPendingException(aCx);
    1782                 :   }
    1783               0 : }
    1784                 : 
    1785                 : struct WorkerPrivate::TimeoutInfo
    1786                 : {
    1787               0 :   TimeoutInfo()
    1788                 :   : mTimeoutVal(JSVAL_VOID), mLineNumber(0), mId(0), mIsInterval(false),
    1789               0 :     mCanceled(false)
    1790                 :   {
    1791               0 :     MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
    1792               0 :   }
    1793                 : 
    1794               0 :   ~TimeoutInfo()
    1795               0 :   {
    1796               0 :     MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate::TimeoutInfo);
    1797               0 :   }
    1798                 : 
    1799               0 :   bool operator==(const TimeoutInfo& aOther)
    1800                 :   {
    1801               0 :     return mTargetTime == aOther.mTargetTime;
    1802                 :   }
    1803                 : 
    1804               0 :   bool operator<(const TimeoutInfo& aOther)
    1805                 :   {
    1806               0 :     return mTargetTime < aOther.mTargetTime;
    1807                 :   }
    1808                 : 
    1809                 :   jsval mTimeoutVal;
    1810                 :   nsTArray<jsval> mExtraArgVals;
    1811                 :   mozilla::TimeStamp mTargetTime;
    1812                 :   mozilla::TimeDuration mInterval;
    1813                 :   nsCString mFilename;
    1814                 :   PRUint32 mLineNumber;
    1815                 :   PRUint32 mId;
    1816                 :   bool mIsInterval;
    1817                 :   bool mCanceled;
    1818                 : };
    1819                 : 
    1820                 : template <class Derived>
    1821                 : WorkerPrivateParent<Derived>::WorkerPrivateParent(
    1822                 :                                      JSContext* aCx, JSObject* aObject,
    1823                 :                                      WorkerPrivate* aParent,
    1824                 :                                      JSContext* aParentJSContext,
    1825                 :                                      const nsAString& aScriptURL,
    1826                 :                                      bool aIsChromeWorker,
    1827                 :                                      const nsACString& aDomain,
    1828                 :                                      nsCOMPtr<nsPIDOMWindow>& aWindow,
    1829                 :                                      nsCOMPtr<nsIScriptContext>& aScriptContext,
    1830                 :                                      nsCOMPtr<nsIURI>& aBaseURI,
    1831                 :                                      nsCOMPtr<nsIPrincipal>& aPrincipal,
    1832                 :                                      nsCOMPtr<nsIDocument>& aDocument)
    1833                 : : mMutex("WorkerPrivateParent Mutex"),
    1834                 :   mCondVar(mMutex, "WorkerPrivateParent CondVar"),
    1835                 :   mJSObject(aObject), mParent(aParent), mParentJSContext(aParentJSContext),
    1836                 :   mScriptURL(aScriptURL), mDomain(aDomain), mBusyCount(0),
    1837                 :   mParentStatus(Pending), mJSContextOptions(0), mJSRuntimeHeapSize(0),
    1838                 :   mGCZeal(0), mJSObjectRooted(false), mParentSuspended(false),
    1839               0 :   mIsChromeWorker(aIsChromeWorker), mPrincipalIsSystem(false)
    1840                 : {
    1841               0 :   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent);
    1842                 : 
    1843               0 :   if (aWindow) {
    1844               0 :     NS_ASSERTION(aWindow->IsInnerWindow(), "Should have inner window here!");
    1845                 :   }
    1846                 : 
    1847               0 :   mWindow.swap(aWindow);
    1848               0 :   mScriptContext.swap(aScriptContext);
    1849               0 :   mBaseURI.swap(aBaseURI);
    1850               0 :   mPrincipal.swap(aPrincipal);
    1851               0 :   mDocument.swap(aDocument);
    1852                 : 
    1853               0 :   if (aParent) {
    1854               0 :     aParent->AssertIsOnWorkerThread();
    1855                 : 
    1856               0 :     NS_ASSERTION(JS_GetOptions(aCx) == aParent->GetJSContextOptions(),
    1857                 :                  "Options mismatch!");
    1858               0 :     mJSContextOptions = aParent->GetJSContextOptions();
    1859                 : 
    1860               0 :     NS_ASSERTION(JS_GetGCParameter(JS_GetRuntime(aCx), JSGC_MAX_BYTES) ==
    1861                 :                  aParent->GetJSRuntimeHeapSize(),
    1862                 :                  "Runtime heap size mismatch!");
    1863               0 :     mJSRuntimeHeapSize = aParent->GetJSRuntimeHeapSize();
    1864                 : 
    1865                 : #ifdef JS_GC_ZEAL
    1866               0 :     mGCZeal = aParent->GetGCZeal();
    1867                 : #endif
    1868                 :   }
    1869                 :   else {
    1870               0 :     AssertIsOnMainThread();
    1871                 : 
    1872               0 :     mJSContextOptions = RuntimeService::GetDefaultJSContextOptions();
    1873               0 :     mJSRuntimeHeapSize = RuntimeService::GetDefaultJSRuntimeHeapSize();
    1874                 : #ifdef JS_GC_ZEAL
    1875               0 :     mGCZeal = RuntimeService::GetDefaultGCZeal();
    1876                 : #endif
    1877                 :   }
    1878               0 : }
    1879                 : 
    1880                 : template <class Derived>
    1881                 : WorkerPrivateParent<Derived>::~WorkerPrivateParent()
    1882                 : {
    1883               0 :   MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivateParent);
    1884               0 : }
    1885                 : 
    1886                 : template <class Derived>
    1887                 : bool
    1888                 : WorkerPrivateParent<Derived>::Start()
    1889                 : {
    1890                 :   // May be called on any thread!
    1891                 :   {
    1892               0 :     MutexAutoLock lock(mMutex);
    1893                 : 
    1894               0 :     NS_ASSERTION(mParentStatus != Running, "How can this be?!");
    1895                 : 
    1896               0 :     if (mParentStatus == Pending) {
    1897               0 :       mParentStatus = Running;
    1898               0 :       return true;
    1899                 :     }
    1900                 :   }
    1901                 : 
    1902               0 :   return false;
    1903                 : }
    1904                 : 
    1905                 : template <class Derived>
    1906                 : bool
    1907                 : WorkerPrivateParent<Derived>::NotifyPrivate(JSContext* aCx, Status aStatus,
    1908                 :                                             bool aFromJSObjectFinalizer)
    1909                 : {
    1910               0 :   AssertIsOnParentThread();
    1911                 : 
    1912                 :   bool pending;
    1913                 :   {
    1914               0 :     MutexAutoLock lock(mMutex);
    1915                 : 
    1916               0 :     if (mParentStatus >= aStatus) {
    1917               0 :       return true;
    1918                 :     }
    1919                 : 
    1920               0 :     pending = mParentStatus == Pending;
    1921               0 :     mParentStatus = aStatus;
    1922                 :   }
    1923                 : 
    1924               0 :   FinalizeInstance(aCx, false);
    1925                 : 
    1926               0 :   if (pending) {
    1927               0 :     WorkerPrivate* self = ParentAsWorkerPrivate();
    1928                 : #ifdef DEBUG
    1929                 :     {
    1930                 :       // Silence useless assertions in debug builds.
    1931               0 :       nsIThread* currentThread = NS_GetCurrentThread();
    1932               0 :       NS_ASSERTION(currentThread, "This should never be null!");
    1933                 : 
    1934               0 :       self->SetThread(currentThread);
    1935                 :     }
    1936                 : #endif
    1937                 :     // Worker never got a chance to run, go ahead and delete it.
    1938               0 :     self->ScheduleDeletion(true);
    1939               0 :     return true;
    1940                 :   }
    1941                 : 
    1942               0 :   NS_ASSERTION(aStatus != Terminating || mQueuedRunnables.IsEmpty(),
    1943                 :                "Shouldn't have anything queued!");
    1944                 : 
    1945                 :   // Anything queued will be discarded.
    1946               0 :   mQueuedRunnables.Clear();
    1947                 : 
    1948                 :   nsRefPtr<NotifyRunnable> runnable =
    1949                 :     new NotifyRunnable(ParentAsWorkerPrivate(), aFromJSObjectFinalizer,
    1950               0 :                        aStatus);
    1951               0 :   return runnable->Dispatch(aFromJSObjectFinalizer ? nsnull : aCx);
    1952                 : }
    1953                 : 
    1954                 : template <class Derived>
    1955                 : bool
    1956                 : WorkerPrivateParent<Derived>::Suspend(JSContext* aCx)
    1957                 : {
    1958               0 :   AssertIsOnParentThread();
    1959               0 :   NS_ASSERTION(!mParentSuspended, "Suspended more than once!");
    1960                 : 
    1961               0 :   mParentSuspended = true;
    1962                 : 
    1963                 :   {
    1964               0 :     MutexAutoLock lock(mMutex);
    1965                 : 
    1966               0 :     if (mParentStatus >= Terminating) {
    1967               0 :       return true;
    1968                 :     }
    1969                 :   }
    1970                 : 
    1971                 :   nsRefPtr<SuspendRunnable> runnable =
    1972               0 :     new SuspendRunnable(ParentAsWorkerPrivate());
    1973               0 :   return runnable->Dispatch(aCx);
    1974                 : }
    1975                 : 
    1976                 : template <class Derived>
    1977                 : bool
    1978                 : WorkerPrivateParent<Derived>::Resume(JSContext* aCx)
    1979                 : {
    1980               0 :   AssertIsOnParentThread();
    1981               0 :   NS_ASSERTION(mParentSuspended, "Not yet suspended!");
    1982                 : 
    1983               0 :   mParentSuspended = false;
    1984                 : 
    1985                 :   {
    1986               0 :     MutexAutoLock lock(mMutex);
    1987                 : 
    1988               0 :     if (mParentStatus >= Terminating) {
    1989               0 :       return true;
    1990                 :     }
    1991                 :   }
    1992                 : 
    1993                 :   // Dispatch queued runnables before waking up the worker, otherwise the worker
    1994                 :   // could post new messages before we run those that have been queued.
    1995               0 :   if (!mQueuedRunnables.IsEmpty()) {
    1996               0 :     AssertIsOnMainThread();
    1997                 : 
    1998               0 :     nsTArray<nsRefPtr<WorkerRunnable> > runnables;
    1999               0 :     mQueuedRunnables.SwapElements(runnables);
    2000                 : 
    2001               0 :     for (PRUint32 index = 0; index < runnables.Length(); index++) {
    2002               0 :       nsRefPtr<WorkerRunnable>& runnable = runnables[index];
    2003               0 :       if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
    2004               0 :         NS_WARNING("Failed to dispatch queued runnable!");
    2005                 :       }
    2006                 :     }
    2007                 :   }
    2008                 : 
    2009                 :   nsRefPtr<ResumeRunnable> runnable =
    2010               0 :     new ResumeRunnable(ParentAsWorkerPrivate());
    2011               0 :   if (!runnable->Dispatch(aCx)) {
    2012               0 :     return false;
    2013                 :   }
    2014                 : 
    2015               0 :   return true;
    2016                 : }
    2017                 : 
    2018                 : template <class Derived>
    2019                 : void
    2020                 : WorkerPrivateParent<Derived>::FinalizeInstance(JSContext* aCx,
    2021                 :                                                bool aFromJSFinalizer)
    2022                 : {
    2023               0 :   AssertIsOnParentThread();
    2024                 : 
    2025               0 :   if (mJSObject) {
    2026                 :     // Make sure we're in the right compartment, but only enter one if this is
    2027                 :     // not running from a finalizer.
    2028               0 :     JSAutoEnterCompartment ac;
    2029               0 :     if (!aFromJSFinalizer && !ac.enter(aCx, mJSObject)) {
    2030               0 :       NS_ERROR("How can this fail?!");
    2031                 :       return;
    2032                 :     }
    2033                 : 
    2034                 :     // Decouple the object from the private now.
    2035               0 :     worker::ClearPrivateSlot(aCx, mJSObject, !aFromJSFinalizer);
    2036                 : 
    2037                 :     // Clear the JS object.
    2038               0 :     mJSObject = nsnull;
    2039                 : 
    2040                 :     // Unroot.
    2041               0 :     RootJSObject(aCx, false);
    2042                 : 
    2043               0 :     if (!TerminatePrivate(aCx, aFromJSFinalizer)) {
    2044               0 :       NS_WARNING("Failed to terminate!");
    2045                 :     }
    2046                 : 
    2047               0 :     events::EventTarget::FinalizeInstance(aCx);
    2048                 :   }
    2049                 : }
    2050                 : 
    2051                 : template <class Derived>
    2052                 : bool
    2053                 : WorkerPrivateParent<Derived>::Close(JSContext* aCx)
    2054                 : {
    2055               0 :   AssertIsOnParentThread();
    2056                 : 
    2057                 :   {
    2058               0 :     MutexAutoLock lock(mMutex);
    2059                 : 
    2060               0 :     if (mParentStatus < Closing) {
    2061               0 :       mParentStatus = Closing;
    2062                 :     }
    2063                 :   }
    2064                 : 
    2065               0 :   return true;
    2066                 : }
    2067                 : 
    2068                 : template <class Derived>
    2069                 : bool
    2070                 : WorkerPrivateParent<Derived>::ModifyBusyCount(JSContext* aCx, bool aIncrease)
    2071                 : {
    2072               0 :   AssertIsOnParentThread();
    2073                 : 
    2074               0 :   NS_ASSERTION(aIncrease || mBusyCount, "Mismatched busy count mods!");
    2075                 : 
    2076               0 :   if (aIncrease) {
    2077               0 :     if (mBusyCount++ == 0) {
    2078               0 :       if (!RootJSObject(aCx, true)) {
    2079               0 :         return false;
    2080                 :       }
    2081                 :     }
    2082               0 :     return true;
    2083                 :   }
    2084                 : 
    2085               0 :   if (--mBusyCount == 0) {
    2086               0 :     if (!RootJSObject(aCx, false)) {
    2087               0 :       return false;
    2088                 :     }
    2089                 : 
    2090                 :     bool shouldCancel;
    2091                 :     {
    2092               0 :       MutexAutoLock lock(mMutex);
    2093               0 :       shouldCancel = mParentStatus == Terminating;
    2094                 :     }
    2095                 : 
    2096               0 :     if (shouldCancel && !Cancel(aCx)) {
    2097               0 :       return false;
    2098                 :     }
    2099                 :   }
    2100                 : 
    2101               0 :   return true;
    2102                 : }
    2103                 : 
    2104                 : template <class Derived>
    2105                 : bool
    2106                 : WorkerPrivateParent<Derived>::RootJSObject(JSContext* aCx, bool aRoot)
    2107                 : {
    2108               0 :   AssertIsOnParentThread();
    2109                 : 
    2110               0 :   if (aRoot) {
    2111               0 :     if (mJSObjectRooted || !mJSObject) {
    2112               0 :       return true;
    2113                 :     }
    2114                 : 
    2115               0 :     if (!JS_AddNamedObjectRoot(aCx, &mJSObject, "Worker root")) {
    2116               0 :       NS_WARNING("JS_AddNamedObjectRoot failed!");
    2117               0 :       return false;
    2118                 :     }
    2119                 :   }
    2120                 :   else {
    2121               0 :     if (!mJSObjectRooted) {
    2122               0 :       return true;
    2123                 :     }
    2124                 : 
    2125               0 :     if (!JS_RemoveObjectRoot(aCx, &mJSObject)) {
    2126               0 :       NS_WARNING("JS_RemoveObjectRoot failed!");
    2127               0 :       return false;
    2128                 :     }
    2129                 :   }
    2130                 : 
    2131               0 :   mJSObjectRooted = aRoot;
    2132               0 :   return true;
    2133                 : }
    2134                 : 
    2135                 : template <class Derived>
    2136                 : void
    2137                 : WorkerPrivateParent<Derived>::ForgetMainThreadObjects(
    2138                 :                                       nsTArray<nsCOMPtr<nsISupports> >& aDoomed)
    2139                 : {
    2140               0 :   AssertIsOnParentThread();
    2141                 : 
    2142               0 :   aDoomed.SetCapacity(6);
    2143                 : 
    2144               0 :   SwapToISupportsArray(mWindow, aDoomed);
    2145               0 :   SwapToISupportsArray(mScriptContext, aDoomed);
    2146               0 :   SwapToISupportsArray(mBaseURI, aDoomed);
    2147               0 :   SwapToISupportsArray(mScriptURI, aDoomed);
    2148               0 :   SwapToISupportsArray(mPrincipal, aDoomed);
    2149               0 :   SwapToISupportsArray(mDocument, aDoomed);
    2150               0 : }
    2151                 : 
    2152                 : template <class Derived>
    2153                 : bool
    2154                 : WorkerPrivateParent<Derived>::PostMessage(JSContext* aCx, jsval aMessage)
    2155                 : {
    2156               0 :   AssertIsOnParentThread();
    2157                 : 
    2158                 :   JSStructuredCloneCallbacks* callbacks;
    2159               0 :   if (GetParent()) {
    2160               0 :     if (IsChromeWorker()) {
    2161               0 :       callbacks = &gChromeWorkerStructuredCloneCallbacks;
    2162                 :     }
    2163                 :     else {
    2164               0 :       callbacks = &gWorkerStructuredCloneCallbacks;
    2165                 :     }
    2166                 :   }
    2167                 :   else {
    2168               0 :     AssertIsOnMainThread();
    2169                 : 
    2170               0 :     if (IsChromeWorker()) {
    2171               0 :       callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks;
    2172                 :     }
    2173                 :     else {
    2174               0 :       callbacks = &gMainThreadWorkerStructuredCloneCallbacks;
    2175                 :     }
    2176                 :   }
    2177                 : 
    2178               0 :   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
    2179                 : 
    2180               0 :   JSAutoStructuredCloneBuffer buffer;
    2181               0 :   if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) {
    2182               0 :     return false;
    2183                 :   }
    2184                 : 
    2185                 :   nsRefPtr<MessageEventRunnable> runnable =
    2186                 :     new MessageEventRunnable(ParentAsWorkerPrivate(),
    2187                 :                              WorkerRunnable::WorkerThread, buffer,
    2188               0 :                              clonedObjects);
    2189               0 :   return runnable->Dispatch(aCx);
    2190                 : }
    2191                 : 
    2192                 : template <class Derived>
    2193                 : PRUint64
    2194                 : WorkerPrivateParent<Derived>::GetInnerWindowId()
    2195                 : {
    2196               0 :   AssertIsOnMainThread();
    2197               0 :   return mDocument ? mDocument->InnerWindowID() : 0;
    2198                 : }
    2199                 : 
    2200                 : template <class Derived>
    2201                 : void
    2202                 : WorkerPrivateParent<Derived>::UpdateJSContextOptions(JSContext* aCx,
    2203                 :                                                      PRUint32 aOptions)
    2204                 : {
    2205               0 :   AssertIsOnParentThread();
    2206                 : 
    2207               0 :   mJSContextOptions = aOptions;
    2208                 : 
    2209                 :   nsRefPtr<UpdateJSContextOptionsRunnable> runnable =
    2210               0 :     new UpdateJSContextOptionsRunnable(ParentAsWorkerPrivate(), aOptions);
    2211               0 :   if (!runnable->Dispatch(aCx)) {
    2212               0 :     NS_WARNING("Failed to update worker context options!");
    2213               0 :     JS_ClearPendingException(aCx);
    2214                 :   }
    2215               0 : }
    2216                 : 
    2217                 : template <class Derived>
    2218                 : void
    2219                 : WorkerPrivateParent<Derived>::UpdateJSRuntimeHeapSize(JSContext* aCx,
    2220                 :                                                       PRUint32 aMaxBytes)
    2221                 : {
    2222               0 :   AssertIsOnParentThread();
    2223                 : 
    2224               0 :   mJSRuntimeHeapSize = aMaxBytes;
    2225                 : 
    2226                 :   nsRefPtr<UpdateJSRuntimeHeapSizeRunnable> runnable =
    2227               0 :     new UpdateJSRuntimeHeapSizeRunnable(ParentAsWorkerPrivate(), aMaxBytes);
    2228               0 :   if (!runnable->Dispatch(aCx)) {
    2229               0 :     NS_WARNING("Failed to update worker heap size!");
    2230               0 :     JS_ClearPendingException(aCx);
    2231                 :   }
    2232               0 : }
    2233                 : 
    2234                 : #ifdef JS_GC_ZEAL
    2235                 : template <class Derived>
    2236                 : void
    2237                 : WorkerPrivateParent<Derived>::UpdateGCZeal(JSContext* aCx, PRUint8 aGCZeal)
    2238                 : {
    2239               0 :   AssertIsOnParentThread();
    2240                 : 
    2241               0 :   mGCZeal = aGCZeal;
    2242                 : 
    2243                 :   nsRefPtr<UpdateGCZealRunnable> runnable =
    2244               0 :     new UpdateGCZealRunnable(ParentAsWorkerPrivate(), aGCZeal);
    2245               0 :   if (!runnable->Dispatch(aCx)) {
    2246               0 :     NS_WARNING("Failed to update worker gczeal!");
    2247               0 :     JS_ClearPendingException(aCx);
    2248                 :   }
    2249               0 : }
    2250                 : #endif
    2251                 : 
    2252                 : template <class Derived>
    2253                 : void
    2254                 : WorkerPrivateParent<Derived>::GarbageCollect(JSContext* aCx, bool aShrinking)
    2255                 : {
    2256                 :   nsRefPtr<GarbageCollectRunnable> runnable =
    2257               0 :     new GarbageCollectRunnable(ParentAsWorkerPrivate(), aShrinking, true);
    2258               0 :   if (!runnable->Dispatch(aCx)) {
    2259               0 :     NS_WARNING("Failed to update worker heap size!");
    2260               0 :     JS_ClearPendingException(aCx);
    2261                 :   }
    2262               0 : }
    2263                 : 
    2264                 : template <class Derived>
    2265                 : void
    2266                 : WorkerPrivateParent<Derived>::SetBaseURI(nsIURI* aBaseURI)
    2267                 : {
    2268               0 :   AssertIsOnMainThread();
    2269                 : 
    2270               0 :   mBaseURI = aBaseURI;
    2271                 : 
    2272               0 :   if (NS_FAILED(aBaseURI->GetSpec(mLocationInfo.mHref))) {
    2273               0 :     mLocationInfo.mHref.Truncate();
    2274                 :   }
    2275                 : 
    2276               0 :   if (NS_FAILED(aBaseURI->GetHost(mLocationInfo.mHostname))) {
    2277               0 :     mLocationInfo.mHostname.Truncate();
    2278                 :   }
    2279                 : 
    2280               0 :   if (NS_FAILED(aBaseURI->GetPath(mLocationInfo.mPathname))) {
    2281               0 :     mLocationInfo.mPathname.Truncate();
    2282                 :   }
    2283                 : 
    2284               0 :   nsCString temp;
    2285                 : 
    2286               0 :   nsCOMPtr<nsIURL> url(do_QueryInterface(aBaseURI));
    2287               0 :   if (url && NS_SUCCEEDED(url->GetQuery(temp)) && !temp.IsEmpty()) {
    2288               0 :     mLocationInfo.mSearch.AssignLiteral("?");
    2289               0 :     mLocationInfo.mSearch.Append(temp);
    2290                 :   }
    2291                 : 
    2292               0 :   if (NS_SUCCEEDED(aBaseURI->GetRef(temp)) && !temp.IsEmpty()) {
    2293                 :     nsCOMPtr<nsITextToSubURI> converter =
    2294               0 :       do_GetService(NS_ITEXTTOSUBURI_CONTRACTID);
    2295               0 :     if (converter) {
    2296               0 :       nsCString charset;
    2297               0 :       nsAutoString unicodeRef;
    2298               0 :       if (NS_SUCCEEDED(aBaseURI->GetOriginCharset(charset)) &&
    2299                 :           NS_SUCCEEDED(converter->UnEscapeURIForUI(charset, temp,
    2300                 :                                                    unicodeRef))) {
    2301               0 :         mLocationInfo.mHash.AssignLiteral("#");
    2302               0 :         mLocationInfo.mHash.Append(NS_ConvertUTF16toUTF8(unicodeRef));
    2303                 :       }
    2304                 :     }
    2305                 : 
    2306               0 :     if (mLocationInfo.mHash.IsEmpty()) {
    2307               0 :       mLocationInfo.mHash.AssignLiteral("#");
    2308               0 :       mLocationInfo.mHash.Append(temp);
    2309                 :     }
    2310                 :   }
    2311                 : 
    2312               0 :   if (NS_SUCCEEDED(aBaseURI->GetScheme(mLocationInfo.mProtocol))) {
    2313               0 :     mLocationInfo.mProtocol.AppendLiteral(":");
    2314                 :   }
    2315                 :   else {
    2316               0 :     mLocationInfo.mProtocol.Truncate();
    2317                 :   }
    2318                 : 
    2319                 :   PRInt32 port;
    2320               0 :   if (NS_SUCCEEDED(aBaseURI->GetPort(&port)) && port != -1) {
    2321               0 :     mLocationInfo.mPort.AppendInt(port);
    2322                 : 
    2323               0 :     nsCAutoString host(mLocationInfo.mHostname);
    2324               0 :     host.AppendLiteral(":");
    2325               0 :     host.Append(mLocationInfo.mPort);
    2326                 : 
    2327               0 :     mLocationInfo.mHost.Assign(host);
    2328                 :   }
    2329                 :   else {
    2330               0 :     mLocationInfo.mHost.Assign(mLocationInfo.mHostname);
    2331                 :   }
    2332               0 : }
    2333                 : 
    2334                 : template <class Derived>
    2335                 : void
    2336                 : WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal)
    2337                 : {
    2338               0 :   AssertIsOnMainThread();
    2339                 : 
    2340               0 :   mPrincipal = aPrincipal;
    2341               0 :   mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal);
    2342               0 : }
    2343                 : 
    2344                 : template <class Derived>
    2345                 : JSContext*
    2346                 : WorkerPrivateParent<Derived>::ParentJSContext() const
    2347                 : {
    2348               0 :   AssertIsOnParentThread();
    2349                 : 
    2350               0 :   if (!mParent) {
    2351               0 :     AssertIsOnMainThread();
    2352                 : 
    2353               0 :     if (!mScriptContext) {
    2354               0 :       NS_ASSERTION(!mParentJSContext, "Shouldn't have a parent context!");
    2355               0 :       return RuntimeService::AutoSafeJSContext::GetSafeContext();
    2356                 :     }
    2357                 : 
    2358               0 :     NS_ASSERTION(mParentJSContext == mScriptContext->GetNativeContext(),
    2359                 :                  "Native context has changed!");
    2360                 :   }
    2361                 : 
    2362               0 :   return mParentJSContext;
    2363                 : }
    2364                 : 
    2365               0 : WorkerPrivate::WorkerPrivate(JSContext* aCx, JSObject* aObject,
    2366                 :                              WorkerPrivate* aParent,
    2367                 :                              JSContext* aParentJSContext,
    2368                 :                              const nsAString& aScriptURL, bool aIsChromeWorker,
    2369                 :                              const nsACString& aDomain,
    2370                 :                              nsCOMPtr<nsPIDOMWindow>& aWindow,
    2371                 :                              nsCOMPtr<nsIScriptContext>& aParentScriptContext,
    2372                 :                              nsCOMPtr<nsIURI>& aBaseURI,
    2373                 :                              nsCOMPtr<nsIPrincipal>& aPrincipal,
    2374                 :                              nsCOMPtr<nsIDocument>& aDocument)
    2375                 : : WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aParentJSContext,
    2376                 :                                      aScriptURL, aIsChromeWorker, aDomain,
    2377                 :                                      aWindow, aParentScriptContext, aBaseURI,
    2378                 :                                      aPrincipal, aDocument),
    2379                 :   mJSContext(nsnull), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
    2380                 :   mStatus(Pending), mSuspended(false), mTimerRunning(false),
    2381                 :   mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
    2382                 :   mCloseHandlerFinished(false), mMemoryReporterRunning(false),
    2383               0 :   mMemoryReporterDisabled(false)
    2384                 : {
    2385               0 :   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
    2386               0 : }
    2387                 : 
    2388               0 : WorkerPrivate::~WorkerPrivate()
    2389                 : {
    2390               0 :   MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate);
    2391               0 : }
    2392                 : 
    2393                 : // static
    2394                 : WorkerPrivate*
    2395               0 : WorkerPrivate::Create(JSContext* aCx, JSObject* aObj, WorkerPrivate* aParent,
    2396                 :                       JSString* aScriptURL, bool aIsChromeWorker)
    2397                 : {
    2398               0 :   nsCString domain;
    2399               0 :   nsCOMPtr<nsIURI> baseURI;
    2400               0 :   nsCOMPtr<nsIPrincipal> principal;
    2401               0 :   nsCOMPtr<nsIScriptContext> scriptContext;
    2402               0 :   nsCOMPtr<nsIDocument> document;
    2403               0 :   nsCOMPtr<nsPIDOMWindow> window;
    2404                 : 
    2405                 :   JSContext* parentContext;
    2406                 : 
    2407               0 :   if (aParent) {
    2408               0 :     aParent->AssertIsOnWorkerThread();
    2409                 : 
    2410               0 :     parentContext = aCx;
    2411                 : 
    2412                 :     // Domain is the only thing we can touch here. The rest will be handled by
    2413                 :     // the ScriptLoader.
    2414               0 :     domain = aParent->Domain();
    2415                 :   }
    2416                 :   else {
    2417               0 :     AssertIsOnMainThread();
    2418                 : 
    2419               0 :     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
    2420               0 :     NS_ASSERTION(ssm, "This should never be null!");
    2421                 : 
    2422                 :     bool isChrome;
    2423               0 :     if (NS_FAILED(ssm->IsCapabilityEnabled("UniversalXPConnect", &isChrome))) {
    2424               0 :       NS_WARNING("IsCapabilityEnabled failed!");
    2425               0 :       isChrome = false;
    2426                 :     }
    2427                 : 
    2428                 :     // First check to make sure the caller has permission to make a
    2429                 :     // ChromeWorker if they called the ChromeWorker constructor.
    2430               0 :     if (aIsChromeWorker && !isChrome) {
    2431               0 :       nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_SECURITY_ERR);
    2432               0 :       return nsnull;
    2433                 :     }
    2434                 : 
    2435                 :     // Chrome callers (whether ChromeWorker of Worker) always get the system
    2436                 :     // principal here as they're allowed to load anything. The script loader may
    2437                 :     // change the principal later depending on the script uri.
    2438               0 :     if (isChrome &&
    2439               0 :         NS_FAILED(ssm->GetSystemPrincipal(getter_AddRefs(principal)))) {
    2440               0 :       JS_ReportError(aCx, "Could not get system principal!");
    2441               0 :       return nsnull;
    2442                 :     }
    2443                 : 
    2444                 :     // See if we're being called from a window or from somewhere else.
    2445                 :     nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
    2446               0 :       nsJSUtils::GetStaticScriptGlobal(aCx, JS_GetGlobalForScopeChain(aCx));
    2447               0 :     if (scriptGlobal) {
    2448                 :       // Window!
    2449               0 :       nsCOMPtr<nsPIDOMWindow> globalWindow = do_QueryInterface(scriptGlobal);
    2450                 : 
    2451                 :       // Only use the current inner window, and only use it if the caller can
    2452                 :       // access it.
    2453                 :       nsPIDOMWindow* outerWindow = globalWindow ?
    2454               0 :                                    globalWindow->GetOuterWindow() :
    2455               0 :                                    nsnull;
    2456               0 :       window = outerWindow ? outerWindow->GetCurrentInnerWindow() : nsnull;
    2457               0 :       if (!window ||
    2458               0 :           (globalWindow != window &&
    2459               0 :            !nsContentUtils::CanCallerAccess(window))) {
    2460               0 :         nsDOMClassInfo::ThrowJSException(aCx, NS_ERROR_DOM_SECURITY_ERR);
    2461               0 :         return nsnull;
    2462                 :       }
    2463                 : 
    2464               0 :       scriptContext = scriptGlobal->GetContext();
    2465               0 :       if (!scriptContext) {
    2466               0 :         JS_ReportError(aCx, "Couldn't get script context for this worker!");
    2467               0 :         return nsnull;
    2468                 :       }
    2469                 : 
    2470               0 :       parentContext = scriptContext->GetNativeContext();
    2471                 : 
    2472                 :       // If we're called from a window then we can dig out the principal and URI
    2473                 :       // from the document.
    2474               0 :       document = do_QueryInterface(window->GetExtantDocument());
    2475               0 :       if (!document) {
    2476               0 :         JS_ReportError(aCx, "No document in this window!");
    2477               0 :         return nsnull;
    2478                 :       }
    2479                 : 
    2480               0 :       baseURI = document->GetDocBaseURI();
    2481                 : 
    2482                 :       // Use the document's NodePrincipal as our principal if we're not being
    2483                 :       // called from chrome.
    2484               0 :       if (!principal) {
    2485               0 :         if (!(principal = document->NodePrincipal())) {
    2486               0 :           JS_ReportError(aCx, "Could not get document principal!");
    2487               0 :           return nsnull;
    2488                 :         }
    2489                 : 
    2490               0 :         nsCOMPtr<nsIURI> codebase;
    2491               0 :         if (NS_FAILED(principal->GetURI(getter_AddRefs(codebase)))) {
    2492               0 :           JS_ReportError(aCx, "Could not determine codebase!");
    2493               0 :           return nsnull;
    2494                 :         }
    2495                 : 
    2496               0 :         NS_NAMED_LITERAL_CSTRING(file, "file");
    2497                 : 
    2498                 :         bool isFile;
    2499               0 :         if (NS_FAILED(codebase->SchemeIs(file.get(), &isFile))) {
    2500               0 :           JS_ReportError(aCx, "Could not determine if codebase is file!");
    2501               0 :           return nsnull;
    2502                 :         }
    2503                 : 
    2504               0 :         if (isFile) {
    2505                 :           // XXX Fix this, need a real domain here.
    2506               0 :           domain = file;
    2507                 :         }
    2508                 :         else {
    2509                 :           nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
    2510               0 :             do_GetService(THIRDPARTYUTIL_CONTRACTID);
    2511               0 :           if (!thirdPartyUtil) {
    2512               0 :             JS_ReportError(aCx, "Could not get third party helper service!");
    2513               0 :             return nsnull;
    2514                 :           }
    2515                 : 
    2516               0 :           if (NS_FAILED(thirdPartyUtil->GetBaseDomain(codebase, domain))) {
    2517               0 :             JS_ReportError(aCx, "Could not get domain!");
    2518               0 :             return nsnull;
    2519                 :           }
    2520                 :         }
    2521                 :       }
    2522                 :     }
    2523                 :     else {
    2524                 :       // Not a window
    2525               0 :       NS_ASSERTION(isChrome, "Should be chrome only!");
    2526                 : 
    2527               0 :       parentContext = nsnull;
    2528                 : 
    2529                 :       // We're being created outside of a window. Need to figure out the script
    2530                 :       // that is creating us in order for us to use relative URIs later on.
    2531                 :       JSScript *script;
    2532               0 :       if (JS_DescribeScriptedCaller(aCx, &script, nsnull)) {
    2533               0 :         if (NS_FAILED(NS_NewURI(getter_AddRefs(baseURI),
    2534                 :                                 JS_GetScriptFilename(aCx, script)))) {
    2535               0 :           JS_ReportError(aCx, "Failed to construct base URI!");
    2536               0 :           return nsnull;
    2537                 :         }
    2538                 :       }
    2539                 :     }
    2540                 : 
    2541               0 :     NS_ASSERTION(principal, "Must have a principal now!");
    2542                 : 
    2543               0 :     if (!isChrome && domain.IsEmpty()) {
    2544               0 :       NS_ERROR("Must be chrome or have an domain!");
    2545               0 :       return nsnull;
    2546                 :     }
    2547                 :   }
    2548                 : 
    2549                 :   size_t urlLength;
    2550                 :   const jschar* urlChars = JS_GetStringCharsZAndLength(aCx, aScriptURL,
    2551               0 :                                                        &urlLength);
    2552               0 :   if (!urlChars) {
    2553               0 :     return nsnull;
    2554                 :   }
    2555                 : 
    2556               0 :   nsDependentString scriptURL(urlChars, urlLength);
    2557                 : 
    2558                 :   nsAutoPtr<WorkerPrivate> worker(
    2559                 :     new WorkerPrivate(aCx, aObj, aParent, parentContext, scriptURL,
    2560                 :                       aIsChromeWorker, domain, window, scriptContext, baseURI,
    2561               0 :                       principal, document));
    2562                 : 
    2563               0 :   nsRefPtr<CompileScriptRunnable> compiler = new CompileScriptRunnable(worker);
    2564               0 :   if (!compiler->Dispatch(aCx)) {
    2565               0 :     return nsnull;
    2566                 :   }
    2567                 : 
    2568               0 :   return worker.forget();
    2569                 : }
    2570                 : 
    2571                 : void
    2572               0 : WorkerPrivate::DoRunLoop(JSContext* aCx)
    2573                 : {
    2574               0 :   AssertIsOnWorkerThread();
    2575                 : 
    2576                 :   {
    2577               0 :     MutexAutoLock lock(mMutex);
    2578               0 :     mJSContext = aCx;
    2579                 : 
    2580               0 :     NS_ASSERTION(mStatus == Pending, "Huh?!");
    2581               0 :     mStatus = Running;
    2582                 :   }
    2583                 : 
    2584                 :   // We need a timer for GC. The basic plan is to run a normal (non-shrinking)
    2585                 :   // GC periodically (NORMAL_GC_TIMER_DELAY_MS) while the worker is running.
    2586                 :   // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_MS) timer to
    2587                 :   // run a shrinking GC. If the worker receives more messages then the short
    2588                 :   // timer is canceled and the periodic timer resumes.
    2589               0 :   nsCOMPtr<nsITimer> gcTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
    2590               0 :   if (!gcTimer) {
    2591               0 :     JS_ReportError(aCx, "Failed to create GC timer!");
    2592                 :     return;
    2593                 :   }
    2594                 : 
    2595               0 :   bool normalGCTimerRunning = false;
    2596                 : 
    2597                 :   // We need to swap event targets below to get different types of GC behavior.
    2598               0 :   nsCOMPtr<nsIEventTarget> normalGCEventTarget;
    2599               0 :   nsCOMPtr<nsIEventTarget> idleGCEventTarget;
    2600                 : 
    2601                 :   // We also need to track the idle GC event so that we don't confuse it with a
    2602                 :   // generic event that should re-trigger the idle GC timer.
    2603               0 :   nsCOMPtr<nsIRunnable> idleGCEvent;
    2604                 :   {
    2605                 :     nsRefPtr<GarbageCollectRunnable> runnable =
    2606               0 :       new GarbageCollectRunnable(this, false, false);
    2607               0 :     normalGCEventTarget = new WorkerRunnableEventTarget(runnable);
    2608                 : 
    2609               0 :     runnable = new GarbageCollectRunnable(this, true, false);
    2610               0 :     idleGCEventTarget = new WorkerRunnableEventTarget(runnable);
    2611                 : 
    2612               0 :     idleGCEvent = runnable;
    2613                 :   }
    2614                 : 
    2615               0 :   mMemoryReporter = new WorkerMemoryReporter(this);
    2616                 : 
    2617               0 :   if (NS_FAILED(NS_RegisterMemoryMultiReporter(mMemoryReporter))) {
    2618               0 :     NS_WARNING("Failed to register memory reporter!");
    2619               0 :     mMemoryReporter = nsnull;
    2620                 :   }
    2621                 : 
    2622               0 :   for (;;) {
    2623                 :     Status currentStatus;
    2624                 :     bool scheduleIdleGC;
    2625                 : 
    2626                 :     WorkerRunnable* event;
    2627                 :     {
    2628               0 :       MutexAutoLock lock(mMutex);
    2629                 : 
    2630               0 :       while (!mControlQueue.Pop(event) && !mQueue.Pop(event)) {
    2631               0 :         mCondVar.Wait();
    2632                 :       }
    2633                 : 
    2634                 :       bool eventIsNotIdleGCEvent;
    2635               0 :       currentStatus = mStatus;
    2636                 : 
    2637                 :       {
    2638               0 :         MutexAutoUnlock unlock(mMutex);
    2639                 : 
    2640               0 :         if (!normalGCTimerRunning &&
    2641               0 :             event != idleGCEvent &&
    2642                 :             currentStatus <= Terminating) {
    2643                 :           // Must always cancel before changing the timer's target.
    2644               0 :           if (NS_FAILED(gcTimer->Cancel())) {
    2645               0 :             NS_WARNING("Failed to cancel GC timer!");
    2646                 :           }
    2647                 : 
    2648               0 :           if (NS_SUCCEEDED(gcTimer->SetTarget(normalGCEventTarget)) &&
    2649               0 :               NS_SUCCEEDED(gcTimer->InitWithFuncCallback(
    2650                 :                                              DummyCallback, nsnull,
    2651                 :                                              NORMAL_GC_TIMER_DELAY_MS,
    2652                 :                                              nsITimer::TYPE_REPEATING_SLACK))) {
    2653               0 :             normalGCTimerRunning = true;
    2654                 :           }
    2655                 :           else {
    2656               0 :             JS_ReportError(aCx, "Failed to start normal GC timer!");
    2657                 :           }
    2658                 :         }
    2659                 : 
    2660                 : #ifdef EXTRA_GC
    2661                 :         // Find GC bugs...
    2662                 :         JS_GC(aCx);
    2663                 : #endif
    2664                 : 
    2665                 :         // Keep track of whether or not this is the idle GC event.
    2666               0 :         eventIsNotIdleGCEvent = event != idleGCEvent;
    2667                 : 
    2668               0 :         static_cast<nsIRunnable*>(event)->Run();
    2669               0 :         NS_RELEASE(event);
    2670                 :       }
    2671                 : 
    2672               0 :       currentStatus = mStatus;
    2673               0 :       scheduleIdleGC = mControlQueue.IsEmpty() &&
    2674               0 :                        mQueue.IsEmpty() &&
    2675               0 :                        eventIsNotIdleGCEvent;
    2676                 :     }
    2677                 : 
    2678                 :     // Take care of the GC timer. If we're starting the close sequence then we
    2679                 :     // kill the timer once and for all. Otherwise we schedule the idle timeout
    2680                 :     // if there are no more events.
    2681               0 :     if (currentStatus > Terminating || scheduleIdleGC) {
    2682               0 :       if (NS_SUCCEEDED(gcTimer->Cancel())) {
    2683               0 :         normalGCTimerRunning = false;
    2684                 :       }
    2685                 :       else {
    2686               0 :         NS_WARNING("Failed to cancel GC timer!");
    2687                 :       }
    2688                 :     }
    2689                 : 
    2690               0 :     if (scheduleIdleGC) {
    2691               0 :       if (NS_SUCCEEDED(gcTimer->SetTarget(idleGCEventTarget)) &&
    2692               0 :           NS_SUCCEEDED(gcTimer->InitWithFuncCallback(
    2693                 :                                                     DummyCallback, nsnull,
    2694                 :                                                     IDLE_GC_TIMER_DELAY_MS,
    2695                 :                                                     nsITimer::TYPE_ONE_SHOT))) {
    2696                 :       }
    2697                 :       else {
    2698               0 :         JS_ReportError(aCx, "Failed to start idle GC timer!");
    2699                 :       }
    2700                 :     }
    2701                 : 
    2702                 : #ifdef EXTRA_GC
    2703                 :     // Find GC bugs...
    2704                 :     JS_GC(aCx);
    2705                 : #endif
    2706                 : 
    2707               0 :     if (currentStatus != Running && !HasActiveFeatures()) {
    2708                 :       // If the close handler has finished and all features are done then we can
    2709                 :       // kill this thread.
    2710               0 :       if (mCloseHandlerFinished && currentStatus != Killing) {
    2711               0 :         if (!NotifyInternal(aCx, Killing)) {
    2712               0 :           JS_ReportPendingException(aCx);
    2713                 :         }
    2714                 : #ifdef DEBUG
    2715                 :         {
    2716               0 :           MutexAutoLock lock(mMutex);
    2717               0 :           currentStatus = mStatus;
    2718                 :         }
    2719               0 :         NS_ASSERTION(currentStatus == Killing, "Should have changed status!");
    2720                 : #else
    2721                 :         currentStatus = Killing;
    2722                 : #endif
    2723                 :       }
    2724                 : 
    2725                 :       // If we're supposed to die then we should exit the loop.
    2726               0 :       if (currentStatus == Killing) {
    2727                 :         // Always make sure the timer is canceled.
    2728               0 :         if (NS_FAILED(gcTimer->Cancel())) {
    2729               0 :           NS_WARNING("Failed to cancel the GC timer!");
    2730                 :         }
    2731                 : 
    2732                 :         // Call this before unregistering the reporter as we may be racing with
    2733                 :         // the main thread.
    2734               0 :         DisableMemoryReporter();
    2735                 : 
    2736               0 :         if (mMemoryReporter) {
    2737               0 :           if (NS_FAILED(NS_UnregisterMemoryMultiReporter(mMemoryReporter))) {
    2738               0 :             NS_WARNING("Failed to unregister memory reporter!");
    2739                 :           }
    2740               0 :           mMemoryReporter = nsnull;
    2741                 :         }
    2742                 : 
    2743               0 :         StopAcceptingEvents();
    2744                 :         return;
    2745                 :       }
    2746                 :     }
    2747                 :   }
    2748                 : 
    2749               0 :   NS_NOTREACHED("Shouldn't get here!");
    2750                 : }
    2751                 : 
    2752                 : bool
    2753               0 : WorkerPrivate::OperationCallback(JSContext* aCx)
    2754                 : {
    2755               0 :   AssertIsOnWorkerThread();
    2756                 : 
    2757               0 :   bool mayContinue = true;
    2758                 : 
    2759               0 :   for (;;) {
    2760                 :     // Run all control events now.
    2761               0 :     mayContinue = ProcessAllControlRunnables();
    2762                 : 
    2763               0 :     if (!mayContinue || !mSuspended) {
    2764                 :       break;
    2765                 :     }
    2766                 : 
    2767                 :     // Clean up before suspending.
    2768               0 :     JS_FlushCaches(aCx);
    2769               0 :     JS_GC(aCx);
    2770                 : 
    2771               0 :     while ((mayContinue = MayContinueRunning())) {
    2772               0 :       MutexAutoLock lock(mMutex);
    2773               0 :       if (!mControlQueue.IsEmpty()) {
    2774                 :         break;
    2775                 :       }
    2776                 : 
    2777               0 :       mCondVar.Wait(PR_MillisecondsToInterval(RemainingRunTimeMS()));
    2778                 :     }
    2779                 :   }
    2780                 : 
    2781               0 :   if (!mayContinue) {
    2782                 :     // We want only uncatchable exceptions here.
    2783               0 :     NS_ASSERTION(!JS_IsExceptionPending(aCx),
    2784                 :                  "Should not have an exception set here!");
    2785               0 :     return false;
    2786                 :   }
    2787                 : 
    2788               0 :   return true;
    2789                 : }
    2790                 : 
    2791                 : void
    2792               0 : WorkerPrivate::ScheduleDeletion(bool aWasPending)
    2793                 : {
    2794               0 :   AssertIsOnWorkerThread();
    2795               0 :   NS_ASSERTION(mChildWorkers.IsEmpty(), "Live child workers!");
    2796               0 :   NS_ASSERTION(mSyncQueues.IsEmpty(), "Should have no sync queues here!");
    2797                 : 
    2798               0 :   StopAcceptingEvents();
    2799                 : 
    2800                 :   nsIThread* currentThread;
    2801               0 :   if (aWasPending) {
    2802                 :     // Don't want to close down this thread since we never got to run!
    2803               0 :     currentThread = nsnull;
    2804                 :   }
    2805                 :   else {
    2806               0 :     currentThread = NS_GetCurrentThread();
    2807               0 :     NS_ASSERTION(currentThread, "This should never be null!");
    2808                 :   }
    2809                 : 
    2810               0 :   WorkerPrivate* parent = GetParent();
    2811               0 :   if (parent) {
    2812                 :     nsRefPtr<WorkerFinishedRunnable> runnable =
    2813               0 :       new WorkerFinishedRunnable(parent, this, currentThread);
    2814               0 :     if (!runnable->Dispatch(nsnull)) {
    2815               0 :       NS_WARNING("Failed to dispatch runnable!");
    2816                 :     }
    2817                 :   }
    2818                 :   else {
    2819                 :     nsRefPtr<TopLevelWorkerFinishedRunnable> runnable =
    2820               0 :       new TopLevelWorkerFinishedRunnable(this, currentThread);
    2821               0 :     if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
    2822               0 :       NS_WARNING("Failed to dispatch runnable!");
    2823                 :     }
    2824                 :   }
    2825               0 : }
    2826                 : 
    2827                 : bool
    2828               0 : WorkerPrivate::BlockAndCollectRuntimeStats(bool aIsQuick, void* aData, bool* aDisabled)
    2829                 : {
    2830               0 :   AssertIsOnMainThread();
    2831               0 :   NS_ASSERTION(aData, "Null data!");
    2832                 : 
    2833                 :   {
    2834               0 :     MutexAutoLock lock(mMutex);
    2835                 : 
    2836               0 :     if (mMemoryReporterDisabled) {
    2837               0 :       *aDisabled = true;
    2838               0 :       return true;
    2839                 :     }
    2840                 : 
    2841               0 :     *aDisabled = false;
    2842               0 :     mMemoryReporterRunning = true;
    2843                 :   }
    2844                 : 
    2845                 :   bool succeeded;
    2846                 : 
    2847                 :   nsRefPtr<CollectRuntimeStatsRunnable> runnable =
    2848               0 :     new CollectRuntimeStatsRunnable(this, aIsQuick, aData, &succeeded);
    2849               0 :   if (!runnable->Dispatch(nsnull)) {
    2850               0 :     NS_WARNING("Failed to dispatch runnable!");
    2851               0 :     succeeded = false;
    2852                 :   }
    2853                 : 
    2854                 :   {
    2855               0 :     MutexAutoLock lock(mMutex);
    2856               0 :     mMemoryReporterRunning = false;
    2857                 :   }
    2858                 : 
    2859               0 :   return succeeded;
    2860                 : }
    2861                 : 
    2862                 : bool
    2863               0 : WorkerPrivate::DisableMemoryReporter()
    2864                 : {
    2865               0 :   AssertIsOnWorkerThread();
    2866                 : 
    2867               0 :   bool result = true;
    2868                 : 
    2869                 :   {
    2870               0 :     MutexAutoLock lock(mMutex);
    2871                 : 
    2872               0 :     mMemoryReporterDisabled = true;
    2873                 : 
    2874               0 :     while (mMemoryReporterRunning) {
    2875               0 :       MutexAutoUnlock unlock(mMutex);
    2876               0 :       result = ProcessAllControlRunnables() && result;
    2877                 :     }
    2878                 :   }
    2879                 : 
    2880               0 :   return result;
    2881                 : }
    2882                 : 
    2883                 : bool
    2884               0 : WorkerPrivate::ProcessAllControlRunnables()
    2885                 : {
    2886               0 :   AssertIsOnWorkerThread();
    2887                 : 
    2888               0 :   bool result = true;
    2889                 : 
    2890               0 :   for (;;) {
    2891                 :     WorkerRunnable* event;
    2892                 :     {
    2893               0 :       MutexAutoLock lock(mMutex);
    2894               0 :       if (!mControlQueue.Pop(event)) {
    2895                 :         break;
    2896                 :       }
    2897                 :     }
    2898                 : 
    2899               0 :     if (NS_FAILED(static_cast<nsIRunnable*>(event)->Run())) {
    2900               0 :       result = false;
    2901                 :     }
    2902                 : 
    2903               0 :     NS_RELEASE(event);
    2904                 :   }
    2905               0 :   return result;
    2906                 : }
    2907                 : 
    2908                 : bool
    2909               0 : WorkerPrivate::Dispatch(WorkerRunnable* aEvent, EventQueue* aQueue)
    2910                 : {
    2911               0 :   nsRefPtr<WorkerRunnable> event(aEvent);
    2912                 : 
    2913                 :   {
    2914               0 :     MutexAutoLock lock(mMutex);
    2915                 : 
    2916               0 :     if (mStatus == Dead) {
    2917                 :       // Nothing may be added after we've set Dead.
    2918               0 :       return false;
    2919                 :     }
    2920                 : 
    2921               0 :     if (aQueue == &mQueue) {
    2922                 :       // Check parent status.
    2923               0 :       Status parentStatus = ParentStatus();
    2924               0 :       if (parentStatus >= Terminating) {
    2925                 :         // Throw.
    2926               0 :         return false;
    2927                 :       }
    2928                 : 
    2929                 :       // Check inner status too.
    2930               0 :       if (parentStatus >= Closing || mStatus >= Closing) {
    2931                 :         // Silently eat this one.
    2932               0 :         return true;
    2933                 :       }
    2934                 :     }
    2935                 : 
    2936               0 :     if (!aQueue->Push(event)) {
    2937               0 :       return false;
    2938                 :     }
    2939                 : 
    2940               0 :     if (aQueue == &mControlQueue && mJSContext) {
    2941               0 :       JS_TriggerOperationCallback(JS_GetRuntime(mJSContext));
    2942                 :     }
    2943                 : 
    2944               0 :     mCondVar.Notify();
    2945                 :   }
    2946                 : 
    2947               0 :   event.forget();
    2948               0 :   return true;
    2949                 : }
    2950                 : 
    2951                 : bool
    2952               0 : WorkerPrivate::DispatchToSyncQueue(WorkerSyncRunnable* aEvent)
    2953                 : {
    2954               0 :   nsRefPtr<WorkerRunnable> event(aEvent);
    2955                 : 
    2956                 :   {
    2957               0 :     MutexAutoLock lock(mMutex);
    2958                 : 
    2959               0 :     NS_ASSERTION(mSyncQueues.Length() > aEvent->mSyncQueueKey, "Bad event!");
    2960                 : 
    2961               0 :     if (!mSyncQueues[aEvent->mSyncQueueKey]->mQueue.Push(event)) {
    2962               0 :       return false;
    2963                 :     }
    2964                 : 
    2965               0 :     mCondVar.Notify();
    2966                 :   }
    2967                 : 
    2968               0 :   event.forget();
    2969               0 :   return true;
    2970                 : }
    2971                 : 
    2972                 : void
    2973               0 : WorkerPrivate::ClearQueue(EventQueue* aQueue)
    2974                 : {
    2975               0 :   AssertIsOnWorkerThread();
    2976               0 :   mMutex.AssertCurrentThreadOwns();
    2977                 : 
    2978                 :   WorkerRunnable* event;
    2979               0 :   while (aQueue->Pop(event)) {
    2980               0 :     if (event->WantsToRunDuringClear()) {
    2981               0 :       MutexAutoUnlock unlock(mMutex);
    2982                 : 
    2983               0 :       static_cast<nsIRunnable*>(event)->Run();
    2984                 :     }
    2985               0 :     event->Release();
    2986                 :   }
    2987               0 : }
    2988                 : 
    2989                 : PRUint32
    2990               0 : WorkerPrivate::RemainingRunTimeMS() const
    2991                 : {
    2992               0 :   if (mKillTime.IsNull()) {
    2993               0 :     return PR_UINT32_MAX;
    2994                 :   }
    2995               0 :   TimeDuration runtime = mKillTime - TimeStamp::Now();
    2996               0 :   double ms = runtime > TimeDuration(0) ? runtime.ToMilliseconds() : 0;
    2997               0 :   return ms > double(PR_UINT32_MAX) ? PR_UINT32_MAX : PRUint32(ms);
    2998                 : }
    2999                 : 
    3000                 : bool
    3001               0 : WorkerPrivate::SuspendInternal(JSContext* aCx)
    3002                 : {
    3003               0 :   AssertIsOnWorkerThread();
    3004                 : 
    3005               0 :   NS_ASSERTION(!mSuspended, "Already suspended!");
    3006                 : 
    3007               0 :   mSuspended = true;
    3008               0 :   return true;
    3009                 : }
    3010                 : 
    3011                 : bool
    3012               0 : WorkerPrivate::ResumeInternal(JSContext* aCx)
    3013                 : {
    3014               0 :   AssertIsOnWorkerThread();
    3015                 : 
    3016               0 :   NS_ASSERTION(mSuspended, "Not yet suspended!");
    3017                 : 
    3018               0 :   mSuspended = false;
    3019               0 :   return true;
    3020                 : }
    3021                 : 
    3022                 : void
    3023               0 : WorkerPrivate::TraceInternal(JSTracer* aTrc)
    3024                 : {
    3025               0 :   AssertIsOnWorkerThread();
    3026                 : 
    3027               0 :   for (PRUint32 index = 0; index < mTimeouts.Length(); index++) {
    3028               0 :     TimeoutInfo* info = mTimeouts[index];
    3029               0 :     JS_CALL_VALUE_TRACER(aTrc, info->mTimeoutVal,
    3030                 :                          "WorkerPrivate timeout value");
    3031               0 :     for (PRUint32 index2 = 0; index2 < info->mExtraArgVals.Length(); index2++) {
    3032               0 :       JS_CALL_VALUE_TRACER(aTrc, info->mExtraArgVals[index2],
    3033                 :                            "WorkerPrivate timeout extra argument value");
    3034                 :     }
    3035                 :   }
    3036               0 : }
    3037                 : 
    3038                 : bool
    3039               0 : WorkerPrivate::ModifyBusyCountFromWorker(JSContext* aCx, bool aIncrease)
    3040                 : {
    3041               0 :   AssertIsOnWorkerThread();
    3042                 : 
    3043                 :   {
    3044               0 :     MutexAutoLock lock(mMutex);
    3045                 : 
    3046                 :     // If we're in shutdown then the busy count is no longer being considered so
    3047                 :     // just return now.
    3048               0 :     if (mStatus >= Killing) {
    3049               0 :       return true;
    3050                 :     }
    3051                 :   }
    3052                 : 
    3053                 :   nsRefPtr<ModifyBusyCountRunnable> runnable =
    3054               0 :     new ModifyBusyCountRunnable(this, aIncrease);
    3055               0 :   return runnable->Dispatch(aCx);
    3056                 : }
    3057                 : 
    3058                 : bool
    3059               0 : WorkerPrivate::AddChildWorker(JSContext* aCx, ParentType* aChildWorker)
    3060                 : {
    3061               0 :   AssertIsOnWorkerThread();
    3062                 : 
    3063                 :   Status currentStatus;
    3064                 :   {
    3065               0 :     MutexAutoLock lock(mMutex);
    3066               0 :     currentStatus = mStatus;
    3067                 :   }
    3068                 : 
    3069               0 :   if (currentStatus > Running) {
    3070               0 :     JS_ReportError(aCx, "Cannot create child workers from the close handler!");
    3071               0 :     return false;
    3072                 :   }
    3073                 : 
    3074               0 :   NS_ASSERTION(!mChildWorkers.Contains(aChildWorker),
    3075                 :                "Already know about this one!");
    3076               0 :   mChildWorkers.AppendElement(aChildWorker);
    3077                 : 
    3078               0 :   return mChildWorkers.Length() == 1 ?
    3079                 :          ModifyBusyCountFromWorker(aCx, true) :
    3080               0 :          true;
    3081                 : }
    3082                 : 
    3083                 : void
    3084               0 : WorkerPrivate::RemoveChildWorker(JSContext* aCx, ParentType* aChildWorker)
    3085                 : {
    3086               0 :   AssertIsOnWorkerThread();
    3087                 : 
    3088               0 :   NS_ASSERTION(mChildWorkers.Contains(aChildWorker),
    3089                 :                "Didn't know about this one!");
    3090               0 :   mChildWorkers.RemoveElement(aChildWorker);
    3091                 : 
    3092               0 :   if (mChildWorkers.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) {
    3093               0 :     NS_WARNING("Failed to modify busy count!");
    3094                 :   }
    3095               0 : }
    3096                 : 
    3097                 : bool
    3098               0 : WorkerPrivate::AddFeature(JSContext* aCx, WorkerFeature* aFeature)
    3099                 : {
    3100               0 :   AssertIsOnWorkerThread();
    3101                 : 
    3102                 :   {
    3103               0 :     MutexAutoLock lock(mMutex);
    3104                 : 
    3105               0 :     if (mStatus >= Canceling) {
    3106               0 :       return false;
    3107                 :     }
    3108                 :   }
    3109                 : 
    3110               0 :   NS_ASSERTION(!mFeatures.Contains(aFeature), "Already know about this one!");
    3111               0 :   mFeatures.AppendElement(aFeature);
    3112                 : 
    3113               0 :   return mFeatures.Length() == 1 ?
    3114                 :          ModifyBusyCountFromWorker(aCx, true) :
    3115               0 :          true;
    3116                 : }
    3117                 : 
    3118                 : void
    3119               0 : WorkerPrivate::RemoveFeature(JSContext* aCx, WorkerFeature* aFeature)
    3120                 : {
    3121               0 :   AssertIsOnWorkerThread();
    3122                 : 
    3123               0 :   NS_ASSERTION(mFeatures.Contains(aFeature), "Didn't know about this one!");
    3124               0 :   mFeatures.RemoveElement(aFeature);
    3125                 : 
    3126               0 :   if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(aCx, false)) {
    3127               0 :     NS_WARNING("Failed to modify busy count!");
    3128                 :   }
    3129               0 : }
    3130                 : 
    3131                 : void
    3132               0 : WorkerPrivate::NotifyFeatures(JSContext* aCx, Status aStatus)
    3133                 : {
    3134               0 :   AssertIsOnWorkerThread();
    3135                 : 
    3136               0 :   NS_ASSERTION(aStatus > Running, "Bad status!");
    3137                 : 
    3138               0 :   if (aStatus >= Closing) {
    3139               0 :     CancelAllTimeouts(aCx);
    3140                 :   }
    3141                 : 
    3142               0 :   nsAutoTArray<WorkerFeature*, 30> features;
    3143               0 :   features.AppendElements(mFeatures);
    3144                 : 
    3145               0 :   for (PRUint32 index = 0; index < features.Length(); index++) {
    3146               0 :     if (!features[index]->Notify(aCx, aStatus)) {
    3147               0 :       NS_WARNING("Failed to notify feature!");
    3148                 :     }
    3149                 :   }
    3150                 : 
    3151               0 :   nsAutoTArray<ParentType*, 10> children;
    3152               0 :   children.AppendElements(mChildWorkers);
    3153                 : 
    3154               0 :   for (PRUint32 index = 0; index < children.Length(); index++) {
    3155               0 :     if (!children[index]->Notify(aCx, aStatus)) {
    3156               0 :       NS_WARNING("Failed to notify child worker!");
    3157                 :     }
    3158                 :   }
    3159               0 : }
    3160                 : 
    3161                 : void
    3162               0 : WorkerPrivate::CancelAllTimeouts(JSContext* aCx)
    3163                 : {
    3164               0 :   AssertIsOnWorkerThread();
    3165                 : 
    3166               0 :   if (mTimerRunning) {
    3167               0 :     NS_ASSERTION(mTimer, "Huh?!");
    3168               0 :     NS_ASSERTION(!mTimeouts.IsEmpty(), "Huh?!");
    3169                 : 
    3170               0 :     if (NS_FAILED(mTimer->Cancel())) {
    3171               0 :       NS_WARNING("Failed to cancel timer!");
    3172                 :     }
    3173                 : 
    3174               0 :     for (PRUint32 index = 0; index < mTimeouts.Length(); index++) {
    3175               0 :       mTimeouts[index]->mCanceled = true;
    3176                 :     }
    3177                 : 
    3178               0 :     if (!RunExpiredTimeouts(aCx)) {
    3179               0 :       JS_ReportPendingException(aCx);
    3180                 :     }
    3181                 : 
    3182               0 :     mTimerRunning = false;
    3183                 :   }
    3184                 : #ifdef DEBUG
    3185               0 :   else if (!mRunningExpiredTimeouts) {
    3186               0 :     NS_ASSERTION(mTimeouts.IsEmpty(), "Huh?!");
    3187                 :   }
    3188                 : #endif
    3189                 : 
    3190               0 :   mTimer = nsnull;
    3191               0 : }
    3192                 : 
    3193                 : PRUint32
    3194               0 : WorkerPrivate::CreateNewSyncLoop()
    3195                 : {
    3196               0 :   AssertIsOnWorkerThread();
    3197                 : 
    3198               0 :   NS_ASSERTION(mSyncQueues.Length() < PR_UINT32_MAX,
    3199                 :                "Should have bailed by now!");
    3200                 : 
    3201               0 :   mSyncQueues.AppendElement(new SyncQueue());
    3202               0 :   return mSyncQueues.Length() - 1;
    3203                 : }
    3204                 : 
    3205                 : bool
    3206               0 : WorkerPrivate::RunSyncLoop(JSContext* aCx, PRUint32 aSyncLoopKey)
    3207                 : {
    3208               0 :   AssertIsOnWorkerThread();
    3209                 : 
    3210               0 :   NS_ASSERTION(!mSyncQueues.IsEmpty() ||
    3211                 :                (aSyncLoopKey != mSyncQueues.Length() - 1),
    3212                 :                "Forgot to call CreateNewSyncLoop!");
    3213               0 :   if (aSyncLoopKey != mSyncQueues.Length() - 1) {
    3214               0 :     return false;
    3215                 :   }
    3216                 : 
    3217               0 :   SyncQueue* syncQueue = mSyncQueues[aSyncLoopKey].get();
    3218                 : 
    3219               0 :   for (;;) {
    3220                 :     WorkerRunnable* event;
    3221                 :     {
    3222               0 :       MutexAutoLock lock(mMutex);
    3223                 : 
    3224               0 :       while (!mControlQueue.Pop(event) && !syncQueue->mQueue.Pop(event)) {
    3225               0 :         mCondVar.Wait();
    3226                 :       }
    3227                 :     }
    3228                 : 
    3229                 : #ifdef EXTRA_GC
    3230                 :     // Find GC bugs...
    3231                 :     JS_GC(mJSContext);
    3232                 : #endif
    3233                 : 
    3234               0 :     static_cast<nsIRunnable*>(event)->Run();
    3235               0 :     NS_RELEASE(event);
    3236                 : 
    3237                 : #ifdef EXTRA_GC
    3238                 :     // Find GC bugs...
    3239                 :     JS_GC(mJSContext);
    3240                 : #endif
    3241                 : 
    3242               0 :     if (syncQueue->mComplete) {
    3243               0 :       NS_ASSERTION(mSyncQueues.Length() - 1 == aSyncLoopKey,
    3244                 :                    "Mismatched calls!");
    3245               0 :       NS_ASSERTION(syncQueue->mQueue.IsEmpty(), "Unprocessed sync events!");
    3246                 : 
    3247               0 :       bool result = syncQueue->mResult;
    3248               0 :       mSyncQueues.RemoveElementAt(aSyncLoopKey);
    3249                 : 
    3250                 : #ifdef DEBUG
    3251               0 :       syncQueue = nsnull;
    3252                 : #endif
    3253                 : 
    3254               0 :       return result;
    3255                 :     }
    3256                 :   }
    3257                 : 
    3258                 :   NS_NOTREACHED("Shouldn't get here!");
    3259                 :   return false;
    3260                 : }
    3261                 : 
    3262                 : void
    3263               0 : WorkerPrivate::StopSyncLoop(PRUint32 aSyncLoopKey, bool aSyncResult)
    3264                 : {
    3265               0 :   AssertIsOnWorkerThread();
    3266                 : 
    3267               0 :   NS_ASSERTION(mSyncQueues.IsEmpty() ||
    3268                 :                (aSyncLoopKey == mSyncQueues.Length() - 1),
    3269                 :                "Forgot to call CreateNewSyncLoop!");
    3270               0 :   if (aSyncLoopKey != mSyncQueues.Length() - 1) {
    3271               0 :     return;
    3272                 :   }
    3273                 : 
    3274               0 :   SyncQueue* syncQueue = mSyncQueues[aSyncLoopKey].get();
    3275                 : 
    3276               0 :   NS_ASSERTION(!syncQueue->mComplete, "Already called StopSyncLoop?!");
    3277                 : 
    3278               0 :   syncQueue->mResult = aSyncResult;
    3279               0 :   syncQueue->mComplete = true;
    3280                 : }
    3281                 : 
    3282                 : bool
    3283               0 : WorkerPrivate::PostMessageToParent(JSContext* aCx, jsval aMessage)
    3284                 : {
    3285               0 :   AssertIsOnWorkerThread();
    3286                 : 
    3287                 :   JSStructuredCloneCallbacks* callbacks =
    3288               0 :     IsChromeWorker() ?
    3289                 :     &gChromeWorkerStructuredCloneCallbacks :
    3290               0 :     &gWorkerStructuredCloneCallbacks;
    3291                 : 
    3292               0 :   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
    3293                 : 
    3294               0 :   JSAutoStructuredCloneBuffer buffer;
    3295               0 :   if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) {
    3296               0 :     return false;
    3297                 :   }
    3298                 : 
    3299                 :   nsRefPtr<MessageEventRunnable> runnable =
    3300                 :     new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer,
    3301               0 :                              clonedObjects);
    3302               0 :   return runnable->Dispatch(aCx);
    3303                 : }
    3304                 : 
    3305                 : bool
    3306               0 : WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus)
    3307                 : {
    3308               0 :   AssertIsOnWorkerThread();
    3309                 : 
    3310               0 :   NS_ASSERTION(aStatus > Running && aStatus < Dead, "Bad status!");
    3311                 : 
    3312                 :   // Save the old status and set the new status.
    3313                 :   Status previousStatus;
    3314                 :   {
    3315               0 :     MutexAutoLock lock(mMutex);
    3316                 : 
    3317               0 :     if (mStatus >= aStatus) {
    3318               0 :       return true;
    3319                 :     }
    3320                 : 
    3321               0 :     previousStatus = mStatus;
    3322               0 :     mStatus = aStatus;
    3323                 :   }
    3324                 : 
    3325                 :   // Now that status > Running, no-one can create a new mCrossThreadDispatcher
    3326                 :   // if we don't already have one.
    3327               0 :   if (mCrossThreadDispatcher) {
    3328                 :     // Since we'll no longer process events, make sure we no longer allow
    3329                 :     // anyone to post them.
    3330                 :     // We have to do this without mMutex held, since our mutex must be
    3331                 :     // acquired *after* mCrossThreadDispatcher's mutex when they're both held.
    3332               0 :     mCrossThreadDispatcher->Forget();
    3333                 :   }
    3334                 : 
    3335               0 :   NS_ASSERTION(previousStatus != Pending, "How is this possible?!");
    3336                 : 
    3337               0 :   NS_ASSERTION(previousStatus >= Canceling || mKillTime.IsNull(),
    3338                 :                "Bad kill time set!");
    3339                 : 
    3340                 :   // Let all our features know the new status.
    3341               0 :   NotifyFeatures(aCx, aStatus);
    3342                 : 
    3343                 :   // There's nothing to do here if we never succeeded in running the worker
    3344                 :   // script or if the close handler has already run.
    3345               0 :   if (!JS_GetGlobalObject(aCx) || mCloseHandlerFinished) {
    3346               0 :     return true;
    3347                 :   }
    3348                 : 
    3349                 :   // If this is the first time our status has changed then we need to clear the
    3350                 :   // main event queue. We also need to schedule the close handler unless we're
    3351                 :   // being shut down.
    3352               0 :   if (previousStatus == Running) {
    3353               0 :     NS_ASSERTION(!mCloseHandlerStarted && !mCloseHandlerFinished,
    3354                 :                  "This is impossible!");
    3355                 : 
    3356                 :     {
    3357               0 :       MutexAutoLock lock(mMutex);
    3358               0 :       ClearQueue(&mQueue);
    3359                 :     }
    3360                 : 
    3361               0 :     if (aStatus != Killing) {
    3362               0 :       nsRefPtr<CloseEventRunnable> closeRunnable = new CloseEventRunnable(this);
    3363                 : 
    3364               0 :       MutexAutoLock lock(mMutex);
    3365                 : 
    3366               0 :       if (!mQueue.Push(closeRunnable)) {
    3367               0 :         NS_WARNING("Failed to push closeRunnable!");
    3368               0 :         return false;
    3369                 :       }
    3370                 : 
    3371               0 :       closeRunnable.forget();
    3372                 :     }
    3373                 :   }
    3374                 : 
    3375               0 :   if (aStatus == Closing) {
    3376                 :     // Notify parent to stop sending us messages and balance our busy count.
    3377               0 :     nsRefPtr<CloseRunnable> runnable = new CloseRunnable(this);
    3378               0 :     if (!runnable->Dispatch(aCx)) {
    3379               0 :       return false;
    3380                 :     }
    3381                 : 
    3382                 :     // Don't abort the script.
    3383               0 :     return true;
    3384                 :   }
    3385                 : 
    3386               0 :   if (aStatus == Terminating) {
    3387                 :     // Only abort the script if we're not yet running the close handler.
    3388               0 :     return mCloseHandlerStarted;
    3389                 :   }
    3390                 : 
    3391               0 :   if (aStatus == Canceling) {
    3392                 :     // We need to enforce a timeout on the close handler.
    3393               0 :     NS_ASSERTION(previousStatus == Running || previousStatus == Closing ||
    3394                 :                  previousStatus == Terminating,
    3395                 :                  "Bad previous status!");
    3396                 : 
    3397               0 :     PRUint32 killSeconds = RuntimeService::GetCloseHandlerTimeoutSeconds();
    3398               0 :     if (killSeconds) {
    3399               0 :       mKillTime = TimeStamp::Now() + TimeDuration::FromSeconds(killSeconds);
    3400                 : 
    3401               0 :       if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable(aCx)) {
    3402               0 :         return false;
    3403                 :       }
    3404                 :     }
    3405                 : 
    3406                 :     // Only abort the script if we're not yet running the close handler.
    3407               0 :     return mCloseHandlerStarted;
    3408                 :   }
    3409                 : 
    3410               0 :   if (aStatus == Killing) {
    3411               0 :     mKillTime = TimeStamp::Now();
    3412                 : 
    3413               0 :     if (!mCloseHandlerFinished && !ScheduleKillCloseEventRunnable(aCx)) {
    3414               0 :       return false;
    3415                 :     }
    3416                 : 
    3417                 :     // Always abort the script.
    3418               0 :     return false;
    3419                 :   }
    3420                 : 
    3421               0 :   NS_NOTREACHED("Should never get here!");
    3422               0 :   return false;
    3423                 : }
    3424                 : 
    3425                 : bool
    3426               0 : WorkerPrivate::ScheduleKillCloseEventRunnable(JSContext* aCx)
    3427                 : {
    3428               0 :   AssertIsOnWorkerThread();
    3429               0 :   NS_ASSERTION(!mKillTime.IsNull(), "Must have a kill time!");
    3430                 : 
    3431                 :   nsRefPtr<KillCloseEventRunnable> killCloseEventRunnable =
    3432               0 :     new KillCloseEventRunnable(this);
    3433               0 :   if (!killCloseEventRunnable->SetTimeout(aCx, RemainingRunTimeMS())) {
    3434               0 :     return false;
    3435                 :   }
    3436                 : 
    3437               0 :   MutexAutoLock lock(mMutex);
    3438                 : 
    3439               0 :   if (!mQueue.Push(killCloseEventRunnable)) {
    3440               0 :     NS_WARNING("Failed to push killCloseEventRunnable!");
    3441               0 :     return false;
    3442                 :   }
    3443                 : 
    3444               0 :   killCloseEventRunnable.forget();
    3445               0 :   return true;
    3446                 : }
    3447                 : 
    3448                 : void
    3449               0 : WorkerPrivate::ReportError(JSContext* aCx, const char* aMessage,
    3450                 :                            JSErrorReport* aReport)
    3451                 : {
    3452               0 :   AssertIsOnWorkerThread();
    3453                 : 
    3454               0 :   if (!MayContinueRunning() || mErrorHandlerRecursionCount == 2) {
    3455               0 :     return;
    3456                 :   }
    3457                 : 
    3458               0 :   NS_ASSERTION(mErrorHandlerRecursionCount == 0 ||
    3459                 :                mErrorHandlerRecursionCount == 1,
    3460                 :                "Bad recursion logic!");
    3461                 : 
    3462               0 :   JS_ClearPendingException(aCx);
    3463                 : 
    3464               0 :   nsString message, filename, line;
    3465                 :   PRUint32 lineNumber, columnNumber, flags, errorNumber;
    3466                 : 
    3467               0 :   if (aReport) {
    3468               0 :     if (aReport->ucmessage) {
    3469               0 :       message = aReport->ucmessage;
    3470                 :     }
    3471               0 :     filename = NS_ConvertUTF8toUTF16(aReport->filename);
    3472               0 :     line = aReport->uclinebuf;
    3473               0 :     lineNumber = aReport->lineno;
    3474               0 :     columnNumber = aReport->uctokenptr - aReport->uclinebuf;
    3475               0 :     flags = aReport->flags;
    3476               0 :     errorNumber = aReport->errorNumber;
    3477                 :   }
    3478                 :   else {
    3479               0 :     lineNumber = columnNumber = errorNumber = 0;
    3480               0 :     flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag;
    3481                 :   }
    3482                 : 
    3483               0 :   if (message.IsEmpty()) {
    3484               0 :     message = NS_ConvertUTF8toUTF16(aMessage);
    3485                 :   }
    3486                 : 
    3487               0 :   mErrorHandlerRecursionCount++;
    3488                 : 
    3489                 :   // Don't want to run the scope's error handler if this is a recursive error or
    3490                 :   // if there was an error in the close handler or if we ran out of memory.
    3491                 :   bool fireAtScope = mErrorHandlerRecursionCount == 1 &&
    3492               0 :                      !mCloseHandlerStarted &&
    3493               0 :                      errorNumber != JSMSG_OUT_OF_MEMORY;
    3494                 : 
    3495               0 :   if (!ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nsnull, message,
    3496                 :                                         filename, line, lineNumber,
    3497               0 :                                         columnNumber, flags, errorNumber, 0)) {
    3498               0 :     JS_ReportPendingException(aCx);
    3499                 :   }
    3500                 : 
    3501               0 :   mErrorHandlerRecursionCount--;
    3502                 : }
    3503                 : 
    3504                 : bool
    3505               0 : WorkerPrivate::SetTimeout(JSContext* aCx, unsigned aArgc, jsval* aVp,
    3506                 :                           bool aIsInterval)
    3507                 : {
    3508               0 :   AssertIsOnWorkerThread();
    3509               0 :   NS_ASSERTION(aArgc, "Huh?!");
    3510                 : 
    3511               0 :   const PRUint32 timerId = mNextTimeoutId++;
    3512                 : 
    3513                 :   Status currentStatus;
    3514                 :   {
    3515               0 :     MutexAutoLock lock(mMutex);
    3516               0 :     currentStatus = mStatus;
    3517                 :   }
    3518                 : 
    3519                 :   // It's a script bug if setTimeout/setInterval are called from a close handler
    3520                 :   // so throw an exception.
    3521               0 :   if (currentStatus == Closing) {
    3522               0 :     JS_ReportError(aCx, "Cannot schedule timeouts from the close handler!");
    3523                 :   }
    3524                 : 
    3525                 :   // If the worker is trying to call setTimeout/setInterval and the parent
    3526                 :   // thread has initiated the close process then just silently fail.
    3527               0 :   if (currentStatus >= Closing) {
    3528               0 :     return false;
    3529                 :   }
    3530                 : 
    3531               0 :   nsAutoPtr<TimeoutInfo> newInfo(new TimeoutInfo());
    3532               0 :   newInfo->mIsInterval = aIsInterval;
    3533               0 :   newInfo->mId = timerId;
    3534                 : 
    3535               0 :   if (NS_UNLIKELY(timerId == PR_UINT32_MAX)) {
    3536               0 :     NS_WARNING("Timeout ids overflowed!");
    3537               0 :     mNextTimeoutId = 1;
    3538                 :   }
    3539                 : 
    3540               0 :   jsval* argv = JS_ARGV(aCx, aVp);
    3541                 : 
    3542                 :   // Take care of the main argument.
    3543               0 :   if (JSVAL_IS_OBJECT(argv[0])) {
    3544               0 :     if (JS_ObjectIsCallable(aCx, JSVAL_TO_OBJECT(argv[0]))) {
    3545               0 :       newInfo->mTimeoutVal = argv[0];
    3546                 :     }
    3547                 :     else {
    3548               0 :       JSString* timeoutStr = JS_ValueToString(aCx, argv[0]);
    3549               0 :       if (!timeoutStr) {
    3550               0 :         return false;
    3551                 :       }
    3552               0 :       newInfo->mTimeoutVal = STRING_TO_JSVAL(timeoutStr);
    3553                 :     }
    3554                 :   }
    3555               0 :   else if (JSVAL_IS_STRING(argv[0])) {
    3556               0 :     newInfo->mTimeoutVal = argv[0];
    3557                 :   }
    3558                 :   else {
    3559                 :     JS_ReportError(aCx, "Useless %s call (missing quotes around argument?)",
    3560               0 :                    aIsInterval ? "setInterval" : "setTimeout");
    3561               0 :     return false;
    3562                 :   }
    3563                 : 
    3564                 :   // See if any of the optional arguments were passed.
    3565               0 :   if (aArgc > 1) {
    3566               0 :     double intervalMS = 0;
    3567               0 :     if (!JS_ValueToNumber(aCx, argv[1], &intervalMS)) {
    3568               0 :       return false;
    3569                 :     }
    3570               0 :     newInfo->mInterval = TimeDuration::FromMilliseconds(intervalMS);
    3571                 : 
    3572               0 :     if (aArgc > 2 && JSVAL_IS_OBJECT(newInfo->mTimeoutVal)) {
    3573               0 :       nsTArray<jsval> extraArgVals(aArgc - 2);
    3574               0 :       for (unsigned index = 2; index < aArgc; index++) {
    3575               0 :         extraArgVals.AppendElement(argv[index]);
    3576                 :       }
    3577               0 :       newInfo->mExtraArgVals.SwapElements(extraArgVals);
    3578                 :     }
    3579                 :   }
    3580                 : 
    3581               0 :   newInfo->mTargetTime = TimeStamp::Now() + newInfo->mInterval;
    3582                 : 
    3583               0 :   if (JSVAL_IS_STRING(newInfo->mTimeoutVal)) {
    3584                 :     const char* filenameChars;
    3585                 :     PRUint32 lineNumber;
    3586               0 :     if (nsJSUtils::GetCallingLocation(aCx, &filenameChars, &lineNumber)) {
    3587               0 :       newInfo->mFilename = filenameChars;
    3588               0 :       newInfo->mLineNumber = lineNumber;
    3589                 :     }
    3590                 :     else {
    3591               0 :       NS_WARNING("Failed to get calling location!");
    3592                 :     }
    3593                 :   }
    3594                 : 
    3595               0 :   mTimeouts.InsertElementSorted(newInfo.get(), GetAutoPtrComparator(mTimeouts));
    3596                 : 
    3597                 :   // If the timeout we just made is set to fire next then we need to update the
    3598                 :   // timer.
    3599               0 :   if (mTimeouts[0] == newInfo) {
    3600                 :     nsresult rv;
    3601                 : 
    3602               0 :     if (!mTimer) {
    3603               0 :       mTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
    3604               0 :       if (NS_FAILED(rv)) {
    3605               0 :         JS_ReportError(aCx, "Failed to create timer!");
    3606               0 :         return false;
    3607                 :       }
    3608                 : 
    3609               0 :       nsRefPtr<TimerRunnable> timerRunnable = new TimerRunnable(this);
    3610                 : 
    3611                 :       nsCOMPtr<nsIEventTarget> target =
    3612               0 :         new WorkerRunnableEventTarget(timerRunnable);
    3613               0 :       rv = mTimer->SetTarget(target);
    3614               0 :       if (NS_FAILED(rv)) {
    3615               0 :         JS_ReportError(aCx, "Failed to set timer's target!");
    3616               0 :         return false;
    3617                 :       }
    3618                 :     }
    3619                 : 
    3620               0 :     if (!mTimerRunning) {
    3621               0 :       if (!ModifyBusyCountFromWorker(aCx, true)) {
    3622               0 :         return false;
    3623                 :       }
    3624               0 :       mTimerRunning = true;
    3625                 :     }
    3626                 : 
    3627               0 :     if (!RescheduleTimeoutTimer(aCx)) {
    3628               0 :       return false;
    3629                 :     }
    3630                 :   }
    3631                 : 
    3632               0 :   JS_SET_RVAL(aCx, aVp, INT_TO_JSVAL(timerId));
    3633                 : 
    3634               0 :   newInfo.forget();
    3635               0 :   return true;
    3636                 : }
    3637                 : 
    3638                 : bool
    3639               0 : WorkerPrivate::ClearTimeout(JSContext* aCx, uint32 aId)
    3640                 : {
    3641               0 :   AssertIsOnWorkerThread();
    3642                 : 
    3643               0 :   if (!mTimeouts.IsEmpty()) {
    3644               0 :     NS_ASSERTION(mTimerRunning, "Huh?!");
    3645                 : 
    3646               0 :     for (PRUint32 index = 0; index < mTimeouts.Length(); index++) {
    3647               0 :       nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
    3648               0 :       if (info->mId == aId) {
    3649               0 :         info->mCanceled = true;
    3650               0 :         break;
    3651                 :       }
    3652                 :     }
    3653                 :   }
    3654                 : 
    3655               0 :   return true;
    3656                 : }
    3657                 : 
    3658                 : bool
    3659               0 : WorkerPrivate::RunExpiredTimeouts(JSContext* aCx)
    3660                 : {
    3661               0 :   AssertIsOnWorkerThread();
    3662                 : 
    3663                 :   // We may be called recursively (e.g. close() inside a timeout) or we could
    3664                 :   // have been canceled while this event was pending, bail out if there is
    3665                 :   // nothing to do.
    3666               0 :   if (mRunningExpiredTimeouts || !mTimerRunning) {
    3667               0 :     return true;
    3668                 :   }
    3669                 : 
    3670               0 :   NS_ASSERTION(mTimer, "Must have a timer!");
    3671               0 :   NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some work to do!");
    3672                 : 
    3673               0 :   bool retval = true;
    3674                 : 
    3675               0 :   AutoPtrComparator<TimeoutInfo> comparator = GetAutoPtrComparator(mTimeouts);
    3676               0 :   JSObject* global = JS_GetGlobalObject(aCx);
    3677               0 :   JSPrincipals* principal = GetWorkerPrincipal();
    3678                 : 
    3679                 :   // We want to make sure to run *something*, even if the timer fired a little
    3680                 :   // early. Fudge the value of now to at least include the first timeout.
    3681               0 :   const TimeStamp now = NS_MAX(TimeStamp::Now(), mTimeouts[0]->mTargetTime);
    3682                 : 
    3683               0 :   nsAutoTArray<TimeoutInfo*, 10> expiredTimeouts;
    3684               0 :   for (PRUint32 index = 0; index < mTimeouts.Length(); index++) {
    3685               0 :     nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
    3686               0 :     if (info->mTargetTime > now) {
    3687               0 :       break;
    3688                 :     }
    3689               0 :     expiredTimeouts.AppendElement(info);
    3690                 :   }
    3691                 : 
    3692                 :   // Guard against recursion.
    3693               0 :   mRunningExpiredTimeouts = true;
    3694                 : 
    3695                 :   // Run expired timeouts.
    3696               0 :   for (PRUint32 index = 0; index < expiredTimeouts.Length(); index++) {
    3697               0 :     TimeoutInfo*& info = expiredTimeouts[index];
    3698                 : 
    3699               0 :     if (info->mCanceled) {
    3700               0 :       continue;
    3701                 :     }
    3702                 : 
    3703                 :     // Always call JS_ReportPendingException if something fails, and if
    3704                 :     // JS_ReportPendingException returns false (i.e. uncatchable exception) then
    3705                 :     // break out of the loop.
    3706                 : 
    3707               0 :     if (JSVAL_IS_STRING(info->mTimeoutVal)) {
    3708               0 :       JSString* expression = JSVAL_TO_STRING(info->mTimeoutVal);
    3709                 : 
    3710                 :       size_t stringLength;
    3711                 :       const jschar* string = JS_GetStringCharsAndLength(aCx, expression,
    3712               0 :                                                         &stringLength);
    3713                 : 
    3714               0 :       if ((!string ||
    3715                 :            !JS_EvaluateUCScriptForPrincipals(aCx, global, principal, string,
    3716                 :                                              stringLength,
    3717                 :                                              info->mFilename.get(),
    3718               0 :                                              info->mLineNumber, nsnull)) &&
    3719               0 :           !JS_ReportPendingException(aCx)) {
    3720               0 :         retval = false;
    3721               0 :         break;
    3722                 :       }
    3723                 :     }
    3724                 :     else {
    3725                 :       jsval rval;
    3726               0 :       if (!JS_CallFunctionValue(aCx, global, info->mTimeoutVal,
    3727                 :                                 info->mExtraArgVals.Length(),
    3728               0 :                                 info->mExtraArgVals.Elements(), &rval) &&
    3729               0 :           !JS_ReportPendingException(aCx)) {
    3730               0 :         retval = false;
    3731               0 :         break;
    3732                 :       }
    3733                 :     }
    3734                 : 
    3735               0 :     NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
    3736                 : 
    3737                 :     // Reschedule intervals.
    3738               0 :     if (info->mIsInterval && !info->mCanceled) {
    3739               0 :       PRUint32 timeoutIndex = mTimeouts.IndexOf(info);
    3740               0 :       NS_ASSERTION(timeoutIndex != PRUint32(-1),
    3741                 :                    "Should still be in the main list!");
    3742                 : 
    3743                 :       // This is nasty but we have to keep the old nsAutoPtr from deleting the
    3744                 :       // info we're about to re-add.
    3745               0 :       mTimeouts[timeoutIndex].forget();
    3746               0 :       mTimeouts.RemoveElementAt(timeoutIndex);
    3747                 : 
    3748               0 :       NS_ASSERTION(!mTimeouts.Contains(info), "Shouldn't have duplicates!");
    3749                 : 
    3750                 :       // NB: We must ensure that info->mTargetTime > now (where now is the
    3751                 :       // now above, not literally TimeStamp::Now()) or we will remove the
    3752                 :       // interval in the next loop below.
    3753               0 :       info->mTargetTime = NS_MAX(info->mTargetTime + info->mInterval,
    3754               0 :                                  now + TimeDuration::FromMilliseconds(1));
    3755               0 :       mTimeouts.InsertElementSorted(info, comparator);
    3756                 :     }
    3757                 :   }
    3758                 : 
    3759                 :   // No longer possible to be called recursively.
    3760               0 :   mRunningExpiredTimeouts = false;
    3761                 : 
    3762                 :   // Now remove canceled and expired timeouts from the main list.
    3763               0 :   for (PRUint32 index = 0; index < mTimeouts.Length(); ) {
    3764               0 :     nsAutoPtr<TimeoutInfo>& info = mTimeouts[index];
    3765               0 :     if (info->mTargetTime <= now || info->mCanceled) {
    3766               0 :       NS_ASSERTION(!info->mIsInterval || info->mCanceled,
    3767                 :                    "Interval timers can only be removed when canceled!");
    3768               0 :       mTimeouts.RemoveElement(info);
    3769                 :     }
    3770                 :     else {
    3771               0 :       index++;
    3772                 :     }
    3773                 :   }
    3774                 : 
    3775                 :   // Either signal the parent that we're no longer using timeouts or reschedule
    3776                 :   // the timer.
    3777               0 :   if (mTimeouts.IsEmpty()) {
    3778               0 :     if (!ModifyBusyCountFromWorker(aCx, false)) {
    3779               0 :       retval = false;
    3780                 :     }
    3781               0 :     mTimerRunning = false;
    3782                 :   }
    3783               0 :   else if (retval && !RescheduleTimeoutTimer(aCx)) {
    3784               0 :     retval = false;
    3785                 :   }
    3786                 : 
    3787               0 :   return retval;
    3788                 : }
    3789                 : 
    3790                 : bool
    3791               0 : WorkerPrivate::RescheduleTimeoutTimer(JSContext* aCx)
    3792                 : {
    3793               0 :   AssertIsOnWorkerThread();
    3794               0 :   NS_ASSERTION(!mTimeouts.IsEmpty(), "Should have some timeouts!");
    3795               0 :   NS_ASSERTION(mTimer, "Should have a timer!");
    3796                 : 
    3797                 :   double delta =
    3798               0 :     (mTimeouts[0]->mTargetTime - TimeStamp::Now()).ToMilliseconds();
    3799               0 :   PRUint32 delay = delta > 0 ? NS_MIN(delta, double(PR_UINT32_MAX)) : 0;
    3800                 : 
    3801               0 :   nsresult rv = mTimer->InitWithFuncCallback(DummyCallback, nsnull, delay,
    3802               0 :                                              nsITimer::TYPE_ONE_SHOT);
    3803               0 :   if (NS_FAILED(rv)) {
    3804               0 :     JS_ReportError(aCx, "Failed to start timer!");
    3805               0 :     return false;
    3806                 :   }
    3807                 : 
    3808               0 :   return true;
    3809                 : }
    3810                 : 
    3811                 : void
    3812               0 : WorkerPrivate::UpdateJSContextOptionsInternal(JSContext* aCx, PRUint32 aOptions)
    3813                 : {
    3814               0 :   AssertIsOnWorkerThread();
    3815                 : 
    3816               0 :   JS_SetOptions(aCx, aOptions);
    3817                 : 
    3818               0 :   for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
    3819               0 :     mChildWorkers[index]->UpdateJSContextOptions(aCx, aOptions);
    3820                 :   }
    3821               0 : }
    3822                 : 
    3823                 : void
    3824               0 : WorkerPrivate::UpdateJSRuntimeHeapSizeInternal(JSContext* aCx,
    3825                 :                                                PRUint32 aMaxBytes)
    3826                 : {
    3827               0 :   AssertIsOnWorkerThread();
    3828                 : 
    3829               0 :   JS_SetGCParameter(JS_GetRuntime(aCx), JSGC_MAX_BYTES, aMaxBytes);
    3830                 : 
    3831               0 :   for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
    3832               0 :     mChildWorkers[index]->UpdateJSRuntimeHeapSize(aCx, aMaxBytes);
    3833                 :   }
    3834               0 : }
    3835                 : 
    3836                 : #ifdef JS_GC_ZEAL
    3837                 : void
    3838               0 : WorkerPrivate::UpdateGCZealInternal(JSContext* aCx, PRUint8 aGCZeal)
    3839                 : {
    3840               0 :   AssertIsOnWorkerThread();
    3841                 : 
    3842               0 :   PRUint32 frequency = aGCZeal <= 2 ? JS_DEFAULT_ZEAL_FREQ : 1;
    3843               0 :   JS_SetGCZeal(aCx, aGCZeal, frequency, false);
    3844                 : 
    3845               0 :   for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
    3846               0 :     mChildWorkers[index]->UpdateGCZeal(aCx, aGCZeal);
    3847                 :   }
    3848               0 : }
    3849                 : #endif
    3850                 : 
    3851                 : void
    3852               0 : WorkerPrivate::GarbageCollectInternal(JSContext* aCx, bool aShrinking,
    3853                 :                                       bool aCollectChildren)
    3854                 : {
    3855               0 :   AssertIsOnWorkerThread();
    3856                 : 
    3857               0 :   if (aShrinking) {
    3858               0 :     js::ShrinkingGC(aCx, js::gcreason::DOM_WORKER);
    3859                 :   }
    3860                 :   else {
    3861               0 :     js::GCForReason(aCx, js::gcreason::DOM_WORKER);
    3862                 :   }
    3863                 : 
    3864               0 :   if (aCollectChildren) {
    3865               0 :     for (PRUint32 index = 0; index < mChildWorkers.Length(); index++) {
    3866               0 :       mChildWorkers[index]->GarbageCollect(aCx, aShrinking);
    3867                 :     }
    3868                 :   }
    3869               0 : }
    3870                 : 
    3871                 : #ifdef DEBUG
    3872                 : template <class Derived>
    3873                 : void
    3874                 : WorkerPrivateParent<Derived>::AssertIsOnParentThread() const
    3875                 : {
    3876               0 :   if (GetParent()) {
    3877               0 :     GetParent()->AssertIsOnWorkerThread();
    3878                 :   }
    3879                 :   else {
    3880               0 :     AssertIsOnMainThread();
    3881                 :   }
    3882               0 : }
    3883                 : 
    3884                 : template <class Derived>
    3885                 : void
    3886                 : WorkerPrivateParent<Derived>::AssertInnerWindowIsCorrect() const
    3887                 : {
    3888               0 :   AssertIsOnParentThread();
    3889                 : 
    3890                 :   // Only care about top level workers from windows.
    3891               0 :   if (mParent || !mWindow) {
    3892               0 :     return;
    3893                 :   }
    3894                 : 
    3895               0 :   AssertIsOnMainThread();
    3896                 : 
    3897               0 :   nsPIDOMWindow* outer = mWindow->GetOuterWindow();
    3898               0 :   NS_ASSERTION(outer && outer->GetCurrentInnerWindow() == mWindow,
    3899                 :                "Inner window no longer correct!");
    3900                 : }
    3901                 : 
    3902                 : void
    3903               0 : WorkerPrivate::AssertIsOnWorkerThread() const
    3904                 : {
    3905               0 :   if (mThread) {
    3906                 :     bool current;
    3907               0 :     if (NS_FAILED(mThread->IsOnCurrentThread(&current)) || !current) {
    3908               0 :       NS_ERROR("Wrong thread!");
    3909                 :     }
    3910                 :   }
    3911                 :   else {
    3912                 :     NS_ERROR("Trying to assert thread identity after thread has been "
    3913               0 :              "shutdown!");
    3914                 :   }
    3915               0 : }
    3916                 : #endif
    3917                 : 
    3918                 : WorkerCrossThreadDispatcher*
    3919               0 : WorkerPrivate::GetCrossThreadDispatcher()
    3920                 : {
    3921               0 :   mozilla::MutexAutoLock lock(mMutex);
    3922               0 :   if (!mCrossThreadDispatcher && mStatus <= Running) {
    3923               0 :     mCrossThreadDispatcher = new WorkerCrossThreadDispatcher(this);
    3924                 :   }
    3925               0 :   return mCrossThreadDispatcher;
    3926                 : }
    3927                 : 
    3928                 : BEGIN_WORKERS_NAMESPACE
    3929                 : 
    3930                 : // Force instantiation.
    3931                 : template class WorkerPrivateParent<WorkerPrivate>;
    3932                 : 
    3933                 : WorkerPrivate*
    3934               0 : GetWorkerPrivateFromContext(JSContext* aCx)
    3935                 : {
    3936               0 :   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
    3937               0 :   return static_cast<WorkerPrivate*>(JS_GetContextPrivate(aCx));
    3938                 : }
    3939                 : 
    3940                 : JSStructuredCloneCallbacks*
    3941               0 : WorkerStructuredCloneCallbacks(bool aMainRuntime)
    3942                 : {
    3943                 :   return aMainRuntime ?
    3944                 :          &gMainThreadWorkerStructuredCloneCallbacks :
    3945               0 :          &gWorkerStructuredCloneCallbacks;
    3946                 : }
    3947                 : 
    3948                 : JSStructuredCloneCallbacks*
    3949               0 : ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime)
    3950                 : {
    3951                 :   return aMainRuntime ?
    3952                 :          &gMainThreadChromeWorkerStructuredCloneCallbacks :
    3953               0 :          &gChromeWorkerStructuredCloneCallbacks;
    3954                 : }
    3955                 : 
    3956                 : END_WORKERS_NAMESPACE

Generated by: LCOV version 1.7