/* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2007 Samuel Weinig * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // gcc 3.x can't handle including the HashMap pointer specialization in this file #if defined __GNUC__ && !defined __GLIBCXX__ // less than gcc 3.4 #define HASH_MAP_PTR_SPEC_WORKAROUND 1 #endif #include "config.h" #include "kjs_binding.h" #include "DOMCoreException.h" #include "EventException.h" #include "ExceptionCode.h" #include "HTMLImageElement.h" #include "HTMLNames.h" #include "JSDOMCoreException.h" #include "JSEventException.h" #include "JSNode.h" #include "JSRangeException.h" #include "JSXMLHttpRequestException.h" #include "KURL.h" #include "RangeException.h" #include "XMLHttpRequestException.h" #include "kjs_window.h" #if ENABLE(SVG) #include "JSSVGException.h" #include "SVGException.h" #endif #if ENABLE(XPATH) #include "JSXPathException.h" #include "XPathException.h" #endif using namespace KJS; namespace WebCore { using namespace HTMLNames; typedef HashMap DOMObjectMap; typedef HashMap NodeMap; typedef HashMap NodePerDocMap; // 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 NodeMap& wrappers); #ifdef NDEBUG static inline void addWrapper(DOMObject*) { } static inline void removeWrapper(DOMObject*) { } static inline void removeWrappers(const NodeMap&) { } #else static HashSet& wrapperSet() { static HashSet staticWrapperSet; return staticWrapperSet; } static void addWrapper(DOMObject* wrapper) { ASSERT(!wrapperSet().contains(wrapper)); wrapperSet().add(wrapper); } static void removeWrapper(DOMObject* wrapper) { if (!wrapper) return; ASSERT(wrapperSet().contains(wrapper)); wrapperSet().remove(wrapper); } static void removeWrappers(const NodeMap& wrappers) { for (NodeMap::const_iterator it = wrappers.begin(); it != wrappers.end(); ++it) removeWrapper(it->second); } DOMObject::~DOMObject() { ASSERT(!wrapperSet().contains(this)); } #endif static DOMObjectMap& domObjects() { // Don't use malloc here. Calling malloc from a mark function can deadlock. static DOMObjectMap staticDOMObjects; return staticDOMObjects; } static NodePerDocMap& domNodesPerDocument() { // domNodesPerDocument() callers must synchronize using the JSLock because // domNodesPerDocument() is called from a mark function, which can run // on a secondary thread. ASSERT(JSLock::lockCount()); // Don't use malloc here. Calling malloc from a mark function can deadlock. static NodePerDocMap staticDOMNodesPerDocument; return staticDOMNodesPerDocument; } DOMObject* ScriptInterpreter::getDOMObject(void* objectHandle) { return domObjects().get(objectHandle); } void ScriptInterpreter::putDOMObject(void* objectHandle, DOMObject* wrapper) { addWrapper(wrapper); domObjects().set(objectHandle, wrapper); } void ScriptInterpreter::forgetDOMObject(void* objectHandle) { removeWrapper(domObjects().take(objectHandle)); } JSNode* ScriptInterpreter::getDOMNodeForDocument(Document* document, WebCore::Node* node) { if (!document) return static_cast(domObjects().get(node)); NodeMap* documentDict = domNodesPerDocument().get(document); if (documentDict) return documentDict->get(node); return NULL; } void ScriptInterpreter::forgetDOMNodeForDocument(Document* document, WebCore::Node* node) { if (!document) { removeWrapper(domObjects().take(node)); return; } NodeMap* documentDict = domNodesPerDocument().get(document); if (documentDict) removeWrapper(documentDict->take(node)); } void ScriptInterpreter::putDOMNodeForDocument(Document* document, WebCore::Node* node, JSNode* wrapper) { addWrapper(wrapper); if (!document) { domObjects().set(node, wrapper); return; } NodeMap* documentDict = domNodesPerDocument().get(document); if (!documentDict) { documentDict = new NodeMap; domNodesPerDocument().set(document, documentDict); } documentDict->set(node, wrapper); } void ScriptInterpreter::forgetAllDOMNodesForDocument(Document* document) { ASSERT(document); NodeMap* map = domNodesPerDocument().take(document); if (!map) return; removeWrappers(*map); delete map; } void ScriptInterpreter::markDOMNodesForDocument(Document* doc) { NodePerDocMap::iterator dictIt = domNodesPerDocument().find(doc); if (dictIt != domNodesPerDocument().end()) { NodeMap* nodeDict = dictIt->second; NodeMap::iterator nodeEnd = nodeDict->end(); for (NodeMap::iterator nodeIt = nodeDict->begin(); nodeIt != nodeEnd; ++nodeIt) { JSNode* jsNode = nodeIt->second; WebCore::Node* node = jsNode->impl(); // don't mark wrappers for nodes that are no longer in the // document - they should not be saved if the node is not // otherwise reachable from JS. // However, image elements that aren't in the document are also // marked, if they are not done loading yet. if (!jsNode->marked() && (node->inDocument() || (node->hasTagName(imgTag) && !static_cast(node)->haveFiredLoadEvent()))) jsNode->mark(); } } } void ScriptInterpreter::updateDOMNodeDocument(WebCore::Node* node, Document* oldDoc, Document* newDoc) { ASSERT(oldDoc != newDoc); JSNode* wrapper = getDOMNodeForDocument(oldDoc, node); if (wrapper) { removeWrapper(wrapper); putDOMNodeForDocument(newDoc, node, wrapper); forgetDOMNodeForDocument(oldDoc, node); addWrapper(wrapper); } } JSValue* jsStringOrNull(const String& s) { if (s.isNull()) return jsNull(); return jsString(s); } JSValue* jsOwnedStringOrNull(const KJS::UString& s) { if (s.isNull()) return jsNull(); return jsOwnedString(s); } JSValue* jsStringOrUndefined(const String& s) { if (s.isNull()) return jsUndefined(); return jsString(s); } JSValue* jsStringOrFalse(const String& s) { if (s.isNull()) return jsBoolean(false); return jsString(s); } JSValue* jsStringOrNull(const KURL& url) { if (url.isNull()) return jsNull(); return jsString(url.string()); } JSValue* jsStringOrUndefined(const KURL& url) { if (url.isNull()) return jsUndefined(); return jsString(url.string()); } JSValue* jsStringOrFalse(const KURL& url) { if (url.isNull()) return jsBoolean(false); return jsString(url.string()); } String valueToStringWithNullCheck(ExecState* exec, JSValue* val) { if (val->isNull()) return String(); return val->toString(exec); } String valueToStringWithUndefinedOrNullCheck(ExecState* exec, JSValue* val) { if (val->isUndefinedOrNull()) return String(); return val->toString(exec); } void setDOMException(ExecState* exec, ExceptionCode ec) { if (!ec || exec->hadException()) return; // To be removed: See XMLHttpRequest.h. if (ec == XMLHttpRequestException::PERMISSION_DENIED) { throwError(exec, GeneralError, "Permission denied"); return; } ExceptionCodeDescription description; getExceptionCodeDescription(ec, description); JSValue* errorObject = 0; switch (description.type) { case DOMExceptionType: errorObject = toJS(exec, new DOMCoreException(description)); break; case RangeExceptionType: errorObject = toJS(exec, new RangeException(description)); break; case EventExceptionType: errorObject = toJS(exec, new EventException(description)); break; case XMLHttpRequestExceptionType: errorObject = toJS(exec, new XMLHttpRequestException(description)); break; #if ENABLE(SVG) case SVGExceptionType: errorObject = toJS(exec, new SVGException(description), 0); break; #endif #if ENABLE(XPATH) case XPathExceptionType: errorObject = toJS(exec, new XPathException(description)); break; #endif } ASSERT(errorObject); exec->setException(errorObject); } bool allowsAccessFromFrame(ExecState* exec, Frame* frame) { if (!frame) return false; Window* window = Window::retrieveWindow(frame); return window && window->allowsAccessFrom(exec); } bool allowsAccessFromFrame(ExecState* exec, Frame* frame, String& message) { if (!frame) return false; Window* window = Window::retrieveWindow(frame); return window && window->allowsAccessFrom(exec, message); } void printErrorMessageForFrame(Frame* frame, const String& message) { if (!frame) return; if (Window* window = Window::retrieveWindow(frame)) window->printErrorMessage(message); } JSValue* nonCachingStaticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) { const HashEntry* entry = slot.staticEntry(); return new PrototypeFunction(exec, entry->params, propertyName, entry->value.functionValue); } JSValue* objectToStringFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot&) { return new PrototypeFunction(exec, 0, propertyName, objectProtoFuncToString); } } // namespace WebCore