| 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 |