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
domObjects()37 static DOMObjectMap& domObjects()
38 {
39 static DOMObjectMap staticDOMObjects;
40 return staticDOMObjects;
41 }
42
getFrameFromHandle(void * objectHandle)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
forget(void * objectHandle)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
weakRefNotify(gpointer data,GObject * zombie)62 static void weakRefNotify(gpointer data, GObject* zombie)
63 {
64 gboolean* objectDead = static_cast<gboolean*>(data);
65 *objectDead = TRUE;
66 }
67
clearByFrame(WebCore::Frame * frame)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
~DOMObjectCache()108 DOMObjectCache::~DOMObjectCache()
109 {
110 clearByFrame();
111 }
112
get(void * objectHandle)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
put(void * objectHandle,void * wrapper)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
put(WebCore::Node * objectHandle,void * wrapper)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