LCOV - app.info - dom/workers/ListenerManager.cpp

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