summaryrefslogtreecommitdiffstats
path: root/WebCore/bindings/js/JSDOMBinding.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/bindings/js/JSDOMBinding.cpp')
-rw-r--r--WebCore/bindings/js/JSDOMBinding.cpp223
1 files changed, 135 insertions, 88 deletions
diff --git a/WebCore/bindings/js/JSDOMBinding.cpp b/WebCore/bindings/js/JSDOMBinding.cpp
index f12c779..04b6dc9 100644
--- a/WebCore/bindings/js/JSDOMBinding.cpp
+++ b/WebCore/bindings/js/JSDOMBinding.cpp
@@ -49,9 +49,11 @@
#include "ScriptController.h"
#include "Settings.h"
#include "XMLHttpRequestException.h"
+#include <runtime/DateInstance.h>
#include <runtime/Error.h>
#include <runtime/JSFunction.h>
#include <runtime/PrototypeFunction.h>
+#include <wtf/MathExtras.h>
#include <wtf/StdLibExtras.h>
#if ENABLE(SVG)
@@ -78,30 +80,32 @@ using namespace HTMLNames;
typedef Document::JSWrapperCache JSWrapperCache;
typedef Document::JSWrapperCacheMap JSWrapperCacheMap;
-// For debugging, keep a set of wrappers currently registered, and check that
-// all are unregistered before they are destroyed. This has helped us fix at
-// least one bug.
-
-static void addWrapper(DOMObject* wrapper);
-static void removeWrapper(DOMObject* wrapper);
-static void removeWrappers(const JSWrapperCache& wrappers);
-static void removeWrappers(const DOMObjectWrapperMap& wrappers);
-
-#ifdef NDEBUG
-
-static inline void addWrapper(DOMObject*)
+inline JSWrapperCache* Document::getWrapperCache(DOMWrapperWorld* world)
{
+ if (world->isNormal()) {
+ if (JSWrapperCache* wrapperCache = m_normalWorldWrapperCache)
+ return wrapperCache;
+ ASSERT(!m_wrapperCacheMap.contains(world));
+ } else if (JSWrapperCache* wrapperCache = m_wrapperCacheMap.get(world))
+ return wrapperCache;
+ return createWrapperCache(world);
}
-static inline void removeWrapper(DOMObject*)
-{
-}
+// For debugging, keep a set of wrappers currently cached, and check that
+// all are uncached before they are destroyed. This helps us catch bugs like:
+// - wrappers being deleted without being removed from the cache
+// - wrappers being cached twice
+
+static void willCacheWrapper(DOMObject* wrapper);
+static void didUncacheWrapper(DOMObject* wrapper);
+
+#ifdef NDEBUG
-static inline void removeWrappers(const JSWrapperCache&)
+static inline void willCacheWrapper(DOMObject*)
{
}
-static inline void removeWrappers(const DOMObjectWrapperMap&)
+static inline void didUncacheWrapper(DOMObject*)
{
}
@@ -118,13 +122,13 @@ static HashSet<DOMObject*>& wrapperSet()
#endif
}
-static void addWrapper(DOMObject* wrapper)
+static void willCacheWrapper(DOMObject* wrapper)
{
ASSERT(!wrapperSet().contains(wrapper));
wrapperSet().add(wrapper);
}
-static void removeWrapper(DOMObject* wrapper)
+static void didUncacheWrapper(DOMObject* wrapper)
{
if (!wrapper)
return;
@@ -132,20 +136,6 @@ static void removeWrapper(DOMObject* wrapper)
wrapperSet().remove(wrapper);
}
-static void removeWrappers(const JSWrapperCache& wrappers)
-{
- JSWrapperCache::const_iterator wrappersEnd = wrappers.end();
- for (JSWrapperCache::const_iterator it = wrappers.begin(); it != wrappersEnd; ++it)
- removeWrapper(it->second);
-}
-
-static inline void removeWrappers(const DOMObjectWrapperMap& wrappers)
-{
- DOMObjectWrapperMap::const_iterator wrappersEnd = wrappers.end();
- for (DOMObjectWrapperMap::const_iterator it = wrappers.begin(); it != wrappersEnd; ++it)
- removeWrapper(it->second);
-}
-
DOMObject::~DOMObject()
{
ASSERT(!wrapperSet().contains(this));
@@ -153,8 +143,9 @@ DOMObject::~DOMObject()
#endif
-DOMWrapperWorld::DOMWrapperWorld(JSC::JSGlobalData* globalData)
+DOMWrapperWorld::DOMWrapperWorld(JSC::JSGlobalData* globalData, bool isNormal)
: m_globalData(globalData)
+ , m_isNormal(isNormal)
{
}
@@ -164,8 +155,6 @@ DOMWrapperWorld::~DOMWrapperWorld()
ASSERT(clientData);
static_cast<WebCoreJSClientData*>(clientData)->forgetWorld(this);
- removeWrappers(m_wrappers);
-
for (HashSet<Document*>::iterator iter = documentsWithWrappers.begin(); iter != documentsWithWrappers.end(); ++iter)
forgetWorldOfDOMNodesForDocument(*iter, this);
}
@@ -206,11 +195,6 @@ private:
HashSet<DOMWrapperWorld*>::iterator m_end;
};
-DOMWrapperWorld* currentWorld(JSC::ExecState* exec)
-{
- return static_cast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->world();
-}
-
DOMWrapperWorld* normalWorld(JSC::JSGlobalData& globalData)
{
JSGlobalData::ClientData* clientData = globalData.clientData;
@@ -242,10 +226,19 @@ static inline DOMObjectWrapperMap& DOMObjectWrapperMapFor(JSC::ExecState* exec)
return currentWorld(exec)->m_wrappers;
}
+bool hasCachedDOMObjectWrapperUnchecked(JSGlobalData* globalData, void* objectHandle)
+{
+ for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
+ if (worldIter->m_wrappers.uncheckedGet(objectHandle))
+ return true;
+ }
+ return false;
+}
+
bool hasCachedDOMObjectWrapper(JSGlobalData* globalData, void* objectHandle)
{
for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
- if (worldIter->m_wrappers.contains(objectHandle))
+ if (worldIter->m_wrappers.get(objectHandle))
return true;
}
return false;
@@ -258,18 +251,18 @@ DOMObject* getCachedDOMObjectWrapper(JSC::ExecState* exec, void* objectHandle)
void cacheDOMObjectWrapper(JSC::ExecState* exec, void* objectHandle, DOMObject* wrapper)
{
- addWrapper(wrapper);
+ willCacheWrapper(wrapper);
DOMObjectWrapperMapFor(exec).set(objectHandle, wrapper);
}
-bool hasCachedDOMNodeWrapper(Document* document, Node* node)
+bool hasCachedDOMNodeWrapperUnchecked(Document* document, Node* node)
{
if (!document)
- return hasCachedDOMObjectWrapper(JSDOMWindow::commonJSGlobalData(), node);
+ return hasCachedDOMObjectWrapperUnchecked(JSDOMWindow::commonJSGlobalData(), node);
JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) {
- if (iter->second->contains(node))
+ if (iter->second->uncheckedGet(node))
return true;
}
return false;
@@ -285,50 +278,50 @@ JSNode* getCachedDOMNodeWrapper(JSC::ExecState* exec, Document* document, Node*
void forgetDOMObject(DOMObject* wrapper, void* objectHandle)
{
JSC::JSGlobalData* globalData = Heap::heap(wrapper)->globalData();
- for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
- DOMObjectWrapperMap& wrappers = worldIter->m_wrappers;
- DOMObjectWrapperMap::iterator iter = wrappers.find(objectHandle);
- if ((iter != wrappers.end()) && (iter->second == wrapper)) {
- removeWrapper(wrapper);
- wrappers.remove(iter);
- return;
- }
+
+ // Check the normal world first!
+ JSGlobalData::ClientData* clientData = globalData->clientData;
+ ASSERT(clientData);
+ DOMObjectWrapperMap& wrappers = static_cast<WebCoreJSClientData*>(clientData)->normalWorld()->m_wrappers;
+ if (wrappers.uncheckedRemove(objectHandle, wrapper)) {
+ didUncacheWrapper(wrapper);
+ return;
}
- // If the world went away, it should have removed this wrapper from the set.
- ASSERT(!wrapperSet().contains(wrapper));
+ // We can't guarantee that a wrapper is in the cache when it uncaches itself,
+ // since a new wrapper may be cached before the old wrapper's destructor runs.
+ for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
+ if (worldIter->m_wrappers.uncheckedRemove(objectHandle, wrapper))
+ break;
+ }
+ didUncacheWrapper(wrapper);
}
-void forgetDOMNode(DOMObject* wrapper, Node* node, Document* document)
+void forgetDOMNode(JSNode* wrapper, Node* node, Document* document)
{
if (!document) {
forgetDOMObject(wrapper, node);
return;
}
+ // We can't guarantee that a wrapper is in the cache when it uncaches itself,
+ // since a new wrapper may be cached before the old wrapper's destructor runs.
JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
for (JSWrapperCacheMap::iterator wrappersIter = wrapperCacheMap.begin(); wrappersIter != wrapperCacheMap.end(); ++wrappersIter) {
- JSWrapperCache* wrappers = wrappersIter->second;
- JSWrapperCache::iterator iter = wrappers->find(node);
- if ((iter != wrappers->end()) && (iter->second == wrapper)) {
- wrappers->remove(iter);
- removeWrapper(wrapper);
- return;
- }
+ if (wrappersIter->second->uncheckedRemove(node, wrapper))
+ break;
}
-
- // If the world went away, it should have removed this wrapper from the set.
- ASSERT(!wrapperSet().contains(wrapper));
+ didUncacheWrapper(wrapper);
}
void cacheDOMNodeWrapper(JSC::ExecState* exec, Document* document, Node* node, JSNode* wrapper)
{
if (!document) {
- addWrapper(wrapper);
+ willCacheWrapper(wrapper);
DOMObjectWrapperMapFor(exec).set(node, wrapper);
return;
}
- addWrapper(wrapper);
+ willCacheWrapper(wrapper);
document->getWrapperCache(currentWorld(exec))->set(node, wrapper);
}
@@ -338,9 +331,7 @@ void forgetAllDOMNodesForDocument(Document* document)
JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
JSWrapperCacheMap::const_iterator wrappersMapEnd = wrapperCacheMap.end();
for (JSWrapperCacheMap::const_iterator wrappersMapIter = wrapperCacheMap.begin(); wrappersMapIter != wrappersMapEnd; ++wrappersMapIter) {
- JSWrapperCache* wrappers = wrappersMapIter->second;
- removeWrappers(*wrappers);
- delete wrappers;
+ delete wrappersMapIter->second;
wrappersMapIter->first->forgetDocument(document);
}
}
@@ -349,7 +340,6 @@ void forgetWorldOfDOMNodesForDocument(Document* document, DOMWrapperWorld* world
{
JSWrapperCache* wrappers = document->wrapperCacheMap().take(world);
ASSERT(wrappers); // 'world' should only know about 'document' if 'document' knows about 'world'!
- removeWrappers(*wrappers);
delete wrappers;
}
@@ -379,14 +369,14 @@ static inline bool isObservableThroughDOM(JSNode* jsNode, DOMWrapperWorld* world
// the custom markChildren functions rather than here.
if (node->isElementNode()) {
if (NamedNodeMap* attributes = static_cast<Element*>(node)->attributeMap()) {
- if (DOMObject* wrapper = world->m_wrappers.get(attributes)) {
+ if (DOMObject* wrapper = world->m_wrappers.uncheckedGet(attributes)) {
if (wrapper->hasCustomProperties())
return true;
}
}
if (node->isStyledElement()) {
if (CSSMutableStyleDeclaration* style = static_cast<StyledElement*>(node)->inlineStyleDecl()) {
- if (DOMObject* wrapper = world->m_wrappers.get(style)) {
+ if (DOMObject* wrapper = world->m_wrappers.uncheckedGet(style)) {
if (wrapper->hasCustomProperties())
return true;
}
@@ -394,7 +384,7 @@ static inline bool isObservableThroughDOM(JSNode* jsNode, DOMWrapperWorld* world
}
if (static_cast<Element*>(node)->hasTagName(canvasTag)) {
if (CanvasRenderingContext* context = static_cast<HTMLCanvasElement*>(node)->renderingContext()) {
- if (DOMObject* wrapper = world->m_wrappers.get(context)) {
+ if (DOMObject* wrapper = world->m_wrappers.uncheckedGet(context)) {
if (wrapper->hasCustomProperties())
return true;
}
@@ -432,8 +422,8 @@ void markDOMNodesForDocument(MarkStack& markStack, Document* document)
DOMWrapperWorld* world = wrappersIter->first;
JSWrapperCache* nodeDict = wrappersIter->second;
- JSWrapperCache::iterator nodeEnd = nodeDict->end();
- for (JSWrapperCache::iterator nodeIt = nodeDict->begin(); nodeIt != nodeEnd; ++nodeIt) {
+ JSWrapperCache::iterator nodeEnd = nodeDict->uncheckedEnd();
+ for (JSWrapperCache::iterator nodeIt = nodeDict->uncheckedBegin(); nodeIt != nodeEnd; ++nodeIt) {
JSNode* jsNode = nodeIt->second;
if (isObservableThroughDOM(jsNode, world))
markStack.append(jsNode);
@@ -451,8 +441,7 @@ void markActiveObjectsForContext(MarkStack& markStack, JSGlobalData& globalData,
for (HashMap<ActiveDOMObject*, void*>::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
if (iter->first->hasPendingActivity()) {
// Generally, an active object with pending activity must have a wrapper to mark its listeners.
- // However, some ActiveDOMObjects don't have JS wrappers (timers created by setTimeout is one example).
- // FIXME: perhaps need to make sure even timers have a markable 'wrapper'.
+ // However, some ActiveDOMObjects don't have JS wrappers.
markDOMObjectWrapper(markStack, globalData, iter->second);
}
}
@@ -475,7 +464,7 @@ static inline void takeWrappers(Node* node, Document* document, WrapperSet& wrap
JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) {
if (JSNode* wrapper = iter->second->take(node)) {
- removeWrapper(wrapper);
+ didUncacheWrapper(wrapper);
wrapperSet.append(WrapperAndWorld(wrapper, iter->first));
}
}
@@ -483,7 +472,7 @@ static inline void takeWrappers(Node* node, Document* document, WrapperSet& wrap
for (JSGlobalDataWorldIterator worldIter(JSDOMWindow::commonJSGlobalData()); worldIter; ++worldIter) {
DOMWrapperWorld* world = *worldIter;
if (JSNode* wrapper = static_cast<JSNode*>(world->m_wrappers.take(node))) {
- removeWrapper(wrapper);
+ didUncacheWrapper(wrapper);
wrapperSet.append(WrapperAndWorld(wrapper, world));
}
}
@@ -499,11 +488,11 @@ void updateDOMNodeDocument(Node* node, Document* oldDocument, Document* newDocum
for (unsigned i = 0; i < wrapperSet.size(); ++i) {
JSNode* wrapper = wrapperSet[i].first;
+ willCacheWrapper(wrapper);
if (newDocument)
newDocument->getWrapperCache(wrapperSet[i].second)->set(node, wrapper);
else
wrapperSet[i].second->m_wrappers.set(node, wrapper);
- addWrapper(wrapper);
}
}
@@ -516,7 +505,7 @@ void markDOMObjectWrapper(MarkStack& markStack, JSGlobalData& globalData, void*
return;
for (JSGlobalDataWorldIterator worldIter(&globalData); worldIter; ++worldIter) {
- if (DOMObject* wrapper = worldIter->m_wrappers.get(object))
+ if (DOMObject* wrapper = worldIter->m_wrappers.uncheckedGet(object))
markStack.append(wrapper);
}
}
@@ -526,18 +515,55 @@ void markDOMNodeWrapper(MarkStack& markStack, Document* document, Node* node)
if (document) {
JSWrapperCacheMap& wrapperCacheMap = document->wrapperCacheMap();
for (JSWrapperCacheMap::iterator iter = wrapperCacheMap.begin(); iter != wrapperCacheMap.end(); ++iter) {
- if (JSNode* wrapper = iter->second->get(node))
+ if (JSNode* wrapper = iter->second->uncheckedGet(node))
markStack.append(wrapper);
}
return;
}
for (JSGlobalDataWorldIterator worldIter(JSDOMWindow::commonJSGlobalData()); worldIter; ++worldIter) {
- if (DOMObject* wrapper = worldIter->m_wrappers.get(node))
+ if (DOMObject* wrapper = worldIter->m_wrappers.uncheckedGet(node))
markStack.append(wrapper);
}
}
+static void stringWrapperDestroyed(JSString* str, void* context)
+{
+ StringImpl* cacheKey = static_cast<StringImpl*>(context);
+ JSC::JSGlobalData* globalData = Heap::heap(str)->globalData();
+
+ // Check the normal world first!
+ JSGlobalData::ClientData* clientData = globalData->clientData;
+ ASSERT(clientData);
+ JSStringCache& cache = static_cast<WebCoreJSClientData*>(clientData)->normalWorld()->m_stringCache;
+ if (cache.uncheckedRemove(cacheKey, str)) {
+ cacheKey->deref();
+ return;
+ }
+
+ for (JSGlobalDataWorldIterator worldIter(globalData); worldIter; ++worldIter) {
+ if (worldIter->m_stringCache.uncheckedRemove(cacheKey, str))
+ break;
+ }
+
+ cacheKey->deref();
+}
+
+JSValue jsStringSlowCase(ExecState* exec, JSStringCache& stringCache, StringImpl* stringImpl)
+{
+ // If there is a stale entry, we have to explicitly remove it to avoid
+ // problems down the line.
+ if (JSString* wrapper = stringCache.uncheckedGet(stringImpl))
+ stringCache.uncheckedRemove(stringImpl, wrapper);
+
+ JSString* wrapper = jsStringWithFinalizer(exec, stringImpl->ustring(), stringWrapperDestroyed, stringImpl);
+ stringCache.set(stringImpl, wrapper);
+ // ref explicitly instead of using a RefPtr-keyed hashtable because the wrapper can
+ // outlive the cache, so the stringImpl has to match the wrapper's lifetime.
+ stringImpl->ref();
+ return wrapper;
+}
+
JSValue jsStringOrNull(ExecState* exec, const String& s)
{
if (s.isNull())
@@ -566,6 +592,11 @@ JSValue jsStringOrFalse(ExecState* exec, const String& s)
return jsString(exec, s);
}
+JSValue jsString(ExecState* exec, const KURL& url)
+{
+ return jsString(exec, url.string());
+}
+
JSValue jsStringOrNull(ExecState* exec, const KURL& url)
{
if (url.isNull())
@@ -601,6 +632,22 @@ UString valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue value)
return value.toString(exec);
}
+JSValue jsDateOrNull(ExecState* exec, double value)
+{
+ if (!isfinite(value))
+ return jsNull();
+ return new (exec) DateInstance(exec, value);
+}
+
+double valueToDate(ExecState* exec, JSValue value)
+{
+ if (value.isNumber())
+ return value.uncheckedGetNumber();
+ if (!value.inherits(&DateInstance::info))
+ return std::numeric_limits<double>::quiet_NaN();
+ return static_cast<DateInstance*>(value.toObject(exec))->internalNumber();
+}
+
void reportException(ExecState* exec, JSValue exception)
{
UString errorMessage = exception.toString(exec);
@@ -659,7 +706,7 @@ void setDOMException(ExecState* exec, ExceptionCode ec)
break;
#if ENABLE(SVG)
case SVGExceptionType:
- errorObject = toJS(exec, globalObject, SVGException::create(description).get(), 0);
+ errorObject = toJS(exec, globalObject, SVGException::create(description).get(), 0 /* no context on purpose */);
break;
#endif
#if ENABLE(XPATH)
@@ -734,7 +781,7 @@ bool processingUserGesture(ExecState* exec)
KURL completeURL(ExecState* exec, const String& relativeURL)
{
- // For histoical reasons, we need to complete the URL using the dynamic frame.
+ // For historical reasons, we need to complete the URL using the dynamic frame.
Frame* frame = toDynamicFrame(exec);
if (!frame)
return KURL();