/* * 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; } }