LCOV - code coverage report
Current view: directory - dom/workers - Worker.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 165 38 23.0 %
Date: 2012-06-02 Functions: 26 10 38.5 %

       1                 : /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Web Workers.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  *   The Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "Worker.h"
      40                 : 
      41                 : #include "jsapi.h"
      42                 : #include "jsfriendapi.h"
      43                 : 
      44                 : #include "EventTarget.h"
      45                 : #include "RuntimeService.h"
      46                 : #include "WorkerPrivate.h"
      47                 : 
      48                 : #include "WorkerInlines.h"
      49                 : 
      50                 : #define PROPERTY_FLAGS \
      51                 :   (JSPROP_ENUMERATE | JSPROP_SHARED)
      52                 : 
      53                 : #define FUNCTION_FLAGS \
      54                 :   JSPROP_ENUMERATE
      55                 : 
      56                 : USING_WORKERS_NAMESPACE
      57                 : 
      58                 : namespace {
      59                 : 
      60                 : class Worker
      61                 : {
      62                 :   static JSClass sClass;
      63                 :   static JSPropertySpec sProperties[];
      64                 :   static JSFunctionSpec sFunctions[];
      65                 : 
      66                 :   enum
      67                 :   {
      68                 :     STRING_onerror = 0,
      69                 :     STRING_onmessage,
      70                 : 
      71                 :     STRING_COUNT
      72                 :   };
      73                 : 
      74                 :   static const char* const sEventStrings[STRING_COUNT];
      75                 : 
      76                 : protected:
      77                 :   enum {
      78                 :     // The constructor function holds a WorkerPrivate* in its first reserved
      79                 :     // slot.
      80                 :     CONSTRUCTOR_SLOT_PARENT = 0
      81                 :   };
      82                 : 
      83                 : public:
      84                 :   static JSClass*
      85               0 :   Class()
      86                 :   {
      87               0 :     return &sClass;
      88                 :   }
      89                 : 
      90                 :   static JSObject*
      91             147 :   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
      92                 :             bool aMainRuntime)
      93                 :   {
      94                 :     JSObject* proto = js::InitClassWithReserved(aCx, aObj, aParentProto, &sClass, Construct,
      95             147 :                                                 0, sProperties, sFunctions, NULL, NULL);
      96             147 :     if (!proto) {
      97               0 :       return NULL;
      98                 :     }
      99                 : 
     100             147 :     if (!aMainRuntime) {
     101               0 :       WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
     102               0 :       parent->AssertIsOnWorkerThread();
     103                 : 
     104               0 :       JSObject* constructor = JS_GetConstructor(aCx, proto);
     105               0 :       if (!constructor)
     106               0 :         return NULL;
     107                 :       js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
     108               0 :                                     PRIVATE_TO_JSVAL(parent));
     109                 :     }
     110                 : 
     111             147 :     return proto;
     112                 :   }
     113                 : 
     114                 :   static void
     115               0 :   ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
     116                 :   {
     117               0 :     JS_ASSERT(!JS_IsExceptionPending(aCx));
     118                 : 
     119               0 :     WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
     120               0 :     JS_ASSERT(worker);
     121                 : 
     122               0 :     if (aSaveEventHandlers) {
     123               0 :       for (int index = 0; index < STRING_COUNT; index++) {
     124               0 :         const char* name = sEventStrings[index];
     125                 :         jsval listener;
     126               0 :         if (!worker->GetEventListenerOnEventTarget(aCx, name + 2, &listener) ||
     127                 :             !JS_DefineProperty(aCx, aObj, name, listener, NULL, NULL,
     128               0 :                                (PROPERTY_FLAGS & ~JSPROP_SHARED))) {
     129               0 :           JS_ClearPendingException(aCx);
     130                 :         }
     131                 :       }
     132                 :     }
     133                 : 
     134               0 :     SetJSPrivateSafeish(aObj, NULL);
     135               0 :   }
     136                 : 
     137                 :   static WorkerPrivate*
     138                 :   GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
     139                 : 
     140                 : protected:
     141                 :   static JSBool
     142               0 :   ConstructInternal(JSContext* aCx, unsigned aArgc, jsval* aVp,
     143                 :                     bool aIsChromeWorker)
     144                 :   {
     145               0 :     if (!aArgc) {
     146               0 :       JS_ReportError(aCx, "Constructor requires at least one argument!");
     147               0 :       return false;
     148                 :     }
     149                 : 
     150               0 :     JSString* scriptURL = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
     151               0 :     if (!scriptURL) {
     152               0 :       return false;
     153                 :     }
     154                 : 
     155                 :     jsval priv = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(JS_CALLEE(aCx, aVp)),
     156               0 :                                                CONSTRUCTOR_SLOT_PARENT);
     157                 : 
     158                 :     RuntimeService* runtimeService;
     159                 :     WorkerPrivate* parent;
     160                 : 
     161               0 :     if (JSVAL_IS_VOID(priv)) {
     162               0 :       runtimeService = RuntimeService::GetOrCreateService();
     163               0 :       if (!runtimeService) {
     164               0 :         JS_ReportError(aCx, "Failed to create runtime service!");
     165               0 :         return false;
     166                 :       }
     167               0 :       parent = NULL;
     168                 :     }
     169                 :     else {
     170               0 :       runtimeService = RuntimeService::GetService();
     171               0 :       parent = static_cast<WorkerPrivate*>(JSVAL_TO_PRIVATE(priv));
     172               0 :       parent->AssertIsOnWorkerThread();
     173                 :     }
     174                 : 
     175               0 :     JSObject* obj = JS_NewObject(aCx, &sClass, nsnull, nsnull);
     176               0 :     if (!obj) {
     177               0 :       return false;
     178                 :     }
     179                 : 
     180                 :     WorkerPrivate* worker = WorkerPrivate::Create(aCx, obj, parent, scriptURL,
     181               0 :                                                   aIsChromeWorker);
     182               0 :     if (!worker) {
     183               0 :       return false;
     184                 :     }
     185                 : 
     186                 :     // Worker now owned by the JS object.
     187               0 :     SetJSPrivateSafeish(obj, worker);
     188                 : 
     189               0 :     if (!runtimeService->RegisterWorker(aCx, worker)) {
     190               0 :       return false;
     191                 :     }
     192                 : 
     193               0 :     JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
     194               0 :     return true;
     195                 :   }
     196                 : 
     197                 : private:
     198                 :   // No instance of this class should ever be created so these are explicitly
     199                 :   // left without an implementation to prevent linking in case someone tries to
     200                 :   // make one.
     201                 :   Worker();
     202                 :   ~Worker();
     203                 : 
     204                 :   static JSBool
     205               0 :   GetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
     206                 :   {
     207               0 :     JS_ASSERT(JSID_IS_INT(aIdval));
     208               0 :     JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
     209                 : 
     210               0 :     const char* name = sEventStrings[JSID_TO_INT(aIdval)];
     211               0 :     WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
     212               0 :     if (!worker) {
     213               0 :       return !JS_IsExceptionPending(aCx);
     214                 :     }
     215                 : 
     216               0 :     return worker->GetEventListenerOnEventTarget(aCx, name + 2, aVp);
     217                 :   }
     218                 : 
     219                 :   static JSBool
     220               0 :   SetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, JSBool aStrict,
     221                 :                    jsval* aVp)
     222                 :   {
     223               0 :     JS_ASSERT(JSID_IS_INT(aIdval));
     224               0 :     JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
     225                 : 
     226               0 :     const char* name = sEventStrings[JSID_TO_INT(aIdval)];
     227               0 :     WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
     228               0 :     if (!worker) {
     229               0 :       return !JS_IsExceptionPending(aCx);
     230                 :     }
     231                 : 
     232               0 :     return worker->SetEventListenerOnEventTarget(aCx, name + 2, aVp);
     233                 :   }
     234                 : 
     235                 :   static JSBool
     236               0 :   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
     237                 :   {
     238               0 :     return ConstructInternal(aCx, aArgc, aVp, false);
     239                 :   }
     240                 : 
     241                 :   static void
     242             147 :   Finalize(JSContext* aCx, JSObject* aObj)
     243                 :   {
     244             147 :     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     245             147 :     WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
     246             147 :     if (worker) {
     247               0 :       worker->FinalizeInstance(aCx, true);
     248                 :     }
     249             147 :   }
     250                 : 
     251                 :   static void
     252              65 :   Trace(JSTracer* aTrc, JSObject* aObj)
     253                 :   {
     254              65 :     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     255              65 :     WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
     256              65 :     if (worker) {
     257               0 :       worker->TraceInstance(aTrc);
     258                 :     }
     259              65 :   }
     260                 : 
     261                 :   static JSBool
     262               0 :   Terminate(JSContext* aCx, unsigned aArgc, jsval* aVp)
     263                 :   {
     264               0 :     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     265               0 :     if (!obj) {
     266               0 :       return false;
     267                 :     }
     268                 : 
     269               0 :     const char*& name = sFunctions[0].name;
     270               0 :     WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
     271               0 :     if (!worker) {
     272               0 :       return !JS_IsExceptionPending(aCx);
     273                 :     }
     274                 : 
     275               0 :     return worker->Terminate(aCx);
     276                 :   }
     277                 : 
     278                 :   static JSBool
     279               0 :   PostMessage(JSContext* aCx, unsigned aArgc, jsval* aVp)
     280                 :   {
     281               0 :     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     282               0 :     if (!obj) {
     283               0 :       return false;
     284                 :     }
     285                 : 
     286               0 :     const char*& name = sFunctions[1].name;
     287               0 :     WorkerPrivate* worker = GetInstancePrivate(aCx, obj, name);
     288               0 :     if (!worker) {
     289               0 :       return !JS_IsExceptionPending(aCx);
     290                 :     }
     291                 : 
     292                 :     jsval message;
     293               0 :     if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &message)) {
     294               0 :       return false;
     295                 :     }
     296                 : 
     297               0 :     return worker->PostMessage(aCx, message);
     298                 :   }
     299                 : };
     300                 : 
     301                 : JSClass Worker::sClass = {
     302                 :   "Worker",
     303                 :   JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
     304                 :   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     305                 :   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
     306                 :   NULL, NULL, NULL, NULL, Trace,
     307                 : };
     308                 : 
     309                 : JSPropertySpec Worker::sProperties[] = {
     310            1464 :   { sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
     311                 :     GetEventListener, SetEventListener },
     312            1464 :   { sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
     313                 :     GetEventListener, SetEventListener },
     314                 :   { 0, 0, 0, NULL, NULL }
     315            2928 : };
     316                 : 
     317                 : JSFunctionSpec Worker::sFunctions[] = {
     318                 :   JS_FN("terminate", Terminate, 0, FUNCTION_FLAGS),
     319                 :   JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
     320                 :   JS_FS_END
     321                 : };
     322                 : 
     323                 : const char* const Worker::sEventStrings[STRING_COUNT] = {
     324                 :   "onerror",
     325                 :   "onmessage"
     326                 : };
     327                 : 
     328                 : class ChromeWorker : public Worker
     329                 : {
     330                 :   static JSClass sClass;
     331                 : 
     332                 : public:
     333                 :   static JSClass*
     334               0 :   Class()
     335                 :   {
     336               0 :     return &sClass;
     337                 :   }
     338                 : 
     339                 :   static JSObject*
     340             147 :   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
     341                 :             bool aMainRuntime)
     342                 :   {
     343                 :     JSObject* proto = js::InitClassWithReserved(aCx, aObj, aParentProto, &sClass, Construct,
     344             147 :                                                 0, NULL, NULL, NULL, NULL);
     345             147 :     if (!proto) {
     346               0 :       return NULL;
     347                 :     }
     348                 : 
     349             147 :     if (!aMainRuntime) {
     350               0 :       WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
     351               0 :       parent->AssertIsOnWorkerThread();
     352                 : 
     353               0 :       JSObject* constructor = JS_GetConstructor(aCx, proto);
     354               0 :       if (!constructor)
     355               0 :         return NULL;
     356                 :       js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
     357               0 :                                     PRIVATE_TO_JSVAL(parent));
     358                 :     }
     359                 : 
     360             147 :     return proto;
     361                 :   }
     362                 : 
     363                 :   static void
     364               0 :   ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
     365                 :   {
     366               0 :     Worker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
     367               0 :   }
     368                 : 
     369                 : private:
     370                 :   // No instance of this class should ever be created so these are explicitly
     371                 :   // left without an implementation to prevent linking in case someone tries to
     372                 :   // make one.
     373                 :   ChromeWorker();
     374                 :   ~ChromeWorker();
     375                 : 
     376                 :   static WorkerPrivate*
     377                 :   GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
     378                 :   {
     379                 :     if (aObj) {
     380                 :       JSClass* classPtr = JS_GetClass(aObj);
     381                 :       if (classPtr == &sClass) {
     382                 :         return GetJSPrivateSafeish<WorkerPrivate>(aObj);
     383                 :       }
     384                 :     }
     385                 : 
     386                 :     return Worker::GetInstancePrivate(aCx, aObj, aFunctionName);
     387                 :   }
     388                 : 
     389                 :   static JSBool
     390               0 :   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
     391                 :   {
     392               0 :     return ConstructInternal(aCx, aArgc, aVp, true);
     393                 :   }
     394                 : 
     395                 :   static void
     396             147 :   Finalize(JSContext* aCx, JSObject* aObj)
     397                 :   {
     398             147 :     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     399             147 :     WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
     400             147 :     if (worker) {
     401               0 :       worker->FinalizeInstance(aCx, true);
     402                 :     }
     403             147 :   }
     404                 : 
     405                 :   static void
     406              65 :   Trace(JSTracer* aTrc, JSObject* aObj)
     407                 :   {
     408              65 :     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     409              65 :     WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aObj);
     410              65 :     if (worker) {
     411               0 :       worker->TraceInstance(aTrc);
     412                 :     }
     413              65 :   }
     414                 : };
     415                 : 
     416                 : JSClass ChromeWorker::sClass = {
     417                 :   "ChromeWorker",
     418                 :   JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
     419                 :   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     420                 :   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
     421                 :   NULL, NULL, NULL, NULL, Trace
     422                 : };
     423                 : 
     424                 : WorkerPrivate*
     425               0 : Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
     426                 :                            const char* aFunctionName)
     427                 : {
     428               0 :   JSClass* classPtr = JS_GetClass(aObj);
     429               0 :   if (classPtr == &sClass || classPtr == ChromeWorker::Class()) {
     430               0 :     return GetJSPrivateSafeish<WorkerPrivate>(aObj);
     431                 :   }
     432                 : 
     433                 :   JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
     434               0 :                        sClass.name, aFunctionName, classPtr->name);
     435               0 :   return NULL;
     436                 : }
     437                 : 
     438                 : } // anonymous namespace
     439                 : 
     440                 : BEGIN_WORKERS_NAMESPACE
     441                 : 
     442                 : namespace worker {
     443                 : 
     444                 : JSObject*
     445             147 : InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
     446                 :           bool aMainRuntime)
     447                 : {
     448             147 :   return Worker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
     449                 : }
     450                 : 
     451                 : void
     452               0 : ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
     453                 : {
     454               0 :   JSClass* clasp = JS_GetClass(aObj);
     455               0 :   JS_ASSERT(clasp == Worker::Class() || clasp == ChromeWorker::Class());
     456                 : 
     457               0 :   if (clasp == ChromeWorker::Class()) {
     458               0 :     ChromeWorker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
     459                 :   }
     460                 :   else {
     461               0 :     Worker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
     462                 :   }
     463               0 : }
     464                 : 
     465                 : } // namespace worker
     466                 : 
     467                 : WorkerCrossThreadDispatcher*
     468               0 : GetWorkerCrossThreadDispatcher(JSContext* aCx, jsval aWorker)
     469                 : {
     470               0 :   if (JSVAL_IS_PRIMITIVE(aWorker)) {
     471               0 :     return NULL;
     472                 :   }
     473                 : 
     474                 :   WorkerPrivate* w =
     475                 :       Worker::GetInstancePrivate(aCx, JSVAL_TO_OBJECT(aWorker),
     476               0 :                                  "GetWorkerCrossThreadDispatcher");
     477               0 :   if (!w) {
     478               0 :     return NULL;
     479                 :   }
     480               0 :   return w->GetCrossThreadDispatcher();
     481                 : }
     482                 : 
     483                 : 
     484                 : namespace chromeworker {
     485                 : 
     486                 : bool
     487             147 : InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
     488                 :           bool aMainRuntime)
     489                 : {
     490             147 :   return !!ChromeWorker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
     491                 : }
     492                 : 
     493                 : } // namespace chromeworker
     494                 : 
     495                 : bool
     496               0 : ClassIsWorker(JSClass* aClass)
     497                 : {
     498               0 :   return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
     499                 : }
     500                 : 
     501            4392 : END_WORKERS_NAMESPACE

Generated by: LCOV version 1.7