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

       1                 : /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Web Workers.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  *   The Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2011
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Ben Turner <bent.mozilla@gmail.com> (Original Author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "mozilla/Util.h"
      40                 : 
      41                 : #include "ListenerManager.h"
      42                 : 
      43                 : #include "jsalloc.h"
      44                 : #include "jsapi.h"
      45                 : #include "jsfriendapi.h"
      46                 : #include "js/Vector.h"
      47                 : 
      48                 : #include "Events.h"
      49                 : 
      50                 : using namespace mozilla;
      51                 : using dom::workers::events::ListenerManager;
      52                 : 
      53                 : namespace {
      54                 : 
      55                 : struct Listener;
      56                 : 
      57                 : struct ListenerCollection : PRCList
      58                 : {
      59                 :   static ListenerCollection*
      60               0 :   Add(JSContext* aCx, ListenerCollection* aCollectionHead, jsid aTypeId)
      61                 :   {
      62                 :     ListenerCollection* collection =
      63                 :       static_cast<ListenerCollection*>(JS_malloc(aCx,
      64               0 :                                                  sizeof(ListenerCollection)));
      65               0 :     if (!collection) {
      66               0 :       return NULL;
      67                 :     }
      68                 : 
      69               0 :     PR_APPEND_LINK(collection, aCollectionHead);
      70                 : 
      71               0 :     collection->mTypeId = aTypeId;
      72               0 :     PR_INIT_CLIST(&collection->mListenerHead);
      73               0 :     return collection;
      74                 :   }
      75                 : 
      76                 :   static void
      77               0 :   Remove(JSContext* aCx, ListenerCollection* aCollection)
      78                 :   {
      79               0 :     PR_REMOVE_LINK(aCollection);
      80               0 :     JS_free(aCx, aCollection);
      81               0 :   }
      82                 : 
      83                 :   jsid mTypeId;
      84                 :   PRCList mListenerHead;
      85                 : };
      86                 : 
      87                 : struct Listener : PRCList
      88                 : {
      89                 :   static Listener*
      90               0 :   Add(JSContext* aCx, Listener* aListenerHead, jsval aListenerVal,
      91                 :       ListenerManager::Phase aPhase, bool aWantsUntrusted)
      92                 :   {
      93                 :     Listener* listener =
      94               0 :       static_cast<Listener*>(JS_malloc(aCx, sizeof(Listener)));
      95               0 :     if (!listener) {
      96               0 :       return NULL;
      97                 :     }
      98                 : 
      99               0 :     PR_APPEND_LINK(listener, aListenerHead);
     100                 : 
     101               0 :     listener->mListenerVal = aListenerVal;
     102               0 :     listener->mPhase = aPhase;
     103               0 :     listener->mWantsUntrusted = aWantsUntrusted;
     104               0 :     return listener;
     105                 :   }
     106                 : 
     107                 :   static void
     108               0 :   Remove(JSContext* aCx, Listener* aListener)
     109                 :   {
     110               0 :     if (js::IsIncrementalBarrierNeeded(aCx))
     111               0 :       js::IncrementalValueBarrier(aListener->mListenerVal);
     112                 : 
     113               0 :     PR_REMOVE_LINK(aListener);
     114               0 :     JS_free(aCx, aListener);
     115               0 :   }
     116                 : 
     117                 :   jsval mListenerVal;
     118                 :   ListenerManager::Phase mPhase;
     119                 :   bool mWantsUntrusted;
     120                 : };
     121                 : 
     122                 : void
     123               0 : DestroyList(JSContext* aCx, PRCList* aListHead)
     124                 : {
     125               0 :   for (PRCList* elem = PR_NEXT_LINK(aListHead); elem != aListHead; ) {
     126               0 :     PRCList* nextElem = PR_NEXT_LINK(elem);
     127               0 :     JS_free(aCx, elem);
     128               0 :     elem = nextElem;
     129                 :   }
     130               0 : }
     131                 : 
     132                 : ListenerCollection*
     133               0 : GetCollectionForType(PRCList* aHead, jsid aTypeId)
     134                 : {
     135               0 :   for (PRCList* elem = PR_NEXT_LINK(aHead);
     136                 :        elem != aHead;
     137                 :        elem = PR_NEXT_LINK(elem)) {
     138               0 :     ListenerCollection* collection = static_cast<ListenerCollection*>(elem);
     139               0 :     if (collection->mTypeId == aTypeId) {
     140               0 :       return collection;
     141                 :     }
     142                 :   }
     143               0 :   return NULL;
     144                 : }
     145                 : 
     146                 : } // anonymous namespace
     147                 : 
     148                 : #ifdef DEBUG
     149               0 : ListenerManager::~ListenerManager()
     150                 : {
     151               0 :   JS_ASSERT(PR_CLIST_IS_EMPTY(&mCollectionHead));
     152               0 : }
     153                 : #endif
     154                 : 
     155                 : void
     156               0 : ListenerManager::TraceInternal(JSTracer* aTrc)
     157                 : {
     158               0 :   JS_ASSERT(!PR_CLIST_IS_EMPTY(&mCollectionHead));
     159                 : 
     160               0 :   for (PRCList* collectionElem = PR_NEXT_LINK(&mCollectionHead);
     161                 :        collectionElem != &mCollectionHead;
     162                 :        collectionElem = PR_NEXT_LINK(collectionElem)) {
     163                 :     ListenerCollection* collection =
     164               0 :       static_cast<ListenerCollection*>(collectionElem);
     165                 : 
     166               0 :     for (PRCList* listenerElem = PR_NEXT_LINK(&collection->mListenerHead);
     167                 :          listenerElem != &collection->mListenerHead;
     168                 :          listenerElem = PR_NEXT_LINK(listenerElem)) {
     169               0 :       JS_CALL_VALUE_TRACER(aTrc,
     170                 :                            static_cast<Listener*>(listenerElem)->mListenerVal,
     171                 :                            "EventListenerManager listener value");
     172                 :     }
     173                 :   }
     174               0 : }
     175                 : 
     176                 : void
     177               0 : ListenerManager::FinalizeInternal(JSContext* aCx)
     178                 : {
     179               0 :   JS_ASSERT(!PR_CLIST_IS_EMPTY(&mCollectionHead));
     180                 : 
     181               0 :   for (PRCList* elem = PR_NEXT_LINK(&mCollectionHead);
     182                 :        elem != &mCollectionHead;
     183                 :        elem = PR_NEXT_LINK(elem)) {
     184               0 :     DestroyList(aCx, &static_cast<ListenerCollection*>(elem)->mListenerHead);
     185                 :   }
     186                 : 
     187               0 :   DestroyList(aCx, &mCollectionHead);
     188                 : 
     189                 : #ifdef DEBUG
     190               0 :   PR_INIT_CLIST(&mCollectionHead);
     191                 : #endif
     192               0 : }
     193                 : 
     194                 : bool
     195               0 : ListenerManager::Add(JSContext* aCx, JSString* aType, jsval aListenerVal,
     196                 :                      Phase aPhase, bool aWantsUntrusted)
     197                 : {
     198               0 :   aType = JS_InternJSString(aCx, aType);
     199               0 :   if (!aType) {
     200               0 :     return false;
     201                 :   }
     202                 : 
     203               0 :   if (JSVAL_IS_PRIMITIVE(aListenerVal)) {
     204               0 :     JS_ReportError(aCx, "Bad listener!");
     205               0 :     return false;
     206                 :   }
     207                 : 
     208               0 :   jsid typeId = INTERNED_STRING_TO_JSID(aCx, aType);
     209                 : 
     210                 :   ListenerCollection* collection =
     211               0 :     GetCollectionForType(&mCollectionHead, typeId);
     212               0 :   if (!collection) {
     213                 :     ListenerCollection* head =
     214               0 :       static_cast<ListenerCollection*>(&mCollectionHead);
     215               0 :     collection = ListenerCollection::Add(aCx, head, typeId);
     216               0 :     if (!collection) {
     217               0 :       return false;
     218                 :     }
     219                 :   }
     220                 : 
     221               0 :   for (PRCList* elem = PR_NEXT_LINK(&collection->mListenerHead);
     222                 :        elem != &collection->mListenerHead;
     223                 :        elem = PR_NEXT_LINK(elem)) {
     224               0 :     Listener* listener = static_cast<Listener*>(elem);
     225               0 :     if (listener->mListenerVal == aListenerVal && listener->mPhase == aPhase) {
     226               0 :       return true;
     227                 :     }
     228                 :   }
     229                 : 
     230                 :   Listener* listener =
     231                 :     Listener::Add(aCx, static_cast<Listener*>(&collection->mListenerHead),
     232               0 :                   aListenerVal, aPhase, aWantsUntrusted);
     233               0 :   if (!listener) {
     234               0 :     return false;
     235                 :   }
     236                 : 
     237               0 :   return true;
     238                 : }
     239                 : 
     240                 : bool
     241               0 : ListenerManager::Remove(JSContext* aCx, JSString* aType, jsval aListenerVal,
     242                 :                         Phase aPhase, bool aClearEmpty)
     243                 : {
     244               0 :   aType = JS_InternJSString(aCx, aType);
     245               0 :   if (!aType) {
     246               0 :     return false;
     247                 :   }
     248                 : 
     249                 :   ListenerCollection* collection =
     250               0 :     GetCollectionForType(&mCollectionHead, INTERNED_STRING_TO_JSID(aCx, aType));
     251               0 :   if (!collection) {
     252               0 :     return true;
     253                 :   }
     254                 : 
     255               0 :   for (PRCList* elem = PR_NEXT_LINK(&collection->mListenerHead);
     256                 :        elem != &collection->mListenerHead;
     257                 :        elem = PR_NEXT_LINK(elem)) {
     258               0 :     Listener* listener = static_cast<Listener*>(elem);
     259               0 :     if (listener->mListenerVal == aListenerVal && listener->mPhase == aPhase) {
     260               0 :       Listener::Remove(aCx, listener);
     261               0 :       if (aClearEmpty && PR_CLIST_IS_EMPTY(&collection->mListenerHead)) {
     262               0 :         ListenerCollection::Remove(aCx, collection);
     263                 :       }
     264               0 :       break;
     265                 :     }
     266                 :   }
     267                 : 
     268               0 :   return true;
     269                 : }
     270                 : 
     271                 : bool
     272               0 : ListenerManager::SetEventListener(JSContext* aCx, JSString* aType,
     273                 :                                   jsval aListener)
     274                 : {
     275                 :   jsval existing;
     276               0 :   if (!GetEventListener(aCx, aType, &existing)) {
     277               0 :     return false;
     278                 :   }
     279                 : 
     280               0 :   if (!JSVAL_IS_VOID(existing) &&
     281               0 :       !Remove(aCx, aType, existing, Onfoo, false)) {
     282               0 :     return false;
     283                 :   }
     284                 : 
     285               0 :   return JSVAL_IS_VOID(aListener) || JSVAL_IS_NULL(aListener) ?
     286                 :          true :
     287               0 :          Add(aCx, aType, aListener, Onfoo, false);
     288                 : }
     289                 : 
     290                 : bool
     291               0 : ListenerManager::GetEventListener(JSContext* aCx, JSString* aType,
     292                 :                                   jsval* aListenerVal)
     293                 : {
     294               0 :   if (PR_CLIST_IS_EMPTY(&mCollectionHead)) {
     295               0 :     *aListenerVal = JSVAL_VOID;
     296               0 :     return true;
     297                 :   }
     298                 : 
     299               0 :   aType = JS_InternJSString(aCx, aType);
     300               0 :   if (!aType) {
     301               0 :     return false;
     302                 :   }
     303                 : 
     304               0 :   jsid typeId = INTERNED_STRING_TO_JSID(aCx, aType);
     305                 : 
     306                 :   ListenerCollection* collection =
     307               0 :     GetCollectionForType(&mCollectionHead, typeId);
     308               0 :   if (collection) {
     309               0 :     for (PRCList* elem = PR_PREV_LINK(&collection->mListenerHead);
     310                 :          elem != &collection->mListenerHead;
     311                 :          elem = PR_NEXT_LINK(elem)) {
     312               0 :       Listener* listener = static_cast<Listener*>(elem);
     313               0 :       if (listener->mPhase == Onfoo) {
     314               0 :         *aListenerVal = listener->mListenerVal;
     315               0 :         return true;
     316                 :       }
     317                 :     }
     318                 :   }
     319               0 :   *aListenerVal = JSVAL_VOID;
     320               0 :   return true;
     321                 : }
     322                 : 
     323                 : class ExternallyUsableContextAllocPolicy
     324                 : {
     325                 :     JSContext *const cx;
     326                 : 
     327                 :   public:
     328               0 :     ExternallyUsableContextAllocPolicy(JSContext *cx) : cx(cx) {}
     329                 :     JSContext *context() const { return cx; }
     330               0 :     void *malloc_(size_t bytes) {
     331               0 :         JSAutoRequest ar(cx);
     332               0 :         return JS_malloc(cx, bytes);
     333                 :     }
     334                 :     void *realloc_(void *p, size_t oldBytes, size_t bytes) {
     335                 :         JSAutoRequest ar(cx);
     336                 :         return JS_realloc(cx, p, bytes);
     337                 :     }
     338               0 :     void free_(void *p) { JS_free(cx, p); }
     339               0 :     void reportAllocOverflow() const { JS_ReportAllocationOverflow(cx); }
     340                 : };
     341                 : 
     342                 : bool
     343               0 : ListenerManager::DispatchEvent(JSContext* aCx, JSObject* aTarget,
     344                 :                                JSObject* aEvent, bool* aPreventDefaultCalled)
     345                 : {
     346               0 :   if (!events::IsSupportedEventClass(aEvent)) {
     347                 :     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
     348                 :                          JSMSG_INCOMPATIBLE_METHOD,
     349               0 :                          "EventTarget", "dispatchEvent", "Event object");
     350               0 :     return false;
     351                 :   }
     352                 : 
     353                 :   jsval val;
     354               0 :   if (!JS_GetProperty(aCx, aEvent, "target", &val)) {
     355               0 :     return false;
     356                 :   }
     357                 : 
     358               0 :   if (!JSVAL_IS_NULL(val)) {
     359                 :     // Already has a target, must be recursively dispatched. Throw.
     360               0 :     JS_ReportError(aCx, "Cannot recursively dispatch the same event!");
     361               0 :     return false;
     362                 :   }
     363                 : 
     364               0 :   if (PR_CLIST_IS_EMPTY(&mCollectionHead)) {
     365               0 :     *aPreventDefaultCalled = false;
     366               0 :     return true;
     367                 :   }
     368                 : 
     369                 :   JSString* eventType;
     370                 :   JSBool eventIsTrusted;
     371                 : 
     372               0 :   if (!JS_GetProperty(aCx, aEvent, "type", &val) ||
     373                 :       !(eventType = JS_ValueToString(aCx, val)) ||
     374                 :       !(eventType = JS_InternJSString(aCx, eventType))) {
     375               0 :     return false;
     376                 :   }
     377                 : 
     378                 :   // We have already ensure that the event is one of our types of events so
     379                 :   // there is no need to worry about this property being faked.
     380               0 :   if (!JS_GetProperty(aCx, aEvent, "isTrusted", &val) ||
     381               0 :       !JS_ValueToBoolean(aCx, val, &eventIsTrusted)) {
     382               0 :     return false;
     383                 :   }
     384                 : 
     385                 :   ListenerCollection* collection =
     386                 :     GetCollectionForType(&mCollectionHead,
     387               0 :                          INTERNED_STRING_TO_JSID(aCx, eventType));
     388               0 :   if (!collection) {
     389               0 :     *aPreventDefaultCalled = false;
     390               0 :     return true;
     391                 :   }
     392                 : 
     393               0 :   ExternallyUsableContextAllocPolicy ap(aCx);
     394               0 :   js::Vector<jsval, 10, ExternallyUsableContextAllocPolicy> listeners(ap);
     395                 : 
     396               0 :   for (PRCList* elem = PR_NEXT_LINK(&collection->mListenerHead);
     397                 :        elem != &collection->mListenerHead;
     398                 :        elem = PR_NEXT_LINK(elem)) {
     399               0 :     Listener* listener = static_cast<Listener*>(elem);
     400                 : 
     401                 :     // Listeners that don't want untrusted events will be skipped if this is an
     402                 :     // untrusted event.
     403               0 :     if ((eventIsTrusted || listener->mWantsUntrusted) &&
     404               0 :         !listeners.append(listener->mListenerVal)) {
     405               0 :       return false;
     406                 :     }
     407                 :   }
     408                 : 
     409               0 :   if (listeners.empty()) {
     410               0 :     return true;
     411                 :   }
     412                 : 
     413               0 :   events::SetEventTarget(aEvent, aTarget);
     414                 : 
     415               0 :   for (size_t index = 0; index < listeners.length(); index++) {
     416               0 :     if (events::EventImmediatePropagationStopped(aEvent)) {
     417               0 :       break;
     418                 :     }
     419                 : 
     420                 :     // If anything fails in here we want to report the exception and continue on
     421                 :     // to the next listener rather than bailing out. If something fails and
     422                 :     // does not set an exception then we bail out entirely as we've either run
     423                 :     // out of memory or the operation callback has indicated that we should
     424                 :     // stop running.
     425                 : 
     426               0 :     jsval listenerVal = listeners[index];
     427                 : 
     428                 :     JSObject* listenerObj;
     429               0 :     if (!JS_ValueToObject(aCx, listenerVal, &listenerObj)) {
     430               0 :       if (!JS_ReportPendingException(aCx)) {
     431               0 :         return false;
     432                 :       }
     433               0 :       continue;
     434                 :     }
     435                 : 
     436                 :     static const char sHandleEventChars[] = "handleEvent";
     437                 : 
     438               0 :     JSObject* thisObj = aTarget;
     439                 : 
     440                 :     JSBool hasHandleEvent;
     441               0 :     if (!JS_HasProperty(aCx, listenerObj, sHandleEventChars, &hasHandleEvent)) {
     442               0 :       if (!JS_ReportPendingException(aCx)) {
     443               0 :         return false;
     444                 :       }
     445               0 :       continue;
     446                 :     }
     447                 : 
     448               0 :     if (hasHandleEvent) {
     449               0 :       if (!JS_GetProperty(aCx, listenerObj, sHandleEventChars, &listenerVal)) {
     450               0 :         if (!JS_ReportPendingException(aCx)) {
     451               0 :           return false;
     452                 :         }
     453               0 :         continue;
     454                 :       }
     455                 : 
     456               0 :       thisObj = listenerObj;
     457                 :     }
     458                 : 
     459               0 :     jsval argv[] = { OBJECT_TO_JSVAL(aEvent) };
     460               0 :     jsval rval = JSVAL_VOID;
     461               0 :     if (!JS_CallFunctionValue(aCx, thisObj, listenerVal, ArrayLength(argv),
     462               0 :                               argv, &rval)) {
     463               0 :       if (!JS_ReportPendingException(aCx)) {
     464               0 :         return false;
     465                 :       }
     466               0 :       continue;
     467                 :     }
     468                 :   }
     469                 : 
     470               0 :   events::SetEventTarget(aEvent, NULL);
     471                 : 
     472               0 :   *aPreventDefaultCalled = events::EventWasCanceled(aEvent);
     473               0 :   return true;
     474                 : }
     475                 : 
     476                 : bool
     477               0 : ListenerManager::HasListenersForTypeInternal(JSContext* aCx, JSString* aType)
     478                 : {
     479               0 :   JS_ASSERT(!PR_CLIST_IS_EMPTY(&mCollectionHead));
     480                 : 
     481               0 :   aType = JS_InternJSString(aCx, aType);
     482               0 :   if (!aType) {
     483               0 :     return false;
     484                 :   }
     485                 : 
     486               0 :   jsid typeId = INTERNED_STRING_TO_JSID(aCx, aType);
     487               0 :   return !!GetCollectionForType(&mCollectionHead, typeId);
     488                 : }

Generated by: LCOV version 1.7