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