[V8] We can merge the wrapper maps for DOM objects and active DOM objects
[WebKit-https.git] / Source / WebCore / bindings / v8 / V8GCController.cpp
1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  * 
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  * 
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  * 
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "V8GCController.h"
33
34 #include "ActiveDOMObject.h"
35 #include "Attr.h"
36 #include "DOMDataStore.h"
37 #include "DOMImplementation.h"
38 #include "HTMLImageElement.h"
39 #include "HTMLNames.h"
40 #include "IntrusiveDOMWrapperMap.h"
41 #include "MemoryUsageSupport.h"
42 #include "MessagePort.h"
43 #include "RetainedDOMInfo.h"
44 #include "RetainedObjectInfo.h"
45 #include "V8AbstractEventListener.h"
46 #include "V8Binding.h"
47 #include "V8CSSRule.h"
48 #include "V8CSSRuleList.h"
49 #include "V8CSSStyleDeclaration.h"
50 #include "V8DOMImplementation.h"
51 #include "V8MessagePort.h"
52 #include "V8RecursionScope.h"
53 #include "V8StyleSheet.h"
54 #include "V8StyleSheetList.h"
55 #include "WrapperTypeInfo.h"
56
57 #include <algorithm>
58 #include <utility>
59 #include <v8-debug.h>
60 #include <wtf/HashMap.h>
61 #include <wtf/StdLibExtras.h>
62 #include <wtf/UnusedParam.h>
63
64 #if PLATFORM(CHROMIUM)
65 #include "TraceEvent.h"
66 #endif
67
68 namespace WebCore {
69
70 class ObjectVisitor : public DOMWrapperVisitor<void> {
71 public:
72     explicit ObjectVisitor(Vector<v8::Persistent<v8::Value> >* liveObjects)
73         : m_liveObjects(liveObjects)
74     {
75     }
76
77     void visitDOMWrapper(DOMDataStore* store, void* object, v8::Persistent<v8::Object> wrapper)
78     {
79         WrapperTypeInfo* type = V8DOMWrapper::domWrapperType(wrapper);
80
81         if (V8MessagePort::info.equals(type)) {
82             // Mark each port as in-use if it's entangled. For simplicity's sake,
83             // we assume all ports are remotely entangled, since the Chromium port
84             // implementation can't tell the difference.
85             MessagePort* port = static_cast<MessagePort*>(object);
86             if (port->isEntangled() || port->hasPendingActivity())
87                 m_liveObjects->append(wrapper);
88         } else {
89             ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(wrapper);
90             if (activeDOMObject && activeDOMObject->hasPendingActivity())
91                 m_liveObjects->append(wrapper);
92         }
93
94         type->visitDOMWrapper(store, object, wrapper);
95     }
96
97 private:
98     Vector<v8::Persistent<v8::Value> >* m_liveObjects;
99 };
100
101 static void addImplicitReferencesForNodeWithEventListeners(Node* node, v8::Persistent<v8::Object> wrapper)
102 {
103     ASSERT(node->hasEventListeners());
104
105     Vector<v8::Persistent<v8::Value> > listeners;
106
107     EventListenerIterator iterator(node);
108     while (EventListener* listener = iterator.nextListener()) {
109         if (listener->type() != EventListener::JSEventListenerType)
110             continue;
111         V8AbstractEventListener* v8listener = static_cast<V8AbstractEventListener*>(listener);
112         if (!v8listener->hasExistingListenerObject())
113             continue;
114         listeners.append(v8listener->existingListenerObjectPersistentHandle());
115     }
116
117     if (listeners.isEmpty())
118         return;
119
120     v8::V8::AddImplicitReferences(wrapper, listeners.data(), listeners.size());
121 }
122
123 static Node* rootForGC(Node* node)
124 {
125     if (node->inDocument() || (node->hasTagName(HTMLNames::imgTag) && static_cast<HTMLImageElement*>(node)->hasPendingActivity()))
126         return node->document();
127
128     if (node->isAttributeNode()) {
129         node = static_cast<Attr*>(node)->ownerElement();
130         if (!node)
131             return 0;
132     }
133
134     while (Node* parent = node->parentOrHostNode())
135         node = parent;
136
137     return node;
138 }
139
140 class ImplicitConnection {
141 public:
142     ImplicitConnection(Node* root, v8::Persistent<v8::Value> wrapper)
143         : m_root(root)
144         , m_wrapper(wrapper)
145     {
146     }
147
148     Node* root() const { return m_root; }
149     v8::Persistent<v8::Value> wrapper() const { return m_wrapper; }
150
151 public:
152     Node* m_root;
153     v8::Persistent<v8::Value> m_wrapper;
154 };
155
156 bool operator<(const ImplicitConnection& left, const ImplicitConnection& right)
157 {
158     return left.root() < right.root();
159 }
160
161 class NodeVisitor : public NodeWrapperVisitor {
162 public:
163     explicit NodeVisitor(Vector<v8::Persistent<v8::Value> >* liveObjects)
164         : m_liveObjects(liveObjects)
165     {
166     }
167
168     void visitNodeWrapper(Node* node, v8::Persistent<v8::Object> wrapper)
169     {
170         if (node->hasEventListeners())
171             addImplicitReferencesForNodeWithEventListeners(node, wrapper);
172
173         WrapperTypeInfo* type = V8DOMWrapper::domWrapperType(wrapper);  
174
175         ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(wrapper);
176         if (activeDOMObject && activeDOMObject->hasPendingActivity())
177             m_liveObjects->append(wrapper);
178
179         Node* root = rootForGC(node);
180         if (!root)
181             return;
182         m_connections.append(ImplicitConnection(root, wrapper));
183     }
184
185     void applyGrouping()
186     {
187         std::sort(m_connections.begin(), m_connections.end());
188         Vector<v8::Persistent<v8::Value> > group;
189         size_t i = 0;
190         while (i < m_connections.size()) {
191             Node* root = m_connections[i].root();
192
193             do {
194                 group.append(m_connections[i++].wrapper());
195             } while (i < m_connections.size() && root == m_connections[i].root());
196
197             if (group.size() > 1)
198                 v8::V8::AddObjectGroup(group.data(), group.size(), new RetainedDOMInfo(root));
199
200             group.shrink(0);
201         }
202     }
203
204 private:
205     Vector<v8::Persistent<v8::Value> >* m_liveObjects;
206     Vector<ImplicitConnection> m_connections;
207 };
208
209 void V8GCController::gcPrologue(v8::GCType type, v8::GCCallbackFlags flags)
210 {
211     if (type == v8::kGCTypeScavenge)
212         minorGCPrologue();
213     else if (type == v8::kGCTypeMarkSweepCompact)
214         majorGCPrologue();
215 }
216
217 void V8GCController::minorGCPrologue()
218 {
219 }
220
221 void V8GCController::majorGCPrologue()
222 {
223     TRACE_EVENT_BEGIN0("v8", "GC");
224
225     v8::HandleScope scope;
226
227     Vector<v8::Persistent<v8::Value> > liveObjects;
228     liveObjects.append(V8PerIsolateData::current()->ensureLiveRoot());
229
230     NodeVisitor nodeVisitor(&liveObjects);
231     visitAllDOMNodes(&nodeVisitor);
232     nodeVisitor.applyGrouping();
233
234     ObjectVisitor objectVisitor(&liveObjects);
235     visitDOMObjects(&objectVisitor);
236
237     v8::V8::AddObjectGroup(liveObjects.data(), liveObjects.size());
238
239     V8PerIsolateData* data = V8PerIsolateData::current();
240     data->stringCache()->clearOnGC();
241 }
242
243 #if PLATFORM(CHROMIUM)
244 static int workingSetEstimateMB = 0;
245
246 static Mutex& workingSetEstimateMBMutex()
247 {
248     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
249     return mutex;
250 }
251 #endif
252
253 void V8GCController::gcEpilogue(v8::GCType type, v8::GCCallbackFlags flags)
254 {
255     if (type == v8::kGCTypeScavenge)
256         minorGCEpilogue();
257     else if (type == v8::kGCTypeMarkSweepCompact)
258         majorGCEpilogue();
259 }
260
261 void V8GCController::minorGCEpilogue()
262 {
263 }
264
265 void V8GCController::majorGCEpilogue()
266 {
267     v8::HandleScope scope;
268
269 #if PLATFORM(CHROMIUM)
270     // The GC can happen on multiple threads in case of dedicated workers which run in-process.
271     {
272         MutexLocker locker(workingSetEstimateMBMutex());
273         workingSetEstimateMB = MemoryUsageSupport::actualMemoryUsageMB();
274     }
275 #endif
276
277     TRACE_EVENT_END0("v8", "GC");
278 }
279
280 void V8GCController::checkMemoryUsage()
281 {
282 #if PLATFORM(CHROMIUM)
283     const int lowMemoryUsageMB = MemoryUsageSupport::lowMemoryUsageMB();
284     const int highMemoryUsageMB = MemoryUsageSupport::highMemoryUsageMB();
285     const int highUsageDeltaMB = MemoryUsageSupport::highUsageDeltaMB();
286     int memoryUsageMB = MemoryUsageSupport::memoryUsageMB();
287     int workingSetEstimateMBCopy;
288     {
289         MutexLocker locker(workingSetEstimateMBMutex());
290         workingSetEstimateMBCopy = workingSetEstimateMB;
291     }
292
293     if ((memoryUsageMB > lowMemoryUsageMB && memoryUsageMB > 2 * workingSetEstimateMBCopy) || (memoryUsageMB > highMemoryUsageMB && memoryUsageMB > workingSetEstimateMBCopy + highUsageDeltaMB))
294         v8::V8::LowMemoryNotification();
295 #endif
296 }
297
298 void V8GCController::hintForCollectGarbage()
299 {
300     V8PerIsolateData* data = V8PerIsolateData::current();
301     if (!data->shouldCollectGarbageSoon())
302         return;
303     const int longIdlePauseInMS = 1000;
304     data->clearShouldCollectGarbageSoon();
305     v8::V8::ContextDisposedNotification();
306     v8::V8::IdleNotification(longIdlePauseInMS);
307 }
308
309 void V8GCController::collectGarbage()
310 {
311     v8::HandleScope handleScope;
312
313     ScopedPersistent<v8::Context> context;
314
315     context.adopt(v8::Context::New());
316     if (context.isEmpty())
317         return;
318
319     {
320         v8::Context::Scope scope(context.get());
321         v8::Local<v8::String> source = v8::String::New("if (gc) gc();");
322         v8::Local<v8::String> name = v8::String::New("gc");
323         v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
324         if (!script.IsEmpty()) {
325             V8RecursionScope::MicrotaskSuppression scope;
326             script->Run();
327         }
328     }
329
330     context.clear();
331 }
332
333 }  // namespace WebCore