OLD | NEW |
| (Empty) |
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 | |
33 #include <v8.h> | |
34 | |
35 #include "DOMObjectsInclude.h" | |
36 #include "V8DOMMap.h" | |
37 | |
38 #include <wtf/HashMap.h> | |
39 #include <wtf/MainThread.h> | |
40 #include <wtf/Threading.h> | |
41 #include <wtf/ThreadSpecific.h> | |
42 | |
43 namespace WebCore { | |
44 | |
45 // DOM binding algorithm: | |
46 // | |
47 // There are two kinds of DOM objects: | |
48 // 1. DOM tree nodes, such as Document, HTMLElement, ... | |
49 // there classes implements TreeShared<T> interface; | |
50 // 2. Non-node DOM objects, such as CSSRule, Location, etc. | |
51 // these classes implement a ref-counted scheme. | |
52 // | |
53 // A DOM object may have a JS wrapper object. If a tree node | |
54 // is alive, its JS wrapper must be kept alive even it is not | |
55 // reachable from JS roots. | |
56 // However, JS wrappers of non-node objects can go away if | |
57 // not reachable from other JS objects. It works like a cache. | |
58 // | |
59 // DOM objects are ref-counted, and JS objects are traced from | |
60 // a set of root objects. They can create a cycle. To break | |
61 // cycles, we do following: | |
62 // Handles from DOM objects to JS wrappers are always weak, | |
63 // so JS wrappers of non-node object cannot create a cycle. | |
64 // Before starting a global GC, we create a virtual connection | |
65 // between nodes in the same tree in the JS heap. If the wrapper | |
66 // of one node in a tree is alive, wrappers of all nodes in | |
67 // the same tree are considered alive. This is done by creating | |
68 // object groups in GC prologue callbacks. The mark-compact | |
69 // collector will remove these groups after each GC. | |
70 // | |
71 // DOM objects should be deref-ed from the owning thread, not the GC thread | |
72 // that does not own them. In V8, GC can kick in from any thread. To ensure | |
73 // that DOM objects are always deref-ed from the owning thread when running | |
74 // V8 in multi-threading environment, we do following: | |
75 // 1. Maintain a thread specific DOM wrapper map for each object map. | |
76 // (We're using TLS support from WTF instead of base since V8Bindings | |
77 // does not depend on base. We further assume that all child threads | |
78 // running V8 instances are created by WTF and thus a destructor will | |
79 // be called to clean up all thread specific data.) | |
80 // 2. When GC happens: | |
81 // 2.1. If the dead object is in GC thread's map, remove the JS reference | |
82 // and deref the DOM object. | |
83 // 2.2. Otherwise, go through all thread maps to find the owning thread. | |
84 // Remove the JS reference from the owning thread's map and move the | |
85 // DOM object to a delayed queue. Post a task to the owning thread | |
86 // to have it deref-ed from the owning thread at later time. | |
87 // 3. When a thread is tearing down, invoke a cleanup routine to go through | |
88 // all objects in the delayed queue and the thread map and deref all of | |
89 // them. | |
90 | |
91 static void weakDOMObjectCallback(v8::Persistent<v8::Value> obj, void* param); | |
92 static void weakNodeCallback(v8::Persistent<v8::Value> obj, void* param); | |
93 | |
94 #if ENABLE(SVG) | |
95 static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> obj, void*
param); | |
96 | |
97 // SVG non-node elements may have a reference to a context node which should be
notified when the element is change | |
98 static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> obj, void
* domObj); | |
99 #endif | |
100 | |
101 // This is to ensure that we will deref DOM objects from the owning thread, not
the GC thread. | |
102 // The helper function will be scheduled by the GC thread to get called from the
owning thread. | |
103 static void derefDelayedObjectsInCurrentThread(void*); | |
104 | |
105 // This should be called to remove all DOM objects associated with the current t
hread when it is tearing down. | |
106 static void removeAllDOMObjectsInCurrentThread(); | |
107 | |
108 // A map from a thread ID to thread's specific data. | |
109 class ThreadSpecificDOMData; | |
110 typedef WTF::HashMap<WTF::ThreadIdentifier, ThreadSpecificDOMData*> DOMThreadMap
; | |
111 static DOMThreadMap domThreadMap; | |
112 | |
113 // Mutex to protect against concurrent access of domThreadMap. | |
114 static WTF::Mutex domThreadMapMutex; | |
115 | |
116 class ThreadSpecificDOMData { | |
117 public: | |
118 enum DOMWrapperMapType | |
119 { | |
120 DOMNodeMap, | |
121 DOMObjectMap, | |
122 ActiveDOMObjectMap, | |
123 #if ENABLE(SVG) | |
124 DOMSVGElementInstanceMap, | |
125 DOMSVGObjectWithContextMap | |
126 #endif | |
127 }; | |
128 | |
129 typedef WTF::HashMap<void*, V8ClassIndex::V8WrapperType> DelayedObjectMap; | |
130 | |
131 template <class KeyType> | |
132 class InternalDOMWrapperMap : public DOMWrapperMap<KeyType> { | |
133 public: | |
134 InternalDOMWrapperMap(v8::WeakReferenceCallback callback) | |
135 : DOMWrapperMap<KeyType>(callback) { } | |
136 | |
137 virtual void forget(KeyType* obj); | |
138 | |
139 void forgetOnly(KeyType* obj) | |
140 { | |
141 DOMWrapperMap<KeyType>::forget(obj); | |
142 } | |
143 }; | |
144 | |
145 ThreadSpecificDOMData() | |
146 : m_domNodeMap(new InternalDOMWrapperMap<Node>(&weakNodeCallback)) | |
147 , m_domObjectMap(new InternalDOMWrapperMap<void>(weakDOMObjectCallback)) | |
148 , m_activeDomObjectMap(new InternalDOMWrapperMap<void>(weakActiveDOMObje
ctCallback)) | |
149 #if ENABLE(SVG) | |
150 , m_domSvgElementInstanceMap(new InternalDOMWrapperMap<SVGElementInstanc
e>(weakSVGElementInstanceCallback)) | |
151 , m_domSvgObjectWithContextMap(new InternalDOMWrapperMap<void>(weakSVGOb
jectWithContextCallback)) | |
152 #endif | |
153 , m_delayedProcessingScheduled(false) | |
154 , m_isMainThread(WTF::isMainThread()) | |
155 { | |
156 WTF::MutexLocker locker(domThreadMapMutex); | |
157 domThreadMap.set(WTF::currentThread(), this); | |
158 } | |
159 | |
160 // This is called when WTF thread is tearing down. | |
161 // We assume that all child threads running V8 instances are created by WTF. | |
162 ~ThreadSpecificDOMData() | |
163 { | |
164 removeAllDOMObjectsInCurrentThread(); | |
165 | |
166 delete m_domNodeMap; | |
167 delete m_domObjectMap; | |
168 delete m_activeDomObjectMap; | |
169 #if ENABLE(SVG) | |
170 delete m_domSvgElementInstanceMap; | |
171 delete m_domSvgObjectWithContextMap; | |
172 #endif | |
173 | |
174 WTF::MutexLocker locker(domThreadMapMutex); | |
175 domThreadMap.remove(WTF::currentThread()); | |
176 } | |
177 | |
178 void* getDOMWrapperMap(DOMWrapperMapType type) | |
179 { | |
180 switch (type) { | |
181 case DOMNodeMap: | |
182 return m_domNodeMap; | |
183 case DOMObjectMap: | |
184 return m_domObjectMap; | |
185 case ActiveDOMObjectMap: | |
186 return m_activeDomObjectMap; | |
187 #if ENABLE(SVG) | |
188 case DOMSVGElementInstanceMap: | |
189 return m_domSvgElementInstanceMap; | |
190 case DOMSVGObjectWithContextMap: | |
191 return m_domSvgObjectWithContextMap; | |
192 #endif | |
193 default: | |
194 ASSERT(false); | |
195 return NULL; | |
196 } | |
197 } | |
198 | |
199 InternalDOMWrapperMap<Node>& domNodeMap() { return *m_domNodeMap; } | |
200 InternalDOMWrapperMap<void>& domObjectMap() { return *m_domObjectMap; } | |
201 InternalDOMWrapperMap<void>& activeDomObjectMap() { return *m_activeDomObjec
tMap; } | |
202 #if ENABLE(SVG) | |
203 InternalDOMWrapperMap<SVGElementInstance>& domSvgElementInstanceMap() { retu
rn *m_domSvgElementInstanceMap; } | |
204 InternalDOMWrapperMap<void>& domSvgObjectWithContextMap() { return *m_domSvg
ObjectWithContextMap; } | |
205 #endif | |
206 | |
207 DelayedObjectMap& delayedObjectMap() { return m_delayedObjectMap; } | |
208 bool delayedProcessingScheduled() const { return m_delayedProcessingSchedule
d; } | |
209 void setDelayedProcessingScheduled(bool value) { m_delayedProcessingSchedule
d = value; } | |
210 bool isMainThread() const { return m_isMainThread; } | |
211 | |
212 private: | |
213 InternalDOMWrapperMap<Node>* m_domNodeMap; | |
214 InternalDOMWrapperMap<void>* m_domObjectMap; | |
215 InternalDOMWrapperMap<void>* m_activeDomObjectMap; | |
216 #if ENABLE(SVG) | |
217 InternalDOMWrapperMap<SVGElementInstance>* m_domSvgElementInstanceMap; | |
218 InternalDOMWrapperMap<void>* m_domSvgObjectWithContextMap; | |
219 #endif | |
220 | |
221 // Stores all the DOM objects that are delayed to be processed when the owni
ng thread gains control. | |
222 DelayedObjectMap m_delayedObjectMap; | |
223 | |
224 // The flag to indicate if the task to do the delayed process has already be
en posted. | |
225 bool m_delayedProcessingScheduled; | |
226 | |
227 bool m_isMainThread; | |
228 }; | |
229 | |
230 static WTF::ThreadSpecific<ThreadSpecificDOMData> threadSpecificDOMData; | |
231 | |
232 template<typename T> | |
233 static void handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMWrapperMapT
ype mapType, V8ClassIndex::V8WrapperType objType, T* obj); | |
234 | |
235 template <class KeyType> | |
236 void ThreadSpecificDOMData::InternalDOMWrapperMap<KeyType>::forget(KeyType* obj) | |
237 { | |
238 DOMWrapperMap<KeyType>::forget(obj); | |
239 | |
240 ThreadSpecificDOMData::DelayedObjectMap& delayedObjectMap = (*threadSpecific
DOMData).delayedObjectMap(); | |
241 delayedObjectMap.take(obj); | |
242 } | |
243 | |
244 DOMWrapperMap<Node>& getDOMNodeMap() | |
245 { | |
246 return (*threadSpecificDOMData).domNodeMap(); | |
247 } | |
248 | |
249 DOMWrapperMap<void>& getDOMObjectMap() | |
250 { | |
251 return (*threadSpecificDOMData).domObjectMap(); | |
252 } | |
253 | |
254 DOMWrapperMap<void>& getActiveDOMObjectMap() | |
255 { | |
256 return (*threadSpecificDOMData).activeDomObjectMap(); | |
257 } | |
258 | |
259 #if ENABLE(SVG) | |
260 DOMWrapperMap<SVGElementInstance>& getDOMSVGElementInstanceMap() | |
261 { | |
262 return (*threadSpecificDOMData).domSvgElementInstanceMap(); | |
263 } | |
264 | |
265 static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> obj, void*
param) | |
266 { | |
267 SVGElementInstance* instance = static_cast<SVGElementInstance*>(param); | |
268 | |
269 ThreadSpecificDOMData::InternalDOMWrapperMap<SVGElementInstance>& map = stat
ic_cast<ThreadSpecificDOMData::InternalDOMWrapperMap<SVGElementInstance>&>(getDO
MSVGElementInstanceMap()); | |
270 if (map.contains(instance)) { | |
271 instance->deref(); | |
272 map.forgetOnly(instance); | |
273 } else { | |
274 handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMSVGElementInsta
nceMap, V8ClassIndex::SVGELEMENTINSTANCE, instance); | |
275 } | |
276 } | |
277 | |
278 // Map of SVG objects with contexts to V8 objects | |
279 DOMWrapperMap<void>& getDOMSVGObjectWithContextMap() | |
280 { | |
281 return (*threadSpecificDOMData).domSvgObjectWithContextMap(); | |
282 } | |
283 | |
284 static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> obj, void
* domObj) | |
285 { | |
286 v8::HandleScope scope; | |
287 ASSERT(obj->IsObject()); | |
288 | |
289 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8:
:Object>::Cast(obj)); | |
290 | |
291 ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<Thread
SpecificDOMData::InternalDOMWrapperMap<void>&>(getDOMSVGObjectWithContextMap()); | |
292 if (map.contains(domObj)) { | |
293 // Forget function removes object from the map and dispose the wrapper. | |
294 map.forgetOnly(domObj); | |
295 | |
296 switch (type) { | |
297 #define MakeCase(TYPE, NAME) \ | |
298 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break; | |
299 SVG_OBJECT_TYPES(MakeCase) | |
300 #undef MakeCase | |
301 #define MakeCase(TYPE, NAME) \ | |
302 case V8ClassIndex::TYPE: \ | |
303 static_cast<V8SVGPODTypeWrapper<NAME>*>(domObj)->deref(); break; | |
304 SVG_POD_NATIVE_TYPES(MakeCase) | |
305 #undef MakeCase | |
306 default: | |
307 ASSERT(false); | |
308 } | |
309 } else { | |
310 handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMSVGObjectWithCo
ntextMap, type, domObj); | |
311 } | |
312 } | |
313 #endif | |
314 | |
315 // Called when the dead object is not in GC thread's map. Go through all thread
maps to find the one containing it. | |
316 // Then clear the JS reference and push the DOM object into the delayed queue fo
r it to be deref-ed at later time from the owning thread. | |
317 // * This is called when the GC thread is not the owning thread. | |
318 // * This can be called on any thread that has GC running. | |
319 // * Only one V8 instance is running at a time due to V8::Locker. So we don't ne
ed to worry about concurrency. | |
320 template<typename T> | |
321 static void handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMWrapperMapT
ype mapType, V8ClassIndex::V8WrapperType objType, T* obj) | |
322 { | |
323 WTF::MutexLocker locker(domThreadMapMutex); | |
324 for (typename DOMThreadMap::iterator iter(domThreadMap.begin()); iter != dom
ThreadMap.end(); ++iter) { | |
325 WTF::ThreadIdentifier threadID = iter->first; | |
326 ThreadSpecificDOMData* threadData = iter->second; | |
327 | |
328 // Skip the current thread that is GC thread. | |
329 if (threadID == WTF::currentThread()) { | |
330 ASSERT(!static_cast<DOMWrapperMap<T>*>(threadData->getDOMWrapperMap(
mapType))->contains(obj)); | |
331 continue; | |
332 } | |
333 | |
334 ThreadSpecificDOMData::InternalDOMWrapperMap<T>* domMap = static_cast<Th
readSpecificDOMData::InternalDOMWrapperMap<T>*>(threadData->getDOMWrapperMap(map
Type)); | |
335 if (domMap->contains(obj)) { | |
336 // Clear the JS reference. | |
337 domMap->forgetOnly(obj); | |
338 | |
339 // Push into the delayed queue. | |
340 threadData->delayedObjectMap().set(obj, objType); | |
341 | |
342 // Post a task to the owning thread in order to process the delayed
queue. | |
343 // FIXME(jianli): For now, we can only post to main thread due to WT
F task posting limitation. We will fix this when we work on nested worker. | |
344 if (!threadData->delayedProcessingScheduled()) { | |
345 threadData->setDelayedProcessingScheduled(true); | |
346 if (threadData->isMainThread()) | |
347 WTF::callOnMainThread(&derefDelayedObjectsInCurrentThread, N
ULL); | |
348 } | |
349 | |
350 break; | |
351 } | |
352 } | |
353 } | |
354 | |
355 // Called when obj is near death (not reachable from JS roots). | |
356 // It is time to remove the entry from the table and dispose the handle. | |
357 static void weakDOMObjectCallback(v8::Persistent<v8::Value> obj, void* domObj) | |
358 { | |
359 v8::HandleScope scope; | |
360 ASSERT(obj->IsObject()); | |
361 | |
362 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8:
:Object>::Cast(obj)); | |
363 | |
364 ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<Thread
SpecificDOMData::InternalDOMWrapperMap<void>&>(getDOMObjectMap()); | |
365 if (map.contains(domObj)) { | |
366 // Forget function removes object from the map and dispose the wrapper. | |
367 map.forgetOnly(domObj); | |
368 | |
369 switch (type) { | |
370 #define MakeCase(TYPE, NAME) \ | |
371 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break; | |
372 DOM_OBJECT_TYPES(MakeCase) | |
373 #undef MakeCase | |
374 default: | |
375 ASSERT(false); | |
376 } | |
377 } else { | |
378 handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMObjectMap, type
, domObj); | |
379 } | |
380 } | |
381 | |
382 void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> obj, void* domObj) | |
383 { | |
384 v8::HandleScope scope; | |
385 ASSERT(obj->IsObject()); | |
386 | |
387 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8:
:Object>::Cast(obj)); | |
388 | |
389 ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<Thread
SpecificDOMData::InternalDOMWrapperMap<void>&>(getActiveDOMObjectMap()); | |
390 if (map.contains(domObj)) { | |
391 // Forget function removes object from the map and dispose the wrapper. | |
392 map.forgetOnly(domObj); | |
393 | |
394 switch (type) { | |
395 #define MakeCase(TYPE, NAME) \ | |
396 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break; | |
397 ACTIVE_DOM_OBJECT_TYPES(MakeCase) | |
398 #undef MakeCase | |
399 default: | |
400 ASSERT(false); | |
401 } | |
402 } else { | |
403 handleWeakObjectInOwningThread(ThreadSpecificDOMData::ActiveDOMObjectMap
, type, domObj); | |
404 } | |
405 } | |
406 | |
407 static void weakNodeCallback(v8::Persistent<v8::Value> obj, void* param) | |
408 { | |
409 Node* node = static_cast<Node*>(param); | |
410 | |
411 ThreadSpecificDOMData::InternalDOMWrapperMap<Node>& map = static_cast<Thread
SpecificDOMData::InternalDOMWrapperMap<Node>&>(getDOMNodeMap()); | |
412 if (map.contains(node)) { | |
413 map.forgetOnly(node); | |
414 node->deref(); | |
415 } else { | |
416 handleWeakObjectInOwningThread<Node>(ThreadSpecificDOMData::DOMNodeMap,
V8ClassIndex::NODE, node); | |
417 } | |
418 } | |
419 | |
420 static void derefObject(V8ClassIndex::V8WrapperType type, void* domObj) | |
421 { | |
422 switch (type) { | |
423 case V8ClassIndex::NODE: | |
424 static_cast<Node*>(domObj)->deref(); | |
425 break; | |
426 | |
427 #define MakeCase(TYPE, NAME) \ | |
428 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break; | |
429 DOM_OBJECT_TYPES(MakeCase) // This includes both active and non-active
. | |
430 #undef MakeCase | |
431 | |
432 #if ENABLE(SVG) | |
433 #define MakeCase(TYPE, NAME) \ | |
434 case V8ClassIndex::TYPE: static_cast<NAME*>(domObj)->deref(); break; | |
435 SVG_OBJECT_TYPES(MakeCase) // This also includes SVGElementInstance. | |
436 #undef MakeCase | |
437 | |
438 #define MakeCase(TYPE, NAME) \ | |
439 case V8ClassIndex::TYPE: \ | |
440 static_cast<V8SVGPODTypeWrapper<NAME>*>(domObj)->deref(); break; | |
441 SVG_POD_NATIVE_TYPES(MakeCase) | |
442 #undef MakeCase | |
443 #endif | |
444 | |
445 default: | |
446 ASSERT(false); | |
447 } | |
448 } | |
449 | |
450 static void derefDelayedObjects() | |
451 { | |
452 WTF::MutexLocker locker(domThreadMapMutex); | |
453 ThreadSpecificDOMData::DelayedObjectMap& delayedObjectMap = (*threadSpecific
DOMData).delayedObjectMap(); | |
454 for (ThreadSpecificDOMData::DelayedObjectMap::iterator iter(delayedObjectMap
.begin()); iter != delayedObjectMap.end(); ++iter) { | |
455 derefObject(iter->second, iter->first); | |
456 } | |
457 delayedObjectMap.clear(); | |
458 } | |
459 | |
460 static void derefDelayedObjectsInCurrentThread(void*) | |
461 { | |
462 (*threadSpecificDOMData).setDelayedProcessingScheduled(false); | |
463 derefDelayedObjects(); | |
464 } | |
465 | |
466 template<typename T> | |
467 static void removeObjectsFromWrapperMap(DOMWrapperMap<T>& domMap) | |
468 { | |
469 for (typename WTF::HashMap<T*, v8::Object*>::iterator iter(domMap.impl().beg
in()); iter != domMap.impl().end(); ++iter) { | |
470 T* domObj = static_cast<T*>(iter->first); | |
471 v8::Persistent<v8::Object> obj(iter->second); | |
472 | |
473 V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle
<v8::Object>::Cast(obj)); | |
474 | |
475 // Deref the DOM object. | |
476 derefObject(type, domObj); | |
477 | |
478 // Clear the JS wrapper. | |
479 obj.Dispose(); | |
480 } | |
481 domMap.impl().clear(); | |
482 } | |
483 | |
484 static void removeAllDOMObjectsInCurrentThread() | |
485 { | |
486 v8::Locker locker; | |
487 v8::HandleScope scope; | |
488 | |
489 // Deref all objects in the delayed queue. | |
490 derefDelayedObjects(); | |
491 | |
492 // Remove all DOM nodes. | |
493 removeObjectsFromWrapperMap<Node>(getDOMNodeMap()); | |
494 | |
495 // Remove all DOM objects in the wrapper map. | |
496 removeObjectsFromWrapperMap<void>(getDOMObjectMap()); | |
497 | |
498 // Remove all active DOM objects in the wrapper map. | |
499 removeObjectsFromWrapperMap<void>(getActiveDOMObjectMap()); | |
500 | |
501 #if ENABLE(SVG) | |
502 // Remove all SVG element instances in the wrapper map. | |
503 removeObjectsFromWrapperMap<SVGElementInstance>(getDOMSVGElementInstanceMap(
)); | |
504 | |
505 // Remove all SVG objects with context in the wrapper map. | |
506 removeObjectsFromWrapperMap<void>(getDOMSVGObjectWithContextMap()); | |
507 #endif | |
508 } | |
509 | |
510 } // namespace WebCore | |
OLD | NEW |