Home | History | Annotate | Download | only in gobject
      1 /*
      2  *  Copyright (C) 2010 Igalia S.L.
      3  *
      4  *  This library is free software; you can redistribute it and/or
      5  *  modify it under the terms of the GNU Lesser General Public
      6  *  License as published by the Free Software Foundation; either
      7  *  version 2 of the License, or (at your option) any later version.
      8  *
      9  *  This library is distributed in the hope that it will be useful,
     10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  *  Lesser General Public License for more details.
     13  *
     14  *  You should have received a copy of the GNU Lesser General Public
     15  *  License along with this library; if not, write to the Free Software
     16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     17  */
     18 
     19 #include "config.h"
     20 #include "DOMObjectCache.h"
     21 
     22 #include "Document.h"
     23 #include "Node.h"
     24 #include "glib-object.h"
     25 #include <wtf/HashMap.h>
     26 
     27 namespace WebKit {
     28 
     29 typedef struct {
     30     GObject* object;
     31     WebCore::Frame* frame;
     32     guint timesReturned;
     33 } DOMObjectCacheData;
     34 
     35 typedef HashMap<void*, DOMObjectCacheData*> DOMObjectMap;
     36 
     37 static DOMObjectMap& domObjects()
     38 {
     39     static DOMObjectMap staticDOMObjects;
     40     return staticDOMObjects;
     41 }
     42 
     43 static WebCore::Frame* getFrameFromHandle(void* objectHandle)
     44 {
     45     WebCore::Node* node = static_cast<WebCore::Node*>(objectHandle);
     46     if (!node->inDocument())
     47         return 0;
     48     WebCore::Document* document = node->document();
     49     if (!document)
     50         return 0;
     51     return document->frame();
     52 }
     53 
     54 void DOMObjectCache::forget(void* objectHandle)
     55 {
     56     DOMObjectCacheData* cacheData = domObjects().get(objectHandle);
     57     ASSERT(cacheData);
     58     g_slice_free(DOMObjectCacheData, cacheData);
     59     domObjects().take(objectHandle);
     60 }
     61 
     62 static void weakRefNotify(gpointer data, GObject* zombie)
     63 {
     64     gboolean* objectDead = static_cast<gboolean*>(data);
     65     *objectDead = TRUE;
     66 }
     67 
     68 void DOMObjectCache::clearByFrame(WebCore::Frame* frame)
     69 {
     70     Vector<DOMObjectCacheData*> toUnref;
     71 
     72     // Unreffing the objects removes them from the cache in their
     73     // finalize method, so just save them to do that while we are not
     74     // iterating the cache itself.
     75     DOMObjectMap::iterator end = domObjects().end();
     76     for (DOMObjectMap::iterator iter = domObjects().begin(); iter != end; ++iter) {
     77         DOMObjectCacheData* data = iter->second;
     78         ASSERT(data);
     79         if ((!frame || data->frame == frame) && data->timesReturned)
     80             toUnref.append(data);
     81     }
     82 
     83     Vector<DOMObjectCacheData*>::iterator last = toUnref.end();
     84     for (Vector<DOMObjectCacheData*>::iterator it = toUnref.begin(); it != last; ++it) {
     85         DOMObjectCacheData* data = *it;
     86         // We can't really know what the user has done with the DOM
     87         // objects, so in case any of the external references to them
     88         // were unreffed (but not all, otherwise the object would be
     89         // dead and out of the cache) we'll add a weak ref before we
     90         // start to get rid of the cache's own references; if the
     91         // object dies in the middle of the process, we'll just stop.
     92         gboolean objectDead = FALSE;
     93         g_object_weak_ref(data->object, weakRefNotify, &objectDead);
     94         // We need to check objectDead first, otherwise the cache data
     95         // might be garbage already.
     96         while (!objectDead && data->timesReturned > 0) {
     97             // If this is the last unref we are going to do,
     98             // disconnect the weak ref. We cannot do it afterwards
     99             // because the object might be dead at that point.
    100             if (data->timesReturned == 1)
    101                 g_object_weak_unref(data->object, weakRefNotify, &objectDead);
    102             data->timesReturned--;
    103             g_object_unref(data->object);
    104         }
    105     }
    106 }
    107 
    108 DOMObjectCache::~DOMObjectCache()
    109 {
    110     clearByFrame();
    111 }
    112 
    113 void* DOMObjectCache::get(void* objectHandle)
    114 {
    115     DOMObjectCacheData* data = domObjects().get(objectHandle);
    116     if (!data)
    117         return 0;
    118 
    119     // We want to add one ref each time a wrapper is returned, so that
    120     // the user can manually unref them if he chooses to.
    121     ASSERT(data->object);
    122     data->timesReturned++;
    123     return g_object_ref(data->object);
    124 }
    125 
    126 void* DOMObjectCache::put(void* objectHandle, void* wrapper)
    127 {
    128     if (domObjects().get(objectHandle))
    129         return wrapper;
    130 
    131     DOMObjectCacheData* data = g_slice_new(DOMObjectCacheData);
    132     data->object = static_cast<GObject*>(wrapper);
    133     data->frame = 0;
    134     data->timesReturned = 1;
    135 
    136     domObjects().set(objectHandle, data);
    137     return wrapper;
    138 }
    139 
    140 void* DOMObjectCache::put(WebCore::Node* objectHandle, void* wrapper)
    141 {
    142     // call the ::put version that takes void* to do the basic cache
    143     // insertion work
    144     put(static_cast<void*>(objectHandle), wrapper);
    145 
    146     DOMObjectCacheData* data = domObjects().get(objectHandle);
    147     ASSERT(data);
    148 
    149     data->frame = getFrameFromHandle(objectHandle);
    150 
    151     return wrapper;
    152 }
    153 
    154 }
    155