Move WebCore into Source
[WebKit-https.git] / Source / WebCore / bindings / gobject / DOMObjectCache.cpp
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 }