#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>
namespace WebCore {
static void weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
#if ENABLE(SVG)
static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
#endif
static void derefDelayedObjectsInCurrentThread(void*);
class ThreadSpecificDOMData;
typedef WTF::Vector<ThreadSpecificDOMData*> DOMDataList;
static DOMDataList& domDataList()
{
DEFINE_STATIC_LOCAL(DOMDataList, staticDOMDataList, ());
return staticDOMDataList;
}
static WTF::Mutex& domDataListMutex()
{
DEFINE_STATIC_LOCAL(WTF::Mutex, staticDOMDataListMutex, ());
return staticDOMDataListMutex;
}
class ThreadSpecificDOMData : Noncopyable {
public:
enum DOMWrapperMapType {
DOMNodeMap,
DOMObjectMap,
ActiveDOMObjectMap,
#if ENABLE(SVG)
DOMSVGElementInstanceMap,
DOMSVGObjectWithContextMap
#endif
};
typedef WTF::HashMap<void*, V8ClassIndex::V8WrapperType> DelayedObjectMap;
template <class KeyType>
class InternalDOMWrapperMap : public DOMWrapperMap<KeyType> {
public:
InternalDOMWrapperMap(v8::WeakReferenceCallback callback)
: DOMWrapperMap<KeyType>(callback) { }
virtual void forget(KeyType*);
void forgetOnly(KeyType* object)
{
DOMWrapperMap<KeyType>::forget(object);
}
};
ThreadSpecificDOMData()
: m_domNodeMap(0)
, m_domObjectMap(0)
, m_activeDomObjectMap(0)
#if ENABLE(SVG)
, m_domSvgElementInstanceMap(0)
, m_domSvgObjectWithContextMap(0)
#endif
, m_delayedProcessingScheduled(false)
, m_isMainThread(WTF::isMainThread())
{
WTF::MutexLocker locker(domDataListMutex());
domDataList().append(this);
}
virtual ~ThreadSpecificDOMData()
{
WTF::MutexLocker locker(domDataListMutex());
domDataList().remove(domDataList().find(this));
}
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
DelayedObjectMap& delayedObjectMap() { return m_delayedObjectMap; }
bool delayedProcessingScheduled() const { return m_delayedProcessingScheduled; }
void setDelayedProcessingScheduled(bool value) { m_delayedProcessingScheduled = value; }
bool isMainThread() const { return m_isMainThread; }
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
DelayedObjectMap m_delayedObjectMap;
bool m_delayedProcessingScheduled;
bool m_isMainThread;
};
class NonMainThreadSpecificDOMData : public ThreadSpecificDOMData {
public:
NonMainThreadSpecificDOMData()
{
m_domNodeMap = new InternalDOMWrapperMap<Node>(&weakNodeCallback);
m_domObjectMap = new InternalDOMWrapperMap<void>(weakDOMObjectCallback);
m_activeDomObjectMap = new InternalDOMWrapperMap<void>(weakActiveDOMObjectCallback);
#if ENABLE(SVG)
m_domSvgElementInstanceMap = new InternalDOMWrapperMap<SVGElementInstance>(weakSVGElementInstanceCallback);
m_domSvgObjectWithContextMap = new InternalDOMWrapperMap<void>(weakSVGObjectWithContextCallback);
#endif
}
virtual ~NonMainThreadSpecificDOMData()
{
delete m_domNodeMap;
delete m_domObjectMap;
delete m_activeDomObjectMap;
#if ENABLE(SVG)
delete m_domSvgElementInstanceMap;
delete m_domSvgObjectWithContextMap;
#endif
}
};
class MainThreadSpecificDOMData : public ThreadSpecificDOMData {
public:
MainThreadSpecificDOMData()
: m_staticDomNodeMap(weakNodeCallback)
, m_staticDomObjectMap(weakDOMObjectCallback)
, m_staticActiveDomObjectMap(weakActiveDOMObjectCallback)
#if ENABLE(SVG)
, m_staticDomSvgElementInstanceMap(weakSVGElementInstanceCallback)
, m_staticDomSvgObjectWithContextMap(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;
};
DEFINE_STATIC_LOCAL(WTF::ThreadSpecific<NonMainThreadSpecificDOMData>, threadSpecificDOMData, ());
template<typename T>
static void handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMWrapperMapType mapType, V8ClassIndex::V8WrapperType objectType, T*);
ThreadSpecificDOMData& getThreadSpecificDOMData()
{
if (WTF::isMainThread()) {
DEFINE_STATIC_LOCAL(MainThreadSpecificDOMData, mainThreadSpecificDOMData, ());
return mainThreadSpecificDOMData;
}
return *threadSpecificDOMData;
}
template <class KeyType>
void ThreadSpecificDOMData::InternalDOMWrapperMap<KeyType>::forget(KeyType* object)
{
DOMWrapperMap<KeyType>::forget(object);
ThreadSpecificDOMData::DelayedObjectMap& delayedObjectMap = getThreadSpecificDOMData().delayedObjectMap();
delayedObjectMap.take(object);
}
DOMWrapperMap<Node>& getDOMNodeMap()
{
return getThreadSpecificDOMData().domNodeMap();
}
DOMWrapperMap<void>& getDOMObjectMap()
{
return getThreadSpecificDOMData().domObjectMap();
}
DOMWrapperMap<void>& getActiveDOMObjectMap()
{
return getThreadSpecificDOMData().activeDomObjectMap();
}
#if ENABLE(SVG)
DOMWrapperMap<SVGElementInstance>& getDOMSVGElementInstanceMap()
{
return getThreadSpecificDOMData().domSvgElementInstanceMap();
}
static void weakSVGElementInstanceCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
{
SVGElementInstance* instance = static_cast<SVGElementInstance*>(domObject);
ThreadSpecificDOMData::InternalDOMWrapperMap<SVGElementInstance>& map = static_cast<ThreadSpecificDOMData::InternalDOMWrapperMap<SVGElementInstance>&>(getDOMSVGElementInstanceMap());
if (map.contains(instance)) {
instance->deref();
map.forgetOnly(instance);
} else
handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMSVGElementInstanceMap, V8ClassIndex::SVGELEMENTINSTANCE, instance);
}
DOMWrapperMap<void>& getDOMSVGObjectWithContextMap()
{
return getThreadSpecificDOMData().domSvgObjectWithContextMap();
}
static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
{
v8::HandleScope scope;
ASSERT(v8Object->IsObject());
V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8::Object>::Cast(v8Object));
ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<ThreadSpecificDOMData::InternalDOMWrapperMap<void>&>(getDOMSVGObjectWithContextMap());
if (map.contains(domObject)) {
map.forgetOnly(domObject);
switch (type) {
#define MakeCase(type, name) \
case V8ClassIndex::type: static_cast<name*>(domObject)->deref(); break;
SVG_OBJECT_TYPES(MakeCase)
#undef MakeCase
#define MakeCase(type, name) \
case V8ClassIndex::type: \
static_cast<V8SVGPODTypeWrapper<name>*>(domObject)->deref(); break;
SVG_POD_NATIVE_TYPES(MakeCase)
#undef MakeCase
default:
ASSERT_NOT_REACHED();
break;
}
} else
handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMSVGObjectWithContextMap, type, domObject);
}
#endif // ENABLE(SVG)
template<typename T>
static void handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMWrapperMapType mapType, V8ClassIndex::V8WrapperType objectType, T* object)
{
WTF::MutexLocker locker(domDataListMutex());
DOMDataList& list = domDataList();
for (size_t i = 0; i < list.size(); ++i) {
ThreadSpecificDOMData* threadData = list[i];
ThreadSpecificDOMData::InternalDOMWrapperMap<T>* domMap = static_cast<ThreadSpecificDOMData::InternalDOMWrapperMap<T>*>(threadData->getDOMWrapperMap(mapType));
if (domMap->contains(object)) {
domMap->forgetOnly(object);
threadData->delayedObjectMap().set(object, objectType);
if (!threadData->delayedProcessingScheduled()) {
threadData->setDelayedProcessingScheduled(true);
if (threadData->isMainThread())
WTF::callOnMainThread(&derefDelayedObjectsInCurrentThread, 0);
}
break;
}
}
}
static void weakDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
{
v8::HandleScope scope;
ASSERT(v8Object->IsObject());
V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8::Object>::Cast(v8Object));
ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<ThreadSpecificDOMData::InternalDOMWrapperMap<void>&>(getDOMObjectMap());
if (map.contains(domObject)) {
map.forgetOnly(domObject);
switch (type) {
#define MakeCase(type, name) \
case V8ClassIndex::type: static_cast<name*>(domObject)->deref(); break;
DOM_OBJECT_TYPES(MakeCase)
#undef MakeCase
default:
ASSERT_NOT_REACHED();
break;
}
} else
handleWeakObjectInOwningThread(ThreadSpecificDOMData::DOMObjectMap, type, domObject);
}
void weakActiveDOMObjectCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
{
v8::HandleScope scope;
ASSERT(v8Object->IsObject());
V8ClassIndex::V8WrapperType type = V8Proxy::GetDOMWrapperType(v8::Handle<v8::Object>::Cast(v8Object));
ThreadSpecificDOMData::InternalDOMWrapperMap<void>& map = static_cast<ThreadSpecificDOMData::InternalDOMWrapperMap<void>&>(getActiveDOMObjectMap());
if (map.contains(domObject)) {
map.forgetOnly(domObject);
switch (type) {
#define MakeCase(type, name) \
case V8ClassIndex::type: static_cast<name*>(domObject)->deref(); break;
ACTIVE_DOM_OBJECT_TYPES(MakeCase)
#undef MakeCase
default:
ASSERT_NOT_REACHED();
break;
}
} else
handleWeakObjectInOwningThread(ThreadSpecificDOMData::ActiveDOMObjectMap, type, domObject);
}
static void weakNodeCallback(v8::Persistent<v8::Value> v8Object, void* domObject)
{
Node* node = static_cast<Node*>(domObject);
ThreadSpecificDOMData::InternalDOMWrapperMap<Node>& map = static_cast<ThreadSpecificDOMData::InternalDOMWrapperMap<Node>&>(getDOMNodeMap());
if (map.contains(node)) {
map.forgetOnly(node);
node->deref();
} else
handleWeakObjectInOwningThread<Node>(ThreadSpecificDOMData::DOMNodeMap, V8ClassIndex::NODE, node);
}
static void 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) #undef MakeCase
#if ENABLE(SVG)
#define MakeCase(type, name) \
case V8ClassIndex::type: static_cast<name*>(domObject)->deref(); break;
SVG_OBJECT_TYPES(MakeCase) #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;
}
}
static void derefDelayedObjects()
{
WTF::MutexLocker locker(domDataListMutex());
getThreadSpecificDOMData().setDelayedProcessingScheduled(false);
ThreadSpecificDOMData::DelayedObjectMap& delayedObjectMap = getThreadSpecificDOMData().delayedObjectMap();
for (ThreadSpecificDOMData::DelayedObjectMap::iterator iter(delayedObjectMap.begin()); iter != delayedObjectMap.end(); ++iter) {
derefObject(iter->second, iter->first);
}
delayedObjectMap.clear();
}
static void derefDelayedObjectsInCurrentThread(void*)
{
derefDelayedObjects();
}
template<typename T>
static void 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 = V8Proxy::GetDOMWrapperType(v8::Handle<v8::Object>::Cast(v8Object));
derefObject(type, domObject);
v8Object.Dispose();
}
domMap.impl().clear();
}
static void removeAllDOMObjectsInCurrentThreadHelper()
{
v8::HandleScope scope;
derefDelayedObjects();
removeObjectsFromWrapperMap<Node>(getDOMNodeMap());
removeObjectsFromWrapperMap<void>(getDOMObjectMap());
removeObjectsFromWrapperMap<void>(getActiveDOMObjectMap());
#if ENABLE(SVG)
removeObjectsFromWrapperMap<SVGElementInstance>(getDOMSVGElementInstanceMap());
removeObjectsFromWrapperMap<void>(getDOMSVGObjectWithContextMap());
#endif
}
void removeAllDOMObjectsInCurrentThread()
{
if (v8::Locker::IsActive()) {
v8::Locker locker;
removeAllDOMObjectsInCurrentThreadHelper();
} else
removeAllDOMObjectsInCurrentThreadHelper();
}
}