#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 {
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);
static void weakSVGObjectWithContextCallback(v8::Persistent<v8::Value> v8Object, void* domObject);
#endif
class DOMData;
class DOMDataStore;
typedef WTF::Vector<DOMDataStore*> DOMDataList;
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;
};
static DOMDataList& allStores()
{
DEFINE_STATIC_LOCAL(DOMDataList, staticDOMDataList, ());
return staticDOMDataList;
}
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:
DOMData* m_domData;
};
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
}
virtual ~ScopedDOMDataStore()
{
delete m_domNodeMap;
delete m_domObjectMap;
delete m_activeDomObjectMap;
#if ENABLE(SVG)
delete m_domSvgElementInstanceMap;
delete m_domSvgObjectWithContextMap;
#endif
}
};
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;
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); }
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);
DelayedObjectMap m_delayedObjectMap;
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;
};
class ChildThreadDOMData : public DOMData {
public:
ChildThreadDOMData() : m_defaultStore(this) { }
DOMDataStore& getStore() {
ASSERT(!WTF::isMainThread());
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();
}
DOMWrapperMap<void>& getDOMSVGObjectWithContextMap()
{
return DOMData::getCurrent()->getStore().domSvgObjectWithContextMap();
}
#endif // ENABLE(SVG)
DOMData* DOMData::getCurrent()
{
if (WTF::isMainThread()) {
DEFINE_STATIC_LOCAL(MainThreadDOMData, mainThreadDOMData, ());
return &mainThreadDOMData;
}
DEFINE_STATIC_LOCAL(WTF::ThreadSpecific<ChildThreadDOMData>, childThreadDOMData, ());
return childThreadDOMData;
}
template<typename T>
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) {
domMap->forgetOnly(domObject);
store->domData()->ensureDeref(V8DOMWrapper::domWrapperType(v8Object), domObject);
}
}
}
void DOMData::ensureDeref(V8ClassIndex::V8WrapperType type, void* domObject)
{
if (m_owningThread == WTF::currentThread()) {
derefObject(type, domObject);
return;
}
m_delayedObjectMap.set(domObject, type);
if (!m_delayedProcessingScheduled) {
m_delayedProcessingScheduled = true;
if (isMainThread())
WTF::callOnMainThread(&derefDelayedObjectsInCurrentThread, 0);
}
}
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)
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) #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;
}
}
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();
}
void DOMData::derefDelayedObjectsInCurrentThread(void*)
{
getCurrent()->derefDelayedObjects();
}
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));
derefObject(type, domObject);
v8Object.Dispose();
}
domMap.impl().clear();
}
static void removeAllDOMObjectsInCurrentThreadHelper()
{
v8::HandleScope scope;
DOMData::getCurrent()->derefDelayedObjects();
DOMData::removeObjectsFromWrapperMap<Node>(getDOMNodeMap());
DOMData::removeObjectsFromWrapperMap<void>(getDOMObjectMap());
DOMData::removeObjectsFromWrapperMap<void>(getActiveDOMObjectMap());
#if ENABLE(SVG)
DOMData::removeObjectsFromWrapperMap<SVGElementInstance>(getDOMSVGElementInstanceMap());
DOMData::removeObjectsFromWrapperMap<void>(getDOMSVGObjectWithContextMap());
#endif
}
void removeAllDOMObjectsInCurrentThread()
{
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
}