| /* |
| * Copyright (C) 2010 Igalia S.L. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include "config.h" |
| #include "DOMObjectCache.h" |
| |
| #include "Document.h" |
| #include "Node.h" |
| #include "glib-object.h" |
| #include <wtf/HashMap.h> |
| |
| namespace WebKit { |
| |
| typedef struct { |
| GObject* object; |
| WebCore::Frame* frame; |
| guint timesReturned; |
| } DOMObjectCacheData; |
| |
| typedef HashMap<void*, DOMObjectCacheData*> DOMObjectMap; |
| |
| static DOMObjectMap& domObjects() |
| { |
| static DOMObjectMap staticDOMObjects; |
| return staticDOMObjects; |
| } |
| |
| static WebCore::Frame* getFrameFromHandle(void* objectHandle) |
| { |
| WebCore::Node* node = static_cast<WebCore::Node*>(objectHandle); |
| if (!node->inDocument()) |
| return 0; |
| WebCore::Document* document = node->document(); |
| if (!document) |
| return 0; |
| return document->frame(); |
| } |
| |
| void DOMObjectCache::forget(void* objectHandle) |
| { |
| DOMObjectCacheData* cacheData = domObjects().get(objectHandle); |
| ASSERT(cacheData); |
| g_slice_free(DOMObjectCacheData, cacheData); |
| domObjects().take(objectHandle); |
| } |
| |
| static void weakRefNotify(gpointer data, GObject* zombie) |
| { |
| gboolean* objectDead = static_cast<gboolean*>(data); |
| *objectDead = TRUE; |
| } |
| |
| void DOMObjectCache::clearByFrame(WebCore::Frame* frame) |
| { |
| Vector<DOMObjectCacheData*> toUnref; |
| |
| // Unreffing the objects removes them from the cache in their |
| // finalize method, so just save them to do that while we are not |
| // iterating the cache itself. |
| DOMObjectMap::iterator end = domObjects().end(); |
| for (DOMObjectMap::iterator iter = domObjects().begin(); iter != end; ++iter) { |
| DOMObjectCacheData* data = iter->second; |
| ASSERT(data); |
| if ((!frame || data->frame == frame) && data->timesReturned) |
| toUnref.append(data); |
| } |
| |
| Vector<DOMObjectCacheData*>::iterator last = toUnref.end(); |
| for (Vector<DOMObjectCacheData*>::iterator it = toUnref.begin(); it != last; ++it) { |
| DOMObjectCacheData* data = *it; |
| // We can't really know what the user has done with the DOM |
| // objects, so in case any of the external references to them |
| // were unreffed (but not all, otherwise the object would be |
| // dead and out of the cache) we'll add a weak ref before we |
| // start to get rid of the cache's own references; if the |
| // object dies in the middle of the process, we'll just stop. |
| gboolean objectDead = FALSE; |
| g_object_weak_ref(data->object, weakRefNotify, &objectDead); |
| // We need to check objectDead first, otherwise the cache data |
| // might be garbage already. |
| while (!objectDead && data->timesReturned > 0) { |
| // If this is the last unref we are going to do, |
| // disconnect the weak ref. We cannot do it afterwards |
| // because the object might be dead at that point. |
| if (data->timesReturned == 1) |
| g_object_weak_unref(data->object, weakRefNotify, &objectDead); |
| data->timesReturned--; |
| g_object_unref(data->object); |
| } |
| } |
| } |
| |
| DOMObjectCache::~DOMObjectCache() |
| { |
| clearByFrame(); |
| } |
| |
| void* DOMObjectCache::get(void* objectHandle) |
| { |
| DOMObjectCacheData* data = domObjects().get(objectHandle); |
| if (!data) |
| return 0; |
| |
| // We want to add one ref each time a wrapper is returned, so that |
| // the user can manually unref them if he chooses to. |
| ASSERT(data->object); |
| data->timesReturned++; |
| return g_object_ref(data->object); |
| } |
| |
| void* DOMObjectCache::put(void* objectHandle, void* wrapper) |
| { |
| if (domObjects().get(objectHandle)) |
| return wrapper; |
| |
| DOMObjectCacheData* data = g_slice_new(DOMObjectCacheData); |
| data->object = static_cast<GObject*>(wrapper); |
| data->frame = 0; |
| data->timesReturned = 1; |
| |
| domObjects().set(objectHandle, data); |
| return wrapper; |
| } |
| |
| void* DOMObjectCache::put(WebCore::Node* objectHandle, void* wrapper) |
| { |
| // call the ::put version that takes void* to do the basic cache |
| // insertion work |
| put(static_cast<void*>(objectHandle), wrapper); |
| |
| DOMObjectCacheData* data = domObjects().get(objectHandle); |
| ASSERT(data); |
| |
| data->frame = getFrameFromHandle(objectHandle); |
| |
| return wrapper; |
| } |
| |
| } |