diff options
Diffstat (limited to 'WebCore/bindings/js/JSDOMBinding.cpp')
-rw-r--r-- | WebCore/bindings/js/JSDOMBinding.cpp | 223 |
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(); |