webengine/osswebengine/WebCore/bindings/js/kjs_binding.cpp
changeset 0 dd21522fd290
child 68 92a765b5b3e7
child 70 8bfb9186a8b8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebCore/bindings/js/kjs_binding.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,469 @@
+/*
+ *  This file is part of the KDE libraries
+ *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
+ *  Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ *  Copyright (C) 2007 Samuel Weinig <sam@webkit.org>
+ *
+ *  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 "Chrome.h"
+#include "Event.h"
+#include "EventNames.h"
+#include "Frame.h"
+#include "JSNode.h"
+#include "Page.h"
+#include "PlatformString.h"
+#include "Range.h"
+#include "RangeException.h"
+#include "XMLHttpRequest.h"
+#include "kjs_dom.h"
+#include "kjs_window.h"
+#include <kjs/collector.h>
+#include <wtf/HashMap.h>
+
+#if ENABLE(SVG)
+#include "SVGException.h"
+#endif
+
+#if ENABLE(XPATH)
+#include "XPathEvaluator.h"
+#endif
+
+using namespace WebCore;
+using namespace EventNames;
+
+namespace KJS {
+
+typedef HashMap<void*, DOMObject*> DOMObjectMap;
+typedef HashMap<Node*, JSNode*> NodeMap;
+typedef HashMap<Document*, NodeMap*> 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.
+
+#ifdef NDEBUG
+
+#define ADD_WRAPPER(wrapper)
+#define REMOVE_WRAPPER(wrapper)
+#define REMOVE_WRAPPERS(wrappers)
+
+#else
+
+#define ADD_WRAPPER(wrapper) addWrapper(wrapper)
+#define REMOVE_WRAPPER(wrapper) removeWrapper(wrapper)
+#define REMOVE_WRAPPERS(wrappers) removeWrappers(wrappers)
+
+static HashSet<DOMObject*>& wrapperSet()
+{
+    static HashSet<DOMObject*> 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;
+}
+
+ScriptInterpreter::ScriptInterpreter(JSObject* global, Frame* frame)
+    : Interpreter(global)
+    , m_frame(frame)
+    , m_currentEvent(0)
+    , m_inlineCode(false)
+    , m_timerCallback(false)
+{
+    // Time in milliseconds before the script timeout handler kicks in.
+    setTimeoutTime(100000);
+}
+
+DOMObject* ScriptInterpreter::getDOMObject(void* objectHandle) 
+{
+    return domObjects().get(objectHandle);
+}
+
+void ScriptInterpreter::putDOMObject(void* objectHandle, DOMObject* wrapper) 
+{
+    ADD_WRAPPER(wrapper);
+    domObjects().set(objectHandle, wrapper);
+}
+
+void ScriptInterpreter::forgetDOMObject(void* objectHandle)
+{
+    REMOVE_WRAPPER(domObjects().get(objectHandle));
+    domObjects().remove(objectHandle);
+}
+
+JSNode* ScriptInterpreter::getDOMNodeForDocument(Document* document, Node* node)
+{
+    if (!document)
+        return static_cast<JSNode*>(domObjects().get(node));
+    NodeMap* documentDict = domNodesPerDocument().get(document);
+    if (documentDict)
+        return documentDict->get(node);
+    return NULL;
+}
+
+void ScriptInterpreter::forgetDOMNodeForDocument(Document* document, Node* node)
+{
+    REMOVE_WRAPPER(getDOMNodeForDocument(document, node));
+    if (!document) {
+        domObjects().remove(node);
+        return;
+    }
+    NodeMap* documentDict = domNodesPerDocument().get(document);
+    if (documentDict)
+        documentDict->remove(node);
+}
+
+void ScriptInterpreter::putDOMNodeForDocument(Document* document, Node* node, JSNode* wrapper)
+{
+    ADD_WRAPPER(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);
+    NodePerDocMap::iterator it = domNodesPerDocument().find(document);
+    if (it != domNodesPerDocument().end()) {
+        REMOVE_WRAPPERS(*it->second);
+        delete it->second;
+        domNodesPerDocument().remove(it);
+    }
+}
+
+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* node = nodeIt->second;
+            // 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.
+            if (node->impl()->inDocument() && !node->marked())
+                node->mark();
+        }
+    }
+}
+
+ExecState* ScriptInterpreter::globalExec()
+{
+    // we need to make sure that any script execution happening in this
+    // frame does not destroy it
+    m_frame->keepAlive();
+    return Interpreter::globalExec();
+}
+
+void ScriptInterpreter::updateDOMNodeDocument(Node* node, Document* oldDoc, Document* newDoc)
+{
+    ASSERT(oldDoc != newDoc);
+    JSNode* wrapper = getDOMNodeForDocument(oldDoc, node);
+    if (wrapper) {
+        REMOVE_WRAPPER(wrapper);
+        putDOMNodeForDocument(newDoc, node, wrapper);
+        forgetDOMNodeForDocument(oldDoc, node);
+        ADD_WRAPPER(wrapper);
+    }
+}
+
+bool ScriptInterpreter::wasRunByUserGesture() const
+{
+    if (m_currentEvent) {
+        const AtomicString& type = m_currentEvent->type();
+        bool eventOk = ( // mouse events
+            type == clickEvent || type == mousedownEvent ||
+            type == mouseupEvent || type == dblclickEvent ||
+            // keyboard events
+            type == keydownEvent || type == keypressEvent ||
+            type == keyupEvent ||
+            // other accepted events
+            type == selectEvent || type == changeEvent ||
+            type == focusEvent || type == blurEvent ||
+            type == submitEvent);
+        if (eventOk)
+            return true;
+    } else { // no event
+        if (m_inlineCode && !m_timerCallback)
+            // This is the <a href="javascript:window.open('...')> case -> we let it through
+            return true;
+        // This is the <script>window.open(...)</script> case or a timer callback -> block it
+    }
+    return false;
+}
+
+bool ScriptInterpreter::isGlobalObject(JSValue* v)
+{
+    return v->isObject(&Window::info);
+}
+
+bool ScriptInterpreter::isSafeScript(const Interpreter* target)
+{
+    return Window::isSafeScript(this, static_cast<const ScriptInterpreter*>(target));
+}
+
+Interpreter* ScriptInterpreter::interpreterForGlobalObject(const JSValue* imp)
+{
+    const Window* win = static_cast<const Window*>(imp);
+    return win->interpreter();
+}
+
+bool ScriptInterpreter::shouldInterruptScript() const
+{
+    Page* page = m_frame->page();
+
+    // See <rdar://problem/5479443>. We don't think that page can ever be NULL
+    // in this case, but if it is, we've gotten into a state where we may have
+    // hung the UI, with no way to ask the client whether to cancel execution. 
+    // For now, our solution is just to cancel execution no matter what, 
+    // ensuring that we never hang. We might want to consider other solutions 
+    // if we discover problems with this one.
+    ASSERT(page);
+    if (!page)
+        return true;
+
+    return page->chrome()->shouldInterruptJavaScript();
+}
+
+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);
+}
+
+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);
+}
+
+static const char* const exceptionNames[] = {
+    0,
+    "INDEX_SIZE_ERR",
+    "DOMSTRING_SIZE_ERR",
+    "HIERARCHY_REQUEST_ERR",
+    "WRONG_DOCUMENT_ERR",
+    "INVALID_CHARACTER_ERR",
+    "NO_DATA_ALLOWED_ERR",
+    "NO_MODIFICATION_ALLOWED_ERR",
+    "NOT_FOUND_ERR",
+    "NOT_SUPPORTED_ERR",
+    "INUSE_ATTRIBUTE_ERR",
+    "INVALID_STATE_ERR",
+    "SYNTAX_ERR",
+    "INVALID_MODIFICATION_ERR",
+    "NAMESPACE_ERR",
+    "INVALID_ACCESS_ERR",
+    "VALIDATION_ERR",
+    "TYPE_MISMATCH_ERR",
+};
+
+static const char* const rangeExceptionNames[] = {
+    0, "BAD_BOUNDARYPOINTS_ERR", "INVALID_NODE_TYPE_ERR"
+};
+
+static const char* const eventExceptionNames[] = {
+    "UNSPECIFIED_EVENT_TYPE_ERR"
+};
+
+static const char* const xmlHttpRequestExceptionNames[] = {
+    "NETWORK_ERR"
+};
+
+#if ENABLE(XPATH)
+static const char* const xpathExceptionNames[] = {
+    "INVALID_EXPRESSION_ERR",
+    "TYPE_ERR"
+};
+#endif
+
+#if ENABLE(SVG)
+static const char* const svgExceptionNames[] = {
+    "SVG_WRONG_TYPE_ERR",
+    "SVG_INVALID_VALUE_ERR",
+    "SVG_MATRIX_NOT_INVERTABLE"
+};
+#endif
+
+void setDOMException(ExecState* exec, ExceptionCode ec)
+{
+    if (ec == 0 || exec->hadException())
+        return;
+
+    const char* type = "DOM";
+    int code = ec;
+
+    const char* const* nameTable;
+  
+    int nameTableSize;
+    int nameIndex;
+    if (code >= RangeExceptionOffset && code <= RangeExceptionMax) {
+        type = "DOM Range";
+        code -= RangeExceptionOffset;
+        nameIndex = code;
+        nameTable = rangeExceptionNames;
+        nameTableSize = sizeof(rangeExceptionNames) / sizeof(rangeExceptionNames[0]);
+    } else if (code >= EventExceptionOffset && code <= EventExceptionMax) {
+        type = "DOM Events";
+        code -= EventExceptionOffset;
+        nameIndex = code;
+        nameTable = eventExceptionNames;
+        nameTableSize = sizeof(eventExceptionNames) / sizeof(eventExceptionNames[0]);
+    } else if (code == XMLHttpRequestExceptionOffset) {
+        // FIXME: this exception should be replaced with DOM SECURITY_ERR when it finds its way to the spec.
+        throwError(exec, GeneralError, "Permission denied");
+        return;
+    } else if (code > XMLHttpRequestExceptionOffset && code <= XMLHttpRequestExceptionMax) {
+        type = "XMLHttpRequest";
+        // XMLHttpRequest exception codes start with 101 and we don't want 100 empty elements in the name array
+        nameIndex = code - NETWORK_ERR;
+        code -= XMLHttpRequestExceptionOffset;
+        nameTable = xmlHttpRequestExceptionNames;
+        nameTableSize = sizeof(xmlHttpRequestExceptionNames) / sizeof(xmlHttpRequestExceptionNames[0]);
+#if ENABLE(XPATH)
+    } else if (code >= XPathExceptionOffset && code <= XPathExceptionMax) {
+        type = "DOM XPath";
+        // XPath exception codes start with 51 and we don't want 51 empty elements in the name array
+        nameIndex = code - INVALID_EXPRESSION_ERR;
+        code -= XPathExceptionOffset;
+        nameTable = xpathExceptionNames;
+        nameTableSize = sizeof(xpathExceptionNames) / sizeof(xpathExceptionNames[0]);
+#endif
+#if ENABLE(SVG)
+    } else if (code >= SVGExceptionOffset && code <= SVGExceptionMax) {
+        type = "DOM SVG";
+        code -= SVGExceptionOffset;
+        nameIndex = code;
+        nameTable = svgExceptionNames;
+        nameTableSize = sizeof(svgExceptionNames) / sizeof(svgExceptionNames[0]);
+#endif
+    } else {
+        nameIndex = code;
+        nameTable = exceptionNames;
+        nameTableSize = sizeof(exceptionNames) / sizeof(exceptionNames[0]);
+    }
+
+    const char* name = (nameIndex < nameTableSize && nameIndex >= 0) ? nameTable[nameIndex] : 0;
+
+    // 100 characters is a big enough buffer, because there are:
+    //   13 characters in the message
+    //   10 characters in the longest type, "DOM Events"
+    //   27 characters in the longest name, "NO_MODIFICATION_ALLOWED_ERR"
+    //   20 or so digits in the longest integer's ASCII form (even if int is 64-bit)
+    //   1 byte for a null character
+    // That adds up to about 70 bytes.
+    char buffer[100];
+
+    if (name)
+        sprintf(buffer, "%s: %s Exception %d", name, type, code);
+    else
+        sprintf(buffer, "%s Exception %d", type, code);
+
+    JSObject* errorObject = throwError(exec, GeneralError, buffer);
+    errorObject->put(exec, "code", jsNumber(code));
+}
+
+}