[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebKit / WebProcess / InjectedBundle / API / glib / DOM / DOMObjectCache.cpp
1 /*
2  *  Copyright (C) 2010, 2015 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 <WebCore/DOMWindow.h>
23 #include <WebCore/Document.h>
24 #include <WebCore/Frame.h>
25 #include <WebCore/FrameDestructionObserver.h>
26 #include <WebCore/Node.h>
27 #include <glib-object.h>
28 #include <wtf/HashMap.h>
29 #include <wtf/NeverDestroyed.h>
30 #include <wtf/RunLoop.h>
31 #include <wtf/Vector.h>
32 #include <wtf/glib/GRefPtr.h>
33
34 namespace WebKit {
35
36 struct DOMObjectCacheData {
37     WTF_MAKE_STRUCT_FAST_ALLOCATED;
38     DOMObjectCacheData(GObject* wrapper)
39         : object(wrapper)
40         , cacheReferences(1)
41     {
42     }
43
44     void clearObject()
45     {
46         ASSERT(object);
47         ASSERT(cacheReferences >= 1);
48         ASSERT(object->ref_count >= 1);
49
50         // Make sure we don't unref more than the references the object actually has. It can happen that user
51         // unreffed a reference owned by the cache.
52         cacheReferences = std::min(static_cast<unsigned>(object->ref_count), cacheReferences);
53         GRefPtr<GObject> protect(object);
54         do {
55             g_object_unref(object);
56         } while (--cacheReferences);
57         object = nullptr;
58     }
59
60     void* refObject()
61     {
62         ASSERT(object);
63
64         cacheReferences++;
65         return g_object_ref(object);
66     }
67
68     GObject* object;
69     unsigned cacheReferences;
70 };
71
72 class DOMObjectCacheFrameObserver;
73 typedef HashMap<WebCore::Frame*, std::unique_ptr<DOMObjectCacheFrameObserver>> DOMObjectCacheFrameObserverMap;
74
75 static DOMObjectCacheFrameObserverMap& domObjectCacheFrameObservers()
76 {
77     static NeverDestroyed<DOMObjectCacheFrameObserverMap> map;
78     return map;
79 }
80
81 class DOMObjectCacheFrameObserver final: public WebCore::FrameDestructionObserver {
82     WTF_MAKE_FAST_ALLOCATED;
83 public:
84     DOMObjectCacheFrameObserver(WebCore::Frame& frame)
85         : FrameDestructionObserver(&frame)
86     {
87     }
88
89     ~DOMObjectCacheFrameObserver()
90     {
91         ASSERT(m_objects.isEmpty());
92     }
93
94     void addObjectCacheData(DOMObjectCacheData& data)
95     {
96         ASSERT(!m_objects.contains(&data));
97
98         WebCore::DOMWindow* domWindow = m_frame->document()->domWindow();
99         if (domWindow && (!m_domWindowObserver || m_domWindowObserver->window() != domWindow)) {
100             // New DOMWindow, clear the cache and create a new DOMWindowObserver.
101             clear();
102             m_domWindowObserver = makeUnique<DOMWindowObserver>(*domWindow, *this);
103         }
104
105         m_objects.append(&data);
106         g_object_weak_ref(data.object, DOMObjectCacheFrameObserver::objectFinalizedCallback, this);
107     }
108
109 private:
110     class DOMWindowObserver final : public WebCore::DOMWindow::Observer {
111         WTF_MAKE_FAST_ALLOCATED;
112     public:
113         DOMWindowObserver(WebCore::DOMWindow& window, DOMObjectCacheFrameObserver& frameObserver)
114             : m_window(makeWeakPtr(window))
115             , m_frameObserver(frameObserver)
116         {
117             window.registerObserver(*this);
118         }
119
120         ~DOMWindowObserver()
121         {
122             if (m_window)
123                 m_window->unregisterObserver(*this);
124         }
125
126         WebCore::DOMWindow* window() const { return m_window.get(); }
127
128     private:
129         void willDetachGlobalObjectFromFrame() override
130         {
131             m_frameObserver.willDetachGlobalObjectFromFrame();
132         }
133
134         WeakPtr<WebCore::DOMWindow> m_window;
135         DOMObjectCacheFrameObserver& m_frameObserver;
136     };
137
138     static void objectFinalizedCallback(gpointer userData, GObject* finalizedObject)
139     {
140         DOMObjectCacheFrameObserver* observer = static_cast<DOMObjectCacheFrameObserver*>(userData);
141         observer->m_objects.removeFirstMatching([finalizedObject](DOMObjectCacheData* data) {
142             return data->object == finalizedObject;
143         });
144     }
145
146     void clear()
147     {
148         if (m_objects.isEmpty())
149             return;
150
151         auto objects = WTFMove(m_objects);
152
153         // Deleting of DOM wrappers might end up deleting the wrapped core object which could cause some problems
154         // for example if a Document is deleted during the frame destruction, so we remove the weak references now
155         // and delete the objects on next run loop iteration. See https://bugs.webkit.org/show_bug.cgi?id=151700.
156         for (auto* data : objects)
157             g_object_weak_unref(data->object, DOMObjectCacheFrameObserver::objectFinalizedCallback, this);
158
159         RunLoop::main().dispatch([objects] {
160             for (auto* data : objects)
161                 data->clearObject();
162         });
163     }
164
165     void willDetachPage() override
166     {
167         clear();
168     }
169
170     void frameDestroyed() override
171     {
172         clear();
173         WebCore::Frame* frame = m_frame;
174         FrameDestructionObserver::frameDestroyed();
175         domObjectCacheFrameObservers().remove(frame);
176     }
177
178     void willDetachGlobalObjectFromFrame()
179     {
180         clear();
181         m_domWindowObserver = nullptr;
182     }
183
184     Vector<DOMObjectCacheData*, 8> m_objects;
185     std::unique_ptr<DOMWindowObserver> m_domWindowObserver;
186 };
187
188 static DOMObjectCacheFrameObserver& getOrCreateDOMObjectCacheFrameObserver(WebCore::Frame& frame)
189 {
190     DOMObjectCacheFrameObserverMap::AddResult result = domObjectCacheFrameObservers().add(&frame, nullptr);
191     if (result.isNewEntry)
192         result.iterator->value = makeUnique<DOMObjectCacheFrameObserver>(frame);
193     return *result.iterator->value;
194 }
195
196 typedef HashMap<void*, std::unique_ptr<DOMObjectCacheData>> DOMObjectMap;
197
198 static DOMObjectMap& domObjects()
199 {
200     static NeverDestroyed<DOMObjectMap> staticDOMObjects;
201     return staticDOMObjects;
202 }
203
204 void DOMObjectCache::forget(void* objectHandle)
205 {
206     ASSERT(domObjects().contains(objectHandle));
207     domObjects().remove(objectHandle);
208 }
209
210 void* DOMObjectCache::get(void* objectHandle)
211 {
212     DOMObjectCacheData* data = domObjects().get(objectHandle);
213     return data ? data->refObject() : nullptr;
214 }
215
216 void DOMObjectCache::put(void* objectHandle, void* wrapper)
217 {
218     DOMObjectMap::AddResult result = domObjects().add(objectHandle, nullptr);
219     if (result.isNewEntry)
220         result.iterator->value = makeUnique<DOMObjectCacheData>(G_OBJECT(wrapper));
221 }
222
223 void DOMObjectCache::put(WebCore::Node* objectHandle, void* wrapper)
224 {
225     DOMObjectMap::AddResult result = domObjects().add(objectHandle, nullptr);
226     if (!result.isNewEntry)
227         return;
228
229     result.iterator->value = makeUnique<DOMObjectCacheData>(G_OBJECT(wrapper));
230     if (WebCore::Frame* frame = objectHandle->document().frame())
231         getOrCreateDOMObjectCacheFrameObserver(*frame).addObjectCacheData(*result.iterator->value);
232 }
233
234 }