blob: 2a760a8016926dc5aaf8cc82a46676f81e1c3690 [file] [log] [blame]
/*
* This file is part of the KDE libraries
* Copyright (C) 2000 Harri Porten (porten@kde.org)
* Copyright (C) 2006 Jon Shier (jshier@iastate.edu)
* Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include "kjs_dom.h"
#include "CDATASection.h"
#include "Comment.h"
#include "DOMImplementation.h"
#include "DocumentFragment.h"
#include "DocumentType.h"
#include "Element.h"
#include "Entity.h"
#include "Event.h"
#include "EventListener.h"
#include "EventNames.h"
#include "ExceptionCode.h"
#include "Frame.h"
#include "HTMLDocument.h"
#include "HTMLNames.h"
#include "HTMLPlugInElement.h"
#include "JSAttr.h"
#include "JSCharacterData.h"
#include "JSDOMImplementation.h"
#include "JSDocumentFragment.h"
#include "JSDocumentType.h"
#include "JSElement.h"
#include "JSEntity.h"
#include "JSHTMLDocument.h"
#include "JSHTMLElementWrapperFactory.h"
#include "JSNode.h"
#include "JSNotation.h"
#include "JSProcessingInstruction.h"
#include "JSRange.h"
#include "JSText.h"
#include "Settings.h"
#include "NamedNodeMap.h"
#include "Notation.h"
#include "ProcessingInstruction.h"
#include "Range.h"
#include "RenderView.h"
#include "kjs_css.h"
#include "kjs_events.h"
#include "kjs_traversal.h"
#include "kjs_window.h"
#if SVG_SUPPORT
#include "JSSVGElementWrapperFactory.h"
#include "SVGElement.h"
#endif
#if __APPLE__
#include <JavaScriptCore/runtime_object.h>
#endif
using namespace WebCore;
using namespace HTMLNames;
using namespace EventNames;
#include "kjs_dom.lut.h"
namespace KJS {
// -------------------------------------------------------------------------
/* Source for DOMNodeProtoTable. Use "make hashtables" to regenerate.
@begin DOMNodeProtoTable 25
insertBefore DOMNode::InsertBefore DontDelete|Function 2
replaceChild DOMNode::ReplaceChild DontDelete|Function 2
removeChild DOMNode::RemoveChild DontDelete|Function 1
appendChild DOMNode::AppendChild DontDelete|Function 1
hasAttributes DOMNode::HasAttributes DontDelete|Function 0
hasChildNodes DOMNode::HasChildNodes DontDelete|Function 0
cloneNode DOMNode::CloneNode DontDelete|Function 1
# DOM2
normalize DOMNode::Normalize DontDelete|Function 0
isSupported DOMNode::IsSupported DontDelete|Function 2
# DOM3
isSameNode DOMNode::IsSameNode DontDelete|Function 1
isEqualNode DOMNode::IsEqualNode DontDelete|Function 1
isDefaultNamespace DOMNode::IsDefaultNamespace DontDelete|Function 1
lookupNamespaceURI DOMNode::LookupNamespaceURI DontDelete|Function 1
lookupPrefix DOMNode::LookupPrefix DontDelete|Function 1
# "DOM level 0" (from Gecko DOM reference; also in WinIE)
item DOMNode::Item DontDelete|Function 1
@end
*/
KJS_IMPLEMENT_PROTOFUNC(DOMNodeProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMNode", DOMNodeProto, DOMNodeProtoFunc)
const ClassInfo DOMNode::info = { "Node", 0, &DOMNodeTable, 0 };
DOMNode::DOMNode(ExecState* exec, Node* n)
: m_impl(n)
{
setPrototype(DOMNodeProto::self(exec));
}
DOMNode::DOMNode(Node* n)
: m_impl(n)
{
}
DOMNode::~DOMNode()
{
ScriptInterpreter::forgetDOMNodeForDocument(m_impl->document(), m_impl.get());
}
void DOMNode::mark()
{
assert(!marked());
Node* node = m_impl.get();
// Nodes in the document are kept alive by ScriptInterpreter::mark,
// so we have no special responsibilities and can just call the base class here.
if (node->inDocument()) {
DOMObject::mark();
return;
}
// This is a node outside the document, so find the root of the tree it is in,
// and start marking from there.
Node* root = node;
for (Node* current = m_impl.get(); current; current = current->parentNode()) {
root = current;
}
static HashSet<Node*> markingRoots;
// If we're already marking this tree, then we can simply mark this wrapper
// by calling the base class; our caller is iterating the tree.
if (markingRoots.contains(root)) {
DOMObject::mark();
return;
}
// Mark the whole tree; use the global set of roots to avoid reentering.
markingRoots.add(root);
for (Node* nodeToMark = root; nodeToMark; nodeToMark = nodeToMark->traverseNextNode()) {
DOMNode *wrapper = ScriptInterpreter::getDOMNodeForDocument(m_impl->document(), nodeToMark);
if (wrapper) {
if (!wrapper->marked())
wrapper->mark();
} else if (nodeToMark == node) {
// This is the case where the map from the document to wrappers has
// been cleared out, but a wrapper is being marked. For now, we'll
// let the rest of the tree of wrappers get collected, because we have
// no good way of finding them. Later we should test behavior of other
// browsers and see if we need to preserve other wrappers in this case.
if (!marked())
mark();
}
}
markingRoots.remove(root);
// Double check that we actually ended up marked. This assert caught problems in the past.
assert(marked());
}
bool DOMNode::toBoolean(ExecState* ) const
{
return m_impl;
}
/* Source for DOMNodeTable. Use "make hashtables" to regenerate.
@begin DOMNodeTable 25
nodeName DOMNode::NodeName DontDelete|ReadOnly
nodeValue DOMNode::NodeValue DontDelete
nodeType DOMNode::NodeType DontDelete|ReadOnly
parentNode DOMNode::ParentNode DontDelete|ReadOnly
parentElement DOMNode::ParentElement DontDelete|ReadOnly
childNodes DOMNode::ChildNodes DontDelete|ReadOnly
firstChild DOMNode::FirstChild DontDelete|ReadOnly
lastChild DOMNode::LastChild DontDelete|ReadOnly
previousSibling DOMNode::PreviousSibling DontDelete|ReadOnly
nextSibling DOMNode::NextSibling DontDelete|ReadOnly
attributes DOMNode::Attributes DontDelete|ReadOnly
namespaceURI DOMNode::NamespaceURI DontDelete|ReadOnly
# DOM2
prefix DOMNode::Prefix DontDelete
localName DOMNode::LocalName DontDelete|ReadOnly
ownerDocument DOMNode::OwnerDocument DontDelete|ReadOnly
# DOM3
textContent DOMNode::TextContent DontDelete
@end
*/
bool DOMNode::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticValueSlot<DOMNode, DOMObject>(exec, &DOMNodeTable, this, propertyName, slot);
}
JSValue* DOMNode::getValueProperty(ExecState* exec, int token) const
{
Node& node = *m_impl;
switch (token) {
case NodeName:
return jsStringOrNull(node.nodeName());
case NodeValue:
return jsStringOrNull(node.nodeValue());
case NodeType:
return jsNumber(node.nodeType());
case ParentNode:
case ParentElement: // IE only apparently
return toJS(exec,node.parentNode());
case ChildNodes:
return toJS(exec,node.childNodes().get());
case FirstChild:
return toJS(exec,node.firstChild());
case LastChild:
return toJS(exec,node.lastChild());
case PreviousSibling:
return toJS(exec,node.previousSibling());
case NextSibling:
return toJS(exec,node.nextSibling());
case Attributes:
return toJS(exec,node.attributes());
case NamespaceURI:
return jsStringOrNull(node.namespaceURI());
case Prefix:
return jsStringOrNull(node.prefix());
case LocalName:
return jsStringOrNull(node.localName());
case OwnerDocument:
return toJS(exec,node.ownerDocument());
case TextContent:
return jsStringOrNull(node.textContent());
}
return jsUndefined();
}
void DOMNode::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
{
lookupPut<DOMNode,DOMObject>(exec, propertyName, value, attr, &DOMNodeTable, this);
}
void DOMNode::putValueProperty(ExecState* exec, int token, JSValue* value, int /*attr*/)
{
DOMExceptionTranslator exception(exec);
Node& node = *m_impl;
switch (token) {
case NodeValue:
node.setNodeValue(value->toString(exec), exception);
break;
case Prefix:
node.setPrefix(value->toString(exec), exception);
break;
case TextContent:
node.setTextContent(valueToStringWithNullCheck(exec, value), exception);
break;
}
}
JSValue* DOMNode::toPrimitive(ExecState* exec, JSType) const
{
if (!m_impl)
return jsNull();
return jsString(toString(exec));
}
UString DOMNode::toString(ExecState* ) const
{
if (!m_impl)
return "null";
return "[object " + (m_impl->isElementNode() ? UString(m_impl->nodeName()) : className()) + "]";
}
JSValue* DOMNodeProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
{
if (!thisObj->inherits(&DOMNode::info))
return throwError(exec, TypeError);
DOMExceptionTranslator exception(exec);
Node& node = *static_cast<DOMNode*>(thisObj)->impl();
switch (id) {
case DOMNode::HasAttributes:
return jsBoolean(node.hasAttributes());
case DOMNode::HasChildNodes:
return jsBoolean(node.hasChildNodes());
case DOMNode::CloneNode:
return toJS(exec,node.cloneNode(args[0]->toBoolean(exec)));
case DOMNode::Normalize:
node.normalize();
return jsUndefined();
case DOMNode::IsSupported:
return jsBoolean(node.isSupported(args[0]->toString(exec),
valueToStringWithNullCheck(exec, args[1])));
case DOMNode::IsSameNode:
return jsBoolean(node.isSameNode(toNode(args[0])));
case DOMNode::IsEqualNode:
return jsBoolean(node.isEqualNode(toNode(args[0])));
case DOMNode::IsDefaultNamespace:
return jsBoolean(node.isDefaultNamespace(valueToStringWithNullCheck(exec, args[0])));
case DOMNode::LookupNamespaceURI:
return jsStringOrNull(node.lookupNamespaceURI(valueToStringWithNullCheck(exec, args[0])));
case DOMNode::LookupPrefix:
return jsStringOrNull(node.lookupPrefix(valueToStringWithNullCheck(exec, args[0])));
case DOMNode::AppendChild:
if (node.appendChild(toNode(args[0]), exception))
return args[0];
return jsNull();
case DOMNode::RemoveChild:
if (node.removeChild(toNode(args[0]), exception))
return args[0];
return jsNull();
case DOMNode::InsertBefore:
if (node.insertBefore(toNode(args[0]), toNode(args[1]), exception))
return args[0];
return jsNull();
case DOMNode::ReplaceChild:
if (node.replaceChild(toNode(args[0]), toNode(args[1]), exception))
return args[1];
return jsNull();
case DOMNode::Item:
return thisObj->get(exec, args[0]->toInt32(exec));
}
return jsUndefined();
}
EventTargetNode *toEventTargetNode(JSValue* val)
{
if (!val || !val->isObject(&DOMEventTargetNode::info))
return 0;
return static_cast<EventTargetNode*>(static_cast<DOMEventTargetNode*>(val)->impl());
}
Node* toNode(JSValue* val)
{
if (!val || !val->isObject(&DOMNode::info))
return 0;
return static_cast<DOMNode*>(val)->impl();
}
// -------------------------------------------------------------------------
/* Source for DOMEventTargetNodeTable
@begin DOMEventTargetNodeTable 50
onabort DOMEventTargetNode::OnAbort DontDelete
onblur DOMEventTargetNode::OnBlur DontDelete
onchange DOMEventTargetNode::OnChange DontDelete
onclick DOMEventTargetNode::OnClick DontDelete
oncontextmenu DOMEventTargetNode::OnContextMenu DontDelete
ondblclick DOMEventTargetNode::OnDblClick DontDelete
onbeforecut DOMEventTargetNode::OnBeforeCut DontDelete
oncut DOMEventTargetNode::OnCut DontDelete
onbeforecopy DOMEventTargetNode::OnBeforeCopy DontDelete
oncopy DOMEventTargetNode::OnCopy DontDelete
onbeforepaste DOMEventTargetNode::OnBeforePaste DontDelete
onpaste DOMEventTargetNode::OnPaste DontDelete
ondrag DOMEventTargetNode::OnDrag DontDelete
ondragdrop DOMEventTargetNode::OnDragDrop DontDelete
ondragend DOMEventTargetNode::OnDragEnd DontDelete
ondragenter DOMEventTargetNode::OnDragEnter DontDelete
ondragleave DOMEventTargetNode::OnDragLeave DontDelete
ondragover DOMEventTargetNode::OnDragOver DontDelete
ondragstart DOMEventTargetNode::OnDragStart DontDelete
ondrop DOMEventTargetNode::OnDrop DontDelete
onerror DOMEventTargetNode::OnError DontDelete
onfocus DOMEventTargetNode::OnFocus DontDelete
oninput DOMEventTargetNode::OnInput DontDelete
onkeydown DOMEventTargetNode::OnKeyDown DontDelete
onkeypress DOMEventTargetNode::OnKeyPress DontDelete
onkeyup DOMEventTargetNode::OnKeyUp DontDelete
onload DOMEventTargetNode::OnLoad DontDelete
onmousedown DOMEventTargetNode::OnMouseDown DontDelete
onmousemove DOMEventTargetNode::OnMouseMove DontDelete
onmouseout DOMEventTargetNode::OnMouseOut DontDelete
onmouseover DOMEventTargetNode::OnMouseOver DontDelete
onmouseup DOMEventTargetNode::OnMouseUp DontDelete
onmousewheel DOMEventTargetNode::OnMouseWheel DontDelete
onmove DOMEventTargetNode::OnMove DontDelete
onreset DOMEventTargetNode::OnReset DontDelete
onresize DOMEventTargetNode::OnResize DontDelete
onscroll DOMEventTargetNode::OnScroll DontDelete
onsearch DOMEventTargetNode::OnSearch DontDelete
onselect DOMEventTargetNode::OnSelect DontDelete
onselectstart DOMEventTargetNode::OnSelectStart DontDelete
onsubmit DOMEventTargetNode::OnSubmit DontDelete
onunload DOMEventTargetNode::OnUnload DontDelete
@end
*/
DOMEventTargetNode::DOMEventTargetNode(ExecState* exec, Node* n)
: JSNode(exec, n)
{
setPrototype(DOMEventTargetNodeProto::self(exec));
}
bool DOMEventTargetNode::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticValueSlot<DOMEventTargetNode, DOMNode>(exec, &DOMEventTargetNodeTable, this, propertyName, slot);
}
JSValue* DOMEventTargetNode::getValueProperty(ExecState* exec, int token) const
{
switch (token) {
case OnAbort:
return getListener(abortEvent);
case OnBlur:
return getListener(blurEvent);
case OnChange:
return getListener(changeEvent);
case OnClick:
return getListener(clickEvent);
case OnContextMenu:
return getListener(contextmenuEvent);
case OnDblClick:
return getListener(dblclickEvent);
case OnDragDrop:
return getListener(khtmlDragdropEvent);
case OnError:
return getListener(errorEvent);
case OnFocus:
return getListener(focusEvent);
case OnInput:
return getListener(inputEvent);
case OnKeyDown:
return getListener(keydownEvent);
case OnKeyPress:
return getListener(keypressEvent);
case OnKeyUp:
return getListener(keyupEvent);
case OnLoad:
return getListener(loadEvent);
case OnMouseDown:
return getListener(mousedownEvent);
case OnMouseMove:
return getListener(mousemoveEvent);
case OnMouseOut:
return getListener(mouseoutEvent);
case OnMouseOver:
return getListener(mouseoverEvent);
case OnMouseUp:
return getListener(mouseupEvent);
case OnMouseWheel:
return getListener(mousewheelEvent);
case OnBeforeCut:
return getListener(beforecutEvent);
case OnCut:
return getListener(cutEvent);
case OnBeforeCopy:
return getListener(beforecopyEvent);
case OnCopy:
return getListener(copyEvent);
case OnBeforePaste:
return getListener(beforepasteEvent);
case OnPaste:
return getListener(pasteEvent);
case OnDragEnter:
return getListener(dragenterEvent);
case OnDragOver:
return getListener(dragoverEvent);
case OnDragLeave:
return getListener(dragleaveEvent);
case OnDrop:
return getListener(dropEvent);
case OnDragStart:
return getListener(dragstartEvent);
case OnDrag:
return getListener(dragEvent);
case OnDragEnd:
return getListener(dragendEvent);
case OnMove:
return getListener(khtmlMoveEvent);
case OnReset:
return getListener(resetEvent);
case OnResize:
return getListener(resizeEvent);
case OnScroll:
return getListener(scrollEvent);
case OnSearch:
return getListener(searchEvent);
case OnSelect:
return getListener(selectEvent);
case OnSelectStart:
return getListener(selectstartEvent);
case OnSubmit:
return getListener(submitEvent);
case OnUnload:
return getListener(unloadEvent);
}
return jsUndefined();
}
void DOMEventTargetNode::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
{
lookupPut<DOMEventTargetNode, DOMNode>(exec, propertyName, value, attr, &DOMEventTargetNodeTable, this);
}
void DOMEventTargetNode::putValueProperty(ExecState* exec, int token, JSValue* value, int /*attr*/)
{
switch (token) {
case OnAbort:
setListener(exec, abortEvent, value);
break;
case OnBlur:
setListener(exec, blurEvent, value);
break;
case OnChange:
setListener(exec, changeEvent, value);
break;
case OnClick:
setListener(exec, clickEvent, value);
break;
case OnContextMenu:
setListener(exec, contextmenuEvent, value);
break;
case OnDblClick:
setListener(exec, dblclickEvent, value);
break;
case OnDragDrop:
setListener(exec, khtmlDragdropEvent, value);
break;
case OnError:
setListener(exec, errorEvent, value);
break;
case OnFocus:
setListener(exec, focusEvent, value);
break;
case OnInput:
setListener(exec, inputEvent, value);
break;
case OnKeyDown:
setListener(exec, keydownEvent, value);
break;
case OnKeyPress:
setListener(exec, keypressEvent, value);
break;
case OnKeyUp:
setListener(exec, keyupEvent, value);
break;
case OnLoad:
setListener(exec, loadEvent, value);
break;
case OnMouseDown:
setListener(exec, mousedownEvent, value);
break;
case OnMouseMove:
setListener(exec, mousemoveEvent, value);
break;
case OnMouseOut:
setListener(exec, mouseoutEvent, value);
break;
case OnMouseOver:
setListener(exec, mouseoverEvent, value);
break;
case OnMouseUp:
setListener(exec, mouseupEvent, value);
break;
case OnMouseWheel:
setListener(exec, mousewheelEvent, value);
break;
case OnBeforeCut:
setListener(exec, beforecutEvent, value);
break;
case OnCut:
setListener(exec, cutEvent, value);
break;
case OnBeforeCopy:
setListener(exec, beforecopyEvent, value);
break;
case OnCopy:
setListener(exec, copyEvent, value);
break;
case OnBeforePaste:
setListener(exec, beforepasteEvent, value);
break;
case OnPaste:
setListener(exec, pasteEvent, value);
break;
case OnDragEnter:
setListener(exec, dragenterEvent, value);
break;
case OnDragOver:
setListener(exec, dragoverEvent, value);
break;
case OnDragLeave:
setListener(exec, dragleaveEvent, value);
break;
case OnDrop:
setListener(exec, dropEvent, value);
break;
case OnDragStart:
setListener(exec, dragstartEvent, value);
break;
case OnDrag:
setListener(exec, dragEvent, value);
break;
case OnDragEnd:
setListener(exec, dragendEvent, value);
break;
case OnMove:
setListener(exec, khtmlMoveEvent, value);
break;
case OnReset:
setListener(exec, resetEvent, value);
break;
case OnResize:
setListener(exec, resizeEvent, value);
break;
case OnScroll:
setListener(exec, scrollEvent, value);
break;
case OnSearch:
setListener(exec, searchEvent, value);
break;
case OnSelect:
setListener(exec, selectEvent, value);
break;
case OnSelectStart:
setListener(exec, selectstartEvent, value);
break;
case OnSubmit:
setListener(exec, submitEvent, value);
break;
case OnUnload:
setListener(exec, unloadEvent, value);
break;
}
}
void DOMEventTargetNode::setListener(ExecState* exec, const AtomicString &eventType, JSValue* func) const
{
EventTargetNodeCast(impl())->setHTMLEventListener(eventType, Window::retrieveActive(exec)->getJSEventListener(func, true));
}
JSValue* DOMEventTargetNode::getListener(const AtomicString &eventType) const
{
EventListener *listener = EventTargetNodeCast(impl())->getHTMLEventListener(eventType);
JSEventListener *jsListener = static_cast<JSEventListener*>(listener);
if (jsListener && jsListener->listenerObj())
return jsListener->listenerObj();
else
return jsNull();
}
void DOMEventTargetNode::pushEventHandlerScope(ExecState*, ScopeChain &) const
{
}
/*
@begin DOMEventTargetNodeProtoTable 5
# from the EventTarget interface
addEventListener DOMEventTargetNode::AddEventListener DontDelete|Function 3
removeEventListener DOMEventTargetNode::RemoveEventListener DontDelete|Function 3
dispatchEvent DOMEventTargetNode::DispatchEvent DontDelete|Function 1
@end
*/
KJS_IMPLEMENT_PROTOFUNC(DOMEventTargetNodeProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("DOMEventTargetNode", DOMEventTargetNodeProto, DOMEventTargetNodeProtoFunc)
JSValue* DOMEventTargetNodeProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
{
if (!thisObj->inherits(&DOMEventTargetNode::info))
return throwError(exec, TypeError);
DOMExceptionTranslator exception(exec);
DOMEventTargetNode* DOMNode = static_cast<DOMEventTargetNode*>(thisObj);
EventTargetNode* node = static_cast<EventTargetNode*>(DOMNode->impl());
switch (id) {
case DOMEventTargetNode::AddEventListener: {
JSEventListener *listener = Window::retrieveActive(exec)->getJSEventListener(args[1]);
if (listener)
node->addEventListener(args[0]->toString(exec), listener,args[2]->toBoolean(exec));
return jsUndefined();
}
case DOMEventTargetNode::RemoveEventListener: {
JSEventListener *listener = Window::retrieveActive(exec)->getJSEventListener(args[1]);
if (listener)
node->removeEventListener(args[0]->toString(exec), listener,args[2]->toBoolean(exec));
return jsUndefined();
}
case DOMEventTargetNode::DispatchEvent:
return jsBoolean(node->dispatchEvent(toEvent(args[0]), exception));
}
return jsUndefined();
}
// -------------------------------------------------------------------------
/*
@begin DOMNodeListTable 2
length DOMNodeList::Length DontDelete|ReadOnly
item DOMNodeList::Item DontDelete|Function 1
@end
*/
KJS_IMPLEMENT_PROTOFUNC(DOMNodeListFunc)
const ClassInfo DOMNodeList::info = { "NodeList", 0, &DOMNodeListTable, 0 };
DOMNodeList::DOMNodeList(ExecState* exec, NodeList *l)
: m_impl(l)
{
setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
}
DOMNodeList::~DOMNodeList()
{
ScriptInterpreter::forgetDOMObject(m_impl.get());
}
JSValue* DOMNodeList::toPrimitive(ExecState* exec, JSType) const
{
if (!m_impl)
return jsNull();
return jsString(toString(exec));
}
JSValue* DOMNodeList::getValueProperty(ExecState* exec, int token) const
{
assert(token == Length);
return jsNumber(m_impl->length());
}
JSValue* DOMNodeList::indexGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
{
DOMNodeList *thisObj = static_cast<DOMNodeList*>(slot.slotBase());
return toJS(exec, thisObj->m_impl->item(slot.index()));
}
JSValue* DOMNodeList::nameGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
{
DOMNodeList *thisObj = static_cast<DOMNodeList*>(slot.slotBase());
return toJS(exec, thisObj->m_impl->itemWithName(propertyName));
}
bool DOMNodeList::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
const HashEntry* entry = Lookup::findEntry(&DOMNodeListTable, propertyName);
if (entry) {
if (entry->attr & Function)
slot.setStaticEntry(this, entry, staticFunctionGetter<DOMNodeListFunc>);
else
slot.setStaticEntry(this, entry, staticValueGetter<DOMNodeList>);
return true;
}
// array index ?
bool ok;
unsigned idx = propertyName.toUInt32(&ok);
if (ok && idx < m_impl->length()) {
slot.setCustomIndex(this, idx, indexGetter);
return true;
} else if (m_impl->itemWithName(String(propertyName).impl())) {
slot.setCustom(this, nameGetter);
return true;
}
return DOMObject::getOwnPropertySlot(exec, propertyName, slot);
}
// Need to support both get and call, so that list[0] and list(0) work.
JSValue* DOMNodeList::callAsFunction(ExecState* exec, JSObject*, const List &args)
{
// Do not use thisObj here. See JSHTMLCollection.
UString s = args[0]->toString(exec);
bool ok;
unsigned int u = s.toUInt32(&ok);
if (ok)
return toJS(exec, m_impl->item(u));
return jsUndefined();
}
// Not a prototype class currently, but should probably be converted to one
JSValue* DOMNodeListFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
{
if (!thisObj->inherits(&KJS::DOMNodeList::info))
return throwError(exec, TypeError);
NodeList &list = *static_cast<DOMNodeList*>(thisObj)->impl();
if (id == DOMNodeList::Item)
return toJS(exec, list.item(args[0]->toInt32(exec)));
return jsUndefined();
}
Attr* toAttr(JSValue* val, bool& ok)
{
if (!val || !val->isObject(&JSAttr::info)) {
ok = false;
return 0;
}
ok = true;
return static_cast<Attr*>(static_cast<DOMNode*>(val)->impl());
}
Element* toElement(JSValue* val)
{
if (!val || !val->isObject(&JSElement::info))
return 0;
return static_cast<Element*>(static_cast<JSElement*>(val)->impl());
}
DocumentType* toDocumentType(JSValue* val)
{
if (!val || !val->isObject(&JSDocumentType::info))
return 0;
return static_cast<DocumentType*>(static_cast<DOMNode*>(val)->impl());
}
// -------------------------------------------------------------------------
/* Source for DOMNamedNodeMapProtoTable. Use "make hashtables" to regenerate.
@begin DOMNamedNodeMapProtoTable 10
getNamedItem DOMNamedNodeMap::GetNamedItem DontDelete|Function 1
setNamedItem DOMNamedNodeMap::SetNamedItem DontDelete|Function 1
removeNamedItem DOMNamedNodeMap::RemoveNamedItem DontDelete|Function 1
item DOMNamedNodeMap::Item DontDelete|Function 1
# DOM2
getNamedItemNS DOMNamedNodeMap::GetNamedItemNS DontDelete|Function 2
setNamedItemNS DOMNamedNodeMap::SetNamedItemNS DontDelete|Function 1
removeNamedItemNS DOMNamedNodeMap::RemoveNamedItemNS DontDelete|Function 2
@end
*/
KJS_DEFINE_PROTOTYPE(DOMNamedNodeMapProto)
KJS_IMPLEMENT_PROTOFUNC(DOMNamedNodeMapProtoFunc)
KJS_IMPLEMENT_PROTOTYPE("NamedNodeMap", DOMNamedNodeMapProto, DOMNamedNodeMapProtoFunc)
const ClassInfo DOMNamedNodeMap::info = { "NamedNodeMap", 0, 0, 0 };
DOMNamedNodeMap::DOMNamedNodeMap(ExecState* exec, NamedNodeMap* m)
: m_impl(m)
{
setPrototype(DOMNamedNodeMapProto::self(exec));
}
DOMNamedNodeMap::~DOMNamedNodeMap()
{
ScriptInterpreter::forgetDOMObject(m_impl.get());
}
JSValue* DOMNamedNodeMap::lengthGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
{
DOMNamedNodeMap* thisObj = static_cast<DOMNamedNodeMap*>(slot.slotBase());
return jsNumber(thisObj->m_impl->length());
}
JSValue* DOMNamedNodeMap::indexGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
{
DOMNamedNodeMap* thisObj = static_cast<DOMNamedNodeMap*>(slot.slotBase());
return toJS(exec, thisObj->m_impl->item(slot.index()));
}
JSValue* DOMNamedNodeMap::nameGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
{
DOMNamedNodeMap* thisObj = static_cast<DOMNamedNodeMap*>(slot.slotBase());
return toJS(exec, thisObj->m_impl->getNamedItem(propertyName));
}
bool DOMNamedNodeMap::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
if (propertyName == lengthPropertyName) {
slot.setCustom(this, lengthGetter);
return true;
} else {
// Look in the prototype (for functions) before assuming it's an item's name
JSValue* proto = prototype();
if (proto->isObject() && static_cast<JSObject*>(proto)->hasProperty(exec, propertyName))
return false;
// name or index ?
bool ok;
unsigned idx = propertyName.toUInt32(&ok);
if (ok && idx < m_impl->length()) {
slot.setCustomIndex(this, idx, indexGetter);
return true;
}
if (m_impl->getNamedItem(propertyName)) {
slot.setCustom(this, nameGetter);
return true;
}
}
return DOMObject::getOwnPropertySlot(exec, propertyName, slot);
}
JSValue* DOMNamedNodeMapProtoFunc::callAsFunction(ExecState* exec, JSObject* thisObj, const List &args)
{
if (!thisObj->inherits(&KJS::DOMNamedNodeMap::info))
return throwError(exec, TypeError);
DOMExceptionTranslator exception(exec);
NamedNodeMap &map = *static_cast<DOMNamedNodeMap*>(thisObj)->impl();
switch (id) {
case DOMNamedNodeMap::GetNamedItem:
return toJS(exec, map.getNamedItem(args[0]->toString(exec)));
case DOMNamedNodeMap::SetNamedItem:
return toJS(exec, map.setNamedItem(toNode(args[0]), exception).get());
case DOMNamedNodeMap::RemoveNamedItem:
return toJS(exec, map.removeNamedItem(args[0]->toString(exec), exception).get());
case DOMNamedNodeMap::Item:
return toJS(exec, map.item(args[0]->toInt32(exec)));
case DOMNamedNodeMap::GetNamedItemNS: // DOM2
return toJS(exec, map.getNamedItemNS(valueToStringWithNullCheck(exec, args[0]), args[1]->toString(exec)));
case DOMNamedNodeMap::SetNamedItemNS: // DOM2
return toJS(exec, map.setNamedItemNS(toNode(args[0]), exception).get());
case DOMNamedNodeMap::RemoveNamedItemNS: // DOM2
return toJS(exec, map.removeNamedItemNS(valueToStringWithNullCheck(exec, args[0]), args[1]->toString(exec), exception).get());
}
return jsUndefined();
}
// -------------------------------------------------------------------------
JSValue* toJS(ExecState* exec, Document *n)
{
if (!n)
return jsNull();
JSDocument* ret = 0;
ScriptInterpreter* interp = static_cast<ScriptInterpreter*>(exec->dynamicInterpreter());
if ((ret = static_cast<JSDocument*>(interp->getDOMObject(n))))
return ret;
if (n->isHTMLDocument())
ret = new WebCore::JSHTMLDocument(exec, static_cast<HTMLDocument*>(n));
else
ret = new JSDocument(exec, n);
// Make sure the document is kept around by the window object, and works right with the
// back/forward cache.
if (n->frame())
Window::retrieveWindow(n->frame())->putDirect("document", ret, DontDelete|ReadOnly);
interp->putDOMObject(n, ret);
return ret;
}
bool checkNodeSecurity(ExecState* exec, Node* n)
{
if (!n)
return false;
// Check to see if the currently executing interpreter is allowed to access the specified node
Window* win = Window::retrieveWindow(n->document()->frame());
return win && win->isSafeScript(exec);
}
JSValue* toJS(ExecState* exec, PassRefPtr<Node> node)
{
Node* n = node.get();
DOMNode* ret = 0;
if (!n)
return jsNull();
ScriptInterpreter* interp = static_cast<ScriptInterpreter*>(exec->dynamicInterpreter());
Document* doc = n->document();
if ((ret = interp->getDOMNodeForDocument(doc, n)))
return ret;
switch (n->nodeType()) {
case Node::ELEMENT_NODE:
if (n->isHTMLElement())
ret = createJSHTMLWrapper(exec, static_pointer_cast<HTMLElement>(node));
#if SVG_SUPPORT
else if (n->isSVGElement())
ret = createJSSVGWrapper(exec, static_pointer_cast<SVGElement>(node));
#endif
else
ret = new JSElement(exec, static_cast<Element*>(n));
break;
case Node::ATTRIBUTE_NODE:
ret = new JSAttr(exec, static_cast<Attr*>(n));
break;
case Node::TEXT_NODE:
case Node::CDATA_SECTION_NODE:
ret = new JSText(exec, static_cast<Text*>(n));
break;
case Node::ENTITY_NODE:
ret = new JSEntity(exec, static_cast<Entity*>(n));
break;
case Node::PROCESSING_INSTRUCTION_NODE:
ret = new JSProcessingInstruction(exec, static_cast<ProcessingInstruction*>(n));
break;
case Node::COMMENT_NODE:
ret = new JSCharacterData(exec, static_cast<CharacterData*>(n));
break;
case Node::DOCUMENT_NODE:
// we don't want to cache the document itself in the per-document dictionary
return toJS(exec, static_cast<Document*>(n));
case Node::DOCUMENT_TYPE_NODE:
ret = new JSDocumentType(exec, static_cast<DocumentType*>(n));
break;
case Node::NOTATION_NODE:
ret = new JSNotation(exec, static_cast<Notation*>(n));
break;
case Node::DOCUMENT_FRAGMENT_NODE:
ret = new JSDocumentFragment(exec, static_cast<DocumentFragment*>(n));
break;
case Node::ENTITY_REFERENCE_NODE:
default:
ret = new JSNode(exec, n);
}
interp->putDOMNodeForDocument(doc, n, ret);
return ret;
}
JSValue* toJS(ExecState* exec, NamedNodeMap* m)
{
return cacheDOMObject<NamedNodeMap, DOMNamedNodeMap>(exec, m);
}
JSValue* getRuntimeObject(ExecState* exec, Node* n)
{
if (!n)
return 0;
#if __APPLE__
if (n->hasTagName(objectTag) || n->hasTagName(embedTag) || n->hasTagName(appletTag)) {
HTMLPlugInElement* plugInElement = static_cast<HTMLPlugInElement*>(n);
if (plugInElement->getInstance())
// The instance is owned by the PlugIn element.
return new RuntimeObjectImp(plugInElement->getInstance());
}
#endif
// If we don't have a runtime object return 0.
return 0;
}
JSValue* toJS(ExecState* exec, PassRefPtr<NodeList> l)
{
return cacheDOMObject<NodeList, DOMNodeList>(exec, l.get());
}
// -------------------------------------------------------------------------
const ClassInfo DOMExceptionConstructor::info = { "DOMExceptionConstructor", 0, 0, 0 };
/* Source for DOMExceptionConstructorTable. Use "make hashtables" to regenerate.
@begin DOMExceptionConstructorTable 15
INDEX_SIZE_ERR WebCore::INDEX_SIZE_ERR DontDelete|ReadOnly
DOMSTRING_SIZE_ERR WebCore::DOMSTRING_SIZE_ERR DontDelete|ReadOnly
HIERARCHY_REQUEST_ERR WebCore::HIERARCHY_REQUEST_ERR DontDelete|ReadOnly
WRONG_DOCUMENT_ERR WebCore::WRONG_DOCUMENT_ERR DontDelete|ReadOnly
INVALID_CHARACTER_ERR WebCore::INVALID_CHARACTER_ERR DontDelete|ReadOnly
NO_DATA_ALLOWED_ERR WebCore::NO_DATA_ALLOWED_ERR DontDelete|ReadOnly
NO_MODIFICATION_ALLOWED_ERR WebCore::NO_MODIFICATION_ALLOWED_ERR DontDelete|ReadOnly
NOT_FOUND_ERR WebCore::NOT_FOUND_ERR DontDelete|ReadOnly
NOT_SUPPORTED_ERR WebCore::NOT_SUPPORTED_ERR DontDelete|ReadOnly
INUSE_ATTRIBUTE_ERR WebCore::INUSE_ATTRIBUTE_ERR DontDelete|ReadOnly
INVALID_STATE_ERR WebCore::INVALID_STATE_ERR DontDelete|ReadOnly
SYNTAX_ERR WebCore::SYNTAX_ERR DontDelete|ReadOnly
INVALID_MODIFICATION_ERR WebCore::INVALID_MODIFICATION_ERR DontDelete|ReadOnly
NAMESPACE_ERR WebCore::NAMESPACE_ERR DontDelete|ReadOnly
INVALID_ACCESS_ERR WebCore::INVALID_ACCESS_ERR DontDelete|ReadOnly
@end
*/
DOMExceptionConstructor::DOMExceptionConstructor(ExecState* exec)
{
setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
}
bool DOMExceptionConstructor::getOwnPropertySlot(ExecState *exec, const Identifier& propertyName, PropertySlot& slot)
{
return getStaticValueSlot<DOMExceptionConstructor, DOMObject>(exec, &DOMExceptionConstructorTable, this, propertyName, slot);
}
JSValue* DOMExceptionConstructor::getValueProperty(ExecState*, int token) const
{
// We use the token as the value to return directly
return jsNumber(token);
}
JSObject* getDOMExceptionConstructor(ExecState* exec)
{
return cacheGlobalObject<DOMExceptionConstructor>(exec, "[[DOMException.constructor]]");
}
// -------------------------------------------------------------------------
const ClassInfo DOMNamedNodesCollection::info = { "Collection", 0, 0, 0 };
// Such a collection is usually very short-lived, it only exists
// for constructs like document.forms.<name>[1],
// so it shouldn't be a problem that it's storing all the nodes (with the same name). (David)
DOMNamedNodesCollection::DOMNamedNodesCollection(ExecState* exec, const DeprecatedValueList< RefPtr<Node> >& nodes )
: m_nodes(nodes)
{
setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
}
JSValue* DOMNamedNodesCollection::lengthGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
{
DOMNamedNodesCollection *thisObj = static_cast<DOMNamedNodesCollection*>(slot.slotBase());
return jsNumber(thisObj->m_nodes.count());
}
JSValue* DOMNamedNodesCollection::indexGetter(ExecState* exec, JSObject* originalObject, const Identifier& propertyName, const PropertySlot& slot)
{
DOMNamedNodesCollection *thisObj = static_cast<DOMNamedNodesCollection*>(slot.slotBase());
return toJS(exec, thisObj->m_nodes[slot.index()].get());
}
bool DOMNamedNodesCollection::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
if (propertyName == lengthPropertyName) {
slot.setCustom(this, lengthGetter);
return true;
}
// array index ?
bool ok;
unsigned idx = propertyName.toUInt32(&ok);
if (ok && idx < m_nodes.count()) {
slot.setCustomIndex(this, idx, indexGetter);
return true;
}
// For IE compatibility, we need to be able to look up elements in a
// document.formName.name result by id as well as be index.
AtomicString atomicPropertyName = propertyName;
DeprecatedValueListConstIterator< RefPtr<Node> > end = m_nodes.end();
int i = 0;
for (DeprecatedValueListConstIterator< RefPtr<Node> > it = m_nodes.begin(); it != end; ++it, ++i) {
Node* node = (*it).get();
if (node->hasAttributes() && node->attributes()->id() == atomicPropertyName) {
slot.setCustomIndex(this, i, indexGetter);
return true;
}
}
return DOMObject::getOwnPropertySlot(exec, propertyName, slot);
}
} // namespace