| /* |
| * Copyright (C) 2009 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "V8DOMMap.h" |
| |
| #include "DOMObjectsInclude.h" |
| |
| #include <v8.h> |
| |
| #include <wtf/HashMap.h> |
| #include <wtf/MainThread.h> |
| #include <wtf/Noncopyable.h> |
| #include <wtf/StdLibExtras.h> |
| #include <wtf/Threading.h> |
| #include <wtf/ThreadSpecific.h> |
| #include <wtf/Vector.h> |
| |
| #include "V8IsolatedWorld.h" |
| |
| namespace WebCore { |
| |
| // DOM binding algorithm: |
| // |
| // There are two kinds of DOM objects: |
| // 1. DOM tree nodes, such as Document, HTMLElement, ... |
| // there classes implement TreeShared<T> interface; |
| // 2. Non-node DOM objects, such as CSSRule, Location, etc. |
| // these classes implement a ref-counted scheme. |
| // |
| // A DOM object may have a JS wrapper object. If a tree node |
| // is alive, its JS wrapper must be kept alive even it is not |
| // reachable from JS roots. |
| // However, JS wrappers of non-node objects can go away if |
| // not reachable from other JS objects. It works like a cache. |
| // |
| // DOM objects are ref-counted, and JS objects are traced from |
| // a set of root objects. They can create a cycle. To break |
| // cycles, we do following: |
| // Handles from DOM objects to JS wrappers are always weak, |
| // so JS wrappers of non-node object cannot create a cycle. |
| // Before starting a global GC, we create a virtual connection |
| // between nodes in the same tree in the JS heap. If the wrapper |
| // of one node in a tree is alive, wrappers of all nodes in |
| // the same tree are considered alive. This is done by creating |
| // object groups in GC prologue callbacks. The mark-compact |
| // collector will remove these groups after each GC. |
| // |
| // DOM objects should be deref-ed from the owning thread, not the GC thread |
| // that does not own them. In V8, GC can kick in from any thread. To ensure |
| // that DOM objects are always deref-ed from the owning thread when running |
| // V8 in multi-threading environment, we do following: |
| // 1. Maintain a thread specific DOM wrapper map for each object map. |
| // (We're using TLS support from WTF instead of base since V8Bindings |
| // does not depend on base. We further assume that all child threads |
| // running V8 instances are created by WTF and thus a destructor will |
| // be called to clean up all thread specific data.) |
| // 2. When GC happens: |
| // 2.1. If the dead object is in GC thread's map, remove the JS reference |
| // and deref the DOM object. |
| // 2.2. Otherwise, go through all thread maps to find the owning thread. |
| // Remove the JS reference from the owning thread's map and move the |
| // DOM object to a delayed queue. Post a task to the owning thread |
| // to have it deref-ed from the owning thread at later time. |
| // 3. When a thread is tearing down, invoke a cleanup routine to go through |
| // all objects in the delayed queue and the thread map and deref all of |
| // them. |
| |
| static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject); |
| static void weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject); |
| void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject); |
| #if ENABLE(SVG) |
| static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject); |
| // SVG non-node elements may have a reference to a context node which should be notified when the element is change. |
| static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object, void* domObject); |
| #endif |
| |
| class DOMData; |
| class DOMDataStore; |
| typedef WTF::Vector<DOMDataStore*> DOMDataList; |
| |
| // DOMDataStore |
| // |
| // DOMDataStore is the backing store that holds the maps between DOM objects |
| // and JavaScript objects. In general, each thread can have multiple backing |
| // stores, one per isolated world. |
| // |
| // This class doesn't manage the lifetime of the store. The data store |
| // lifetime is managed by subclasses. |
| // |
| class DOMDataStore : Noncopyable { |
| public: |
| enum DOMWrapperMapType { |
| DOMNodeMap, |
| DOMObjectMap, |
| ActiveDOMObjectMap, |
| #if ENABLE(SVG) |
| DOMSVGElementInstanceMap, |
| DOMSVGObjectWithContextMap |
| #endif |
| }; |
| |
| template <class KeyType> |
| class InternalDOMWrapperMap : public DOMWrapperMap<KeyType> { |
| public: |
| InternalDOMWrapperMap(DOMData* domData, v8::WeakReferenceCallback callback) |
| : DOMWrapperMap<KeyType>(callback) |
| , m_domData(domData) { } |
| |
| virtual void forget(KeyType*); |
| |
| void forgetOnly(KeyType* object) |
| { |
| DOMWrapperMap<KeyType>::forget(object); |
| } |
| |
| private: |
| DOMData* m_domData; |
| }; |
| |
| // A list of all DOMDataStore objects. Traversed during GC to find a thread-specific map that |
| // contains the object - so we can schedule the object to be deleted on the thread which created it. |
| static DOMDataList& allStores() |
| { |
| DEFINE_STATIC_LOCAL(DOMDataList, staticDOMDataList, ()); |
| return staticDOMDataList; |
| } |
| |
| // Mutex to protect against concurrent access of DOMDataList. |
| static WTF::Mutex& allStoresMutex() |
| { |
| DEFINE_STATIC_LOCAL(WTF::Mutex, staticDOMDataListMutex, ()); |
| return staticDOMDataListMutex; |
| } |
| |
| DOMDataStore(DOMData* domData); |
| virtual ~DOMDataStore(); |
| |
| DOMData* domData() const { return m_domData; } |
| |
| void* getDOMWrapperMap(DOMWrapperMapType type) |
| { |
| switch (type) { |
| case DOMNodeMap: |
| return m_domNodeMap; |
| case DOMObjectMap: |
| return m_domObjectMap; |
| case ActiveDOMObjectMap: |
| return m_activeDomObjectMap; |
| #if ENABLE(SVG) |
| case DOMSVGElementInstanceMap: |
| return m_domSvgElementInstanceMap; |
| case DOMSVGObjectWithContextMap: |
| return m_domSvgObjectWithContextMap; |
| #endif |
| } |
| |
| ASSERT_NOT_REACHED(); |
| return 0; |
| } |
| |
| InternalDOMWrapperMap<Node>& domNodeMap() { return *m_domNodeMap; } |
| InternalDOMWrapperMap<void>& domObjectMap() { return *m_domObjectMap; } |
| InternalDOMWrapperMap<void>& activeDomObjectMap() { return *m_activeDomObjectMap; } |
| #if ENABLE(SVG) |
| InternalDOMWrapperMap<SVGElementInstance>& domSvgElementInstanceMap() { return *m_domSvgElementInstanceMap; } |
| InternalDOMWrapperMap<void>& domSvgObjectWithContextMap() { return *m_domSvgObjectWithContextMap; } |
| #endif |
| |
| protected: |
| InternalDOMWrapperMap<Node>* m_domNodeMap; |
| InternalDOMWrapperMap<void>* m_domObjectMap; |
| InternalDOMWrapperMap<void>* m_activeDomObjectMap; |
| #if ENABLE(SVG) |
| InternalDOMWrapperMap<SVGElementInstance>* m_domSvgElementInstanceMap; |
| InternalDOMWrapperMap<void>* m_domSvgObjectWithContextMap; |
| #endif |
| |
| private: |
| // A back-pointer to the DOMData to which we belong. |
| DOMData* m_domData; |
| }; |
| |
| // ScopedDOMDataStore |
| // |
| // ScopedDOMDataStore is a DOMDataStore that controls limits the lifetime of |
| // the store to the lifetime of the object itself. In other words, when the |
| // ScopedDOMDataStore object is deallocated, the maps that belong to the store |
| // are deallocated as well. |
| // |
| class ScopedDOMDataStore : public DOMDataStore { |
| public: |
| ScopedDOMDataStore(DOMData* domData) : DOMDataStore(domData) |
| { |
| m_domNodeMap = new InternalDOMWrapperMap<Node>(domData, weakNodeCallback); |
| m_domObjectMap = new InternalDOMWrapperMap<void>(domData, weakDOMObjectCallback); |
| m_activeDomObjectMap = new InternalDOMWrapperMap<void>(domData, weakActiveDOMObjectCallback); |
| #if ENABLE(SVG) |
| m_domSvgElementInstanceMap = new InternalDOMWrapperMap<SVGElementInstance>(domData, weakSVGElementInstanceCallback); |
| m_domSvgObjectWithContextMap = new InternalDOMWrapperMap<void>(domData, weakSVGObjectWithContextCallback); |
| #endif |
| } |
| |
| // This can be called when WTF thread is tearing down. |
| // We assume that all child threads running V8 instances are created by WTF. |
| virtual ~ScopedDOMDataStore() |
| { |
| delete m_domNodeMap; |
| delete m_domObjectMap; |
| delete m_activeDomObjectMap; |
| #if ENABLE(SVG) |
| delete m_domSvgElementInstanceMap; |
| delete m_domSvgObjectWithContextMap; |
| #endif |
| } |
| }; |
| |
| // StaticDOMDataStore |
| // |
| // StaticDOMDataStore is a DOMDataStore that manages the lifetime of the store |
| // statically. This encapsulates thread-specific DOM data for the main |
| // thread. All the maps in it are static. This is because we are unable to |
| // rely on WTF::ThreadSpecificThreadExit to do the cleanup since the place that |
| // tears down the main thread can not call any WTF functions. |
| class StaticDOMDataStore : public DOMDataStore { |
| public: |
| StaticDOMDataStore(DOMData* domData) |
| : DOMDataStore(domData) |
| , m_staticDomNodeMap(domData, weakNodeCallback) |
| , m_staticDomObjectMap(domData, weakDOMObjectCallback) |
| , m_staticActiveDomObjectMap(domData, weakActiveDOMObjectCallback) |
| #if ENABLE(SVG) |
| , m_staticDomSvgElementInstanceMap(domData, weakSVGElementInstanceCallback) |
| , m_staticDomSvgObjectWithContextMap(domData, weakSVGObjectWithContextCallback) |
| #endif |
| { |
| m_domNodeMap = &m_staticDomNodeMap; |
| m_domObjectMap = &m_staticDomObjectMap; |
| m_activeDomObjectMap = &m_staticActiveDomObjectMap; |
| #if ENABLE(SVG) |
| m_domSvgElementInstanceMap = &m_staticDomSvgElementInstanceMap; |
| m_domSvgObjectWithContextMap = &m_staticDomSvgObjectWithContextMap; |
| #endif |
| } |
| |
| private: |
| InternalDOMWrapperMap<Node> m_staticDomNodeMap; |
| InternalDOMWrapperMap<void> m_staticDomObjectMap; |
| InternalDOMWrapperMap<void> m_staticActiveDomObjectMap; |
| InternalDOMWrapperMap<SVGElementInstance> m_staticDomSvgElementInstanceMap; |
| InternalDOMWrapperMap<void> m_staticDomSvgObjectWithContextMap; |
| }; |
| |
| typedef WTF::Vector<DOMDataStore*> DOMDataStoreList; |
| |
| // DOMData |
| // |
| // DOMData represents the all the DOM wrappers for a given thread. In |
| // particular, DOMData holds wrappers for all the isolated worlds in the |
| // thread. The DOMData for the main thread and the DOMData for child threads |
| // use different subclasses. |
| // |
| class DOMData: Noncopyable { |
| public: |
| DOMData() |
| : m_delayedProcessingScheduled(false) |
| , m_isMainThread(WTF::isMainThread()) |
| , m_owningThread(WTF::currentThread()) |
| { |
| } |
| |
| static DOMData* getCurrent(); |
| virtual DOMDataStore& getStore() = 0; |
| |
| template<typename T> |
| static void handleWeakObject(DOMDataStore::DOMWrapperMapType mapType, v8::Handle<v8::Object> v8Object, T* domObject); |
| |
| void forgetDelayedObject(void* object) { m_delayedObjectMap.take(object); } |
| |
| // This is to ensure that we will deref DOM objects from the owning thread, |
| // not the GC thread. The helper function will be scheduled by the GC |
| // thread to get called from the owning thread. |
| static void derefDelayedObjectsInCurrentThread(void*); |
| void derefDelayedObjects(); |
| |
| template<typename T> |
| static void removeObjectsFromWrapperMap(DOMWrapperMap<T>& domMap); |
| |
| ThreadIdentifier owningThread() const { return m_owningThread; } |
| |
| private: |
| typedef WTF::HashMap<void*, V8ClassIndex::V8WrapperType> DelayedObjectMap; |
| |
| void ensureDeref(V8ClassIndex::V8WrapperType type, void* domObject); |
| static void derefObject(V8ClassIndex::V8WrapperType type, void* domObject); |
| |
| // Stores all the DOM objects that are delayed to be processed when the owning thread gains control. |
| DelayedObjectMap m_delayedObjectMap; |
| |
| // The flag to indicate if the task to do the delayed process has already been posted. |
| bool m_delayedProcessingScheduled; |
| |
| bool m_isMainThread; |
| ThreadIdentifier m_owningThread; |
| }; |
| |
| class MainThreadDOMData : public DOMData { |
| public: |
| MainThreadDOMData() : m_defaultStore(this) { } |
| |
| DOMDataStore& getStore() |
| { |
| ASSERT(WTF::isMainThread()); |
| V8IsolatedWorld* world = V8IsolatedWorld::getEntered(); |
| if (world) |
| return *world->getDOMDataStore(); |
| return m_defaultStore; |
| } |
| |
| private: |
| StaticDOMDataStore m_defaultStore; |
| // Note: The DOMDataStores for isolated world are owned by the world object. |
| }; |
| |
| class ChildThreadDOMData : public DOMData { |
| public: |
| ChildThreadDOMData() : m_defaultStore(this) { } |
| |
| DOMDataStore& getStore() { |
| ASSERT(!WTF::isMainThread()); |
| // Currently, child threads have only one world. |
| return m_defaultStore; |
| } |
| |
| private: |
| ScopedDOMDataStore m_defaultStore; |
| }; |
| |
| DOMDataStore::DOMDataStore(DOMData* domData) |
| : m_domNodeMap(0) |
| , m_domObjectMap(0) |
| , m_activeDomObjectMap(0) |
| #if ENABLE(SVG) |
| , m_domSvgElementInstanceMap(0) |
| , m_domSvgObjectWithContextMap(0) |
| #endif |
| , m_domData(domData) |
| { |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| DOMDataStore::allStores().append(this); |
| } |
| |
| DOMDataStore::~DOMDataStore() |
| { |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| DOMDataStore::allStores().remove(DOMDataStore::allStores().find(this)); |
| } |
| |
| DOMDataStoreHandle::DOMDataStoreHandle() |
| : m_store(new ScopedDOMDataStore(DOMData::getCurrent())) |
| { |
| } |
| |
| DOMDataStoreHandle::~DOMDataStoreHandle() |
| { |
| } |
| |
| template <class KeyType> |
| void DOMDataStore::InternalDOMWrapperMap<KeyType>::forget(KeyType* object) |
| { |
| DOMWrapperMap<KeyType>::forget(object); |
| m_domData->forgetDelayedObject(object); |
| } |
| |
| DOMWrapperMap<Node>& getDOMNodeMap() |
| { |
| return DOMData::getCurrent()->getStore().domNodeMap(); |
| } |
| |
| DOMWrapperMap<void>& getDOMObjectMap() |
| { |
| return DOMData::getCurrent()->getStore().domObjectMap(); |
| } |
| |
| DOMWrapperMap<void>& getActiveDOMObjectMap() |
| { |
| return DOMData::getCurrent()->getStore().activeDomObjectMap(); |
| } |
| |
| #if ENABLE(SVG) |
| |
| DOMWrapperMap<SVGElementInstance>& getDOMSVGElementInstanceMap() |
| { |
| return DOMData::getCurrent()->getStore().domSvgElementInstanceMap(); |
| } |
| |
| // Map of SVG objects with contexts to V8 objects |
| DOMWrapperMap<void>& getDOMSVGObjectWithContextMap() |
| { |
| return DOMData::getCurrent()->getStore().domSvgObjectWithContextMap(); |
| } |
| |
| #endif // ENABLE(SVG) |
| |
| // static |
| DOMData* DOMData::getCurrent() |
| { |
| if (WTF::isMainThread()) { |
| DEFINE_STATIC_LOCAL(MainThreadDOMData, mainThreadDOMData, ()); |
| return &mainThreadDOMData; |
| } |
| DEFINE_STATIC_LOCAL(WTF::ThreadSpecific<ChildThreadDOMData>, childThreadDOMData, ()); |
| return childThreadDOMData; |
| } |
| |
| // Called when the dead object is not in GC thread's map. Go through all thread maps to find the one containing it. |
| // Then clear the JS reference and push the DOM object into the delayed queue for it to be deref-ed at later time from the owning thread. |
| // * This is called when the GC thread is not the owning thread. |
| // * This can be called on any thread that has GC running. |
| // * Only one V8 instance is running at a time due to V8::Locker. So we don't need to worry about concurrency. |
| template<typename T> |
| // static |
| void DOMData::handleWeakObject(DOMDataStore::DOMWrapperMapType mapType, v8::Handle<v8::Object> v8Object, T* domObject) |
| { |
| |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| DOMDataList& list = DOMDataStore::allStores(); |
| for (size_t i = 0; i < list.size(); ++i) { |
| DOMDataStore* store = list[i]; |
| |
| DOMDataStore::InternalDOMWrapperMap<T>* domMap = static_cast<DOMDataStore::InternalDOMWrapperMap<T>*>(store->getDOMWrapperMap(mapType)); |
| |
| v8::Handle<v8::Object> wrapper = domMap->get(domObject); |
| if (*wrapper == *v8Object) { |
| // Clear the JS reference. |
| domMap->forgetOnly(domObject); |
| store->domData()->ensureDeref(V8DOMWrapper::domWrapperType(v8Object), domObject); |
| } |
| } |
| } |
| |
| void DOMData::ensureDeref(V8ClassIndex::V8WrapperType type, void* domObject) |
| { |
| if (m_owningThread == WTF::currentThread()) { |
| // No need to delay the work. We can deref right now. |
| derefObject(type, domObject); |
| return; |
| } |
| |
| // We need to do the deref on the correct thread. |
| m_delayedObjectMap.set(domObject, type); |
| |
| // Post a task to the owning thread in order to process the delayed queue. |
| // FIXME: For now, we can only post to main thread due to WTF task posting limitation. We will fix this when we work on nested worker. |
| if (!m_delayedProcessingScheduled) { |
| m_delayedProcessingScheduled = true; |
| if (isMainThread()) |
| WTF::callOnMainThread(&derefDelayedObjectsInCurrentThread, 0); |
| } |
| } |
| |
| // Called when the object is near death (not reachable from JS roots). |
| // It is time to remove the entry from the table and dispose the handle. |
| static void weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject) |
| { |
| v8::HandleScope scope; |
| ASSERT(v8Object->IsObject()); |
| DOMData::handleWeakObject(DOMDataStore::DOMObjectMap, v8::Handle<v8::Object>::Cast(v8Object), domObject); |
| } |
| |
| void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject) |
| { |
| v8::HandleScope scope; |
| ASSERT(v8Object->IsObject()); |
| DOMData::handleWeakObject(DOMDataStore::ActiveDOMObjectMap, v8::Handle<v8::Object>::Cast(v8Object), domObject); |
| } |
| |
| static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject) |
| { |
| v8::HandleScope scope; |
| ASSERT(v8Object->IsObject()); |
| DOMData::handleWeakObject<Node>(DOMDataStore::DOMNodeMap, v8::Handle<v8::Object>::Cast(v8Object), static_cast<Node*>(domObject)); |
| } |
| |
| #if ENABLE(SVG) |
| |
| static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject) |
| { |
| v8::HandleScope scope; |
| ASSERT(v8Object->IsObject()); |
| DOMData::handleWeakObject(DOMDataStore::DOMSVGElementInstanceMap, v8::Handle<v8::Object>::Cast(v8Object), static_cast<SVGElementInstance*>(domObject)); |
| } |
| |
| static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object, void* domObject) |
| { |
| v8::HandleScope scope; |
| ASSERT(v8Object->IsObject()); |
| DOMData::handleWeakObject(DOMDataStore::DOMSVGObjectWithContextMap, v8::Handle<v8::Object>::Cast(v8Object), domObject); |
| } |
| |
| #endif // ENABLE(SVG) |
| |
| // static |
| void DOMData::derefObject(V8ClassIndex::V8WrapperType type, void* domObject) |
| { |
| switch (type) { |
| case V8ClassIndex::NODE: |
| static_cast<Node*>(domObject)->deref(); |
| break; |
| |
| #define MakeCase(type, name) \ |
| case V8ClassIndex::type: static_cast<name*>(domObject)->deref(); break; |
| DOM_OBJECT_TYPES(MakeCase) // This includes both active and non-active. |
| #undef MakeCase |
| |
| #if ENABLE(SVG) |
| #define MakeCase(type, name) \ |
| case V8ClassIndex::type: static_cast<name*>(domObject)->deref(); break; |
| SVG_OBJECT_TYPES(MakeCase) // This also includes SVGElementInstance. |
| #undef MakeCase |
| |
| #define MakeCase(type, name) \ |
| case V8ClassIndex::type: \ |
| static_cast<V8SVGPODTypeWrapper<name>*>(domObject)->deref(); break; |
| SVG_POD_NATIVE_TYPES(MakeCase) |
| #undef MakeCase |
| #endif |
| |
| default: |
| ASSERT_NOT_REACHED(); |
| break; |
| } |
| } |
| |
| void DOMData::derefDelayedObjects() |
| { |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| |
| m_delayedProcessingScheduled = false; |
| |
| for (DelayedObjectMap::iterator iter(m_delayedObjectMap.begin()); iter != m_delayedObjectMap.end(); ++iter) |
| derefObject(iter->second, iter->first); |
| |
| m_delayedObjectMap.clear(); |
| } |
| |
| // static |
| void DOMData::derefDelayedObjectsInCurrentThread(void*) |
| { |
| getCurrent()->derefDelayedObjects(); |
| } |
| |
| // static |
| template<typename T> |
| void DOMData::removeObjectsFromWrapperMap(DOMWrapperMap<T>& domMap) |
| { |
| for (typename WTF::HashMap<T*, v8::Object*>::iterator iter(domMap.impl().begin()); iter != domMap.impl().end(); ++iter) { |
| T* domObject = static_cast<T*>(iter->first); |
| v8::Persistent<v8::Object> v8Object(iter->second); |
| |
| V8ClassIndex::V8WrapperType type = V8DOMWrapper::domWrapperType(v8::Handle<v8::Object>::Cast(v8Object)); |
| |
| // Deref the DOM object. |
| derefObject(type, domObject); |
| |
| // Clear the JS wrapper. |
| v8Object.Dispose(); |
| } |
| domMap.impl().clear(); |
| } |
| |
| static void removeAllDOMObjectsInCurrentThreadHelper() |
| { |
| v8::HandleScope scope; |
| |
| // Deref all objects in the delayed queue. |
| DOMData::getCurrent()->derefDelayedObjects(); |
| |
| // Remove all DOM nodes. |
| DOMData::removeObjectsFromWrapperMap<Node>(getDOMNodeMap()); |
| |
| // Remove all DOM objects in the wrapper map. |
| DOMData::removeObjectsFromWrapperMap<void>(getDOMObjectMap()); |
| |
| // Remove all active DOM objects in the wrapper map. |
| DOMData::removeObjectsFromWrapperMap<void>(getActiveDOMObjectMap()); |
| |
| #if ENABLE(SVG) |
| // Remove all SVG element instances in the wrapper map. |
| DOMData::removeObjectsFromWrapperMap<SVGElementInstance>(getDOMSVGElementInstanceMap()); |
| |
| // Remove all SVG objects with context in the wrapper map. |
| DOMData::removeObjectsFromWrapperMap<void>(getDOMSVGObjectWithContextMap()); |
| #endif |
| } |
| |
| void removeAllDOMObjectsInCurrentThread() |
| { |
| // Use the locker only if it has already been invoked before, as by worker thread. |
| if (v8::Locker::IsActive()) { |
| v8::Locker locker; |
| removeAllDOMObjectsInCurrentThreadHelper(); |
| } else |
| removeAllDOMObjectsInCurrentThreadHelper(); |
| } |
| |
| |
| void visitDOMNodesInCurrentThread(DOMWrapperMap<Node>::Visitor* visitor) |
| { |
| v8::HandleScope scope; |
| |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| DOMDataList& list = DOMDataStore::allStores(); |
| for (size_t i = 0; i < list.size(); ++i) { |
| DOMDataStore* store = list[i]; |
| if (!store->domData()->owningThread() == WTF::currentThread()) |
| continue; |
| |
| HashMap<Node*, v8::Object*>& map = store->domNodeMap().impl(); |
| for (HashMap<Node*, v8::Object*>::iterator it = map.begin(); it != map.end(); ++it) |
| visitor->visitDOMWrapper(it->first, v8::Persistent<v8::Object>(it->second)); |
| } |
| } |
| |
| void visitDOMObjectsInCurrentThread(DOMWrapperMap<void>::Visitor* visitor) |
| { |
| v8::HandleScope scope; |
| |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| DOMDataList& list = DOMDataStore::allStores(); |
| for (size_t i = 0; i < list.size(); ++i) { |
| DOMDataStore* store = list[i]; |
| if (!store->domData()->owningThread() == WTF::currentThread()) |
| continue; |
| |
| HashMap<void*, v8::Object*> & map = store->domObjectMap().impl(); |
| for (HashMap<void*, v8::Object*>::iterator it = map.begin(); it != map.end(); ++it) |
| visitor->visitDOMWrapper(it->first, v8::Persistent<v8::Object>(it->second)); |
| } |
| } |
| |
| void visitActiveDOMObjectsInCurrentThread(DOMWrapperMap<void>::Visitor* visitor) |
| { |
| v8::HandleScope scope; |
| |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| DOMDataList& list = DOMDataStore::allStores(); |
| for (size_t i = 0; i < list.size(); ++i) { |
| DOMDataStore* store = list[i]; |
| if (!store->domData()->owningThread() == WTF::currentThread()) |
| continue; |
| |
| HashMap<void*, v8::Object*>& map = store->activeDomObjectMap().impl(); |
| for (HashMap<void*, v8::Object*>::iterator it = map.begin(); it != map.end(); ++it) |
| visitor->visitDOMWrapper(it->first, v8::Persistent<v8::Object>(it->second)); |
| } |
| } |
| |
| #if ENABLE(SVG) |
| |
| void visitDOMSVGElementInstancesInCurrentThread(DOMWrapperMap<SVGElementInstance>::Visitor* visitor) |
| { |
| v8::HandleScope scope; |
| |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| DOMDataList& list = DOMDataStore::allStores(); |
| for (size_t i = 0; i < list.size(); ++i) { |
| DOMDataStore* store = list[i]; |
| if (!store->domData()->owningThread() == WTF::currentThread()) |
| continue; |
| |
| HashMap<SVGElementInstance*, v8::Object*> & map = store->domSvgElementInstanceMap().impl(); |
| for (HashMap<SVGElementInstance*, v8::Object*>::iterator it = map.begin(); it != map.end(); ++it) |
| visitor->visitDOMWrapper(it->first, v8::Persistent<v8::Object>(it->second)); |
| } |
| } |
| |
| void visitSVGObjectsInCurrentThread(DOMWrapperMap<void>::Visitor* visitor) |
| { |
| v8::HandleScope scope; |
| |
| WTF::MutexLocker locker(DOMDataStore::allStoresMutex()); |
| DOMDataList& list = DOMDataStore::allStores(); |
| for (size_t i = 0; i < list.size(); ++i) { |
| DOMDataStore* store = list[i]; |
| if (!store->domData()->owningThread() == WTF::currentThread()) |
| continue; |
| |
| HashMap<void*, v8::Object*>& map = store->domSvgObjectWithContextMap().impl(); |
| for (HashMap<void*, v8::Object*>::iterator it = map.begin(); it != map.end(); ++it) |
| visitor->visitDOMWrapper(it->first, v8::Persistent<v8::Object>(it->second)); |
| } |
| } |
| |
| #endif |
| |
| } // namespace WebCore |