dom_elementimpl.cpp [plain text]
#include "dom/dom_exception.h"
#include "dom/dom_node.h"
#include "xml/dom_textimpl.h"
#include "xml/dom_docimpl.h"
#include "xml/dom2_eventsimpl.h"
#include "xml/dom_elementimpl.h"
#include "html/dtd.h"
#include "html/htmlparser.h"
#include "rendering/render_canvas.h"
#include "misc/htmlhashes.h"
#include "css/css_valueimpl.h"
#include "css/css_stylesheetimpl.h"
#include "css/cssstyleselector.h"
#include "xml/dom_xmlimpl.h"
#include <qtextstream.h>
#include <kdebug.h>
using namespace DOM;
using namespace khtml;
void AttributeImpl::allocateImpl(ElementImpl* e) {
_impl = new AttrImpl(e, e->docPtr(), this);
}
AttrImpl::AttrImpl(ElementImpl* element, DocumentPtr* docPtr, AttributeImpl* a)
: NodeBaseImpl(docPtr),
m_element(element),
m_attribute(a)
{
assert(!m_attribute->_impl);
m_attribute->_impl = this;
m_attribute->ref();
m_specified = true;
}
AttrImpl::~AttrImpl()
{
assert(m_attribute->_impl == this);
m_attribute->_impl = 0;
m_attribute->deref();
}
DOMString AttrImpl::nodeName() const
{
return getDocument()->attrName(m_attribute->id());
}
unsigned short AttrImpl::nodeType() const
{
return Node::ATTRIBUTE_NODE;
}
DOMString AttrImpl::prefix() const
{
return m_attribute->prefix();
}
void AttrImpl::setPrefix(const DOMString &_prefix, int &exceptioncode )
{
checkSetPrefix(_prefix, exceptioncode);
if (exceptioncode)
return;
m_attribute->setPrefix(_prefix.implementation());
}
DOMString AttrImpl::nodeValue() const {
return m_attribute->val();
}
void AttrImpl::setValue( const DOMString &v, int &exceptioncode )
{
exceptioncode = 0;
if (isReadOnly()) {
exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (v.isNull()) {
exceptioncode = DOMException::DOMSTRING_SIZE_ERR;
return;
}
m_attribute->setValue(v.implementation());
if (m_element)
m_element->parseAttribute(m_attribute);
}
void AttrImpl::setNodeValue( const DOMString &v, int &exceptioncode )
{
exceptioncode = 0;
setValue(v, exceptioncode);
}
NodeImpl *AttrImpl::cloneNode ( bool )
{
return new AttrImpl(0, docPtr(), new AttributeImpl(m_attribute->id(), m_attribute->val()));
}
bool AttrImpl::childAllowed( NodeImpl *newChild )
{
if(!newChild)
return false;
return childTypeAllowed(newChild->nodeType());
}
bool AttrImpl::childTypeAllowed( unsigned short type )
{
switch (type) {
case Node::TEXT_NODE:
case Node::ENTITY_REFERENCE_NODE:
return true;
break;
default:
return false;
}
}
DOMString AttrImpl::toString() const
{
DOMString result;
result += nodeName();
if (firstChild() != NULL) {
result += "=\"";
for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
result += child->toString();
}
result += "\"";
}
return result;
}
ElementImpl::ElementImpl(DocumentPtr *doc)
: NodeBaseImpl(doc)
{
namedAttrMap = 0;
m_styleDecls = 0;
m_prefix = 0;
}
ElementImpl::~ElementImpl()
{
if(namedAttrMap) {
namedAttrMap->detachFromElement();
namedAttrMap->deref();
}
if (m_styleDecls) {
m_styleDecls->setNode(0);
m_styleDecls->parent()->deref();
m_styleDecls->setParent(0);
m_styleDecls->deref();
}
if (m_prefix)
m_prefix->deref();
}
void ElementImpl::removeAttribute( NodeImpl::Id id, int &exceptioncode )
{
if (namedAttrMap) {
namedAttrMap->removeNamedItem(id, exceptioncode);
if (exceptioncode == DOMException::NOT_FOUND_ERR) {
exceptioncode = 0;
}
}
}
void ElementImpl::setAttribute(NodeImpl::Id id, const DOMString &value)
{
int exceptioncode = 0;
setAttribute(id,value.implementation(),exceptioncode);
}
unsigned short ElementImpl::nodeType() const
{
return Node::ELEMENT_NODE;
}
DOMString ElementImpl::getAttribute(NodeImpl::Id id) const
{
if (!namedAttrMap) return DOMString();
AttributeImpl* a = namedAttrMap->getAttributeItem(id);
if (a) return a->val();
NamedAttrMapImpl* dm = defaultMap();
if(!dm) return DOMString();
AttributeImpl* defattr = dm->getAttributeItem(id);
if (!defattr) return DOMString();
return defattr->val();
}
void ElementImpl::setAttribute(NodeImpl::Id id, DOMStringImpl* value, int &exceptioncode )
{
AttributeImpl* old = attributes(false)->getAttributeItem(id);
if (namedAttrMap->isReadOnly()) {
exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (id == ATTR_ID) {
updateId(old ? old->val() : 0, value);
}
if (old && !value)
namedAttrMap->removeAttribute(id);
else if (!old && value)
namedAttrMap->addAttribute(new AttributeImpl(id, value));
else if (old && value) {
old->setValue(value);
parseAttribute(old);
}
}
void ElementImpl::setAttributeMap( NamedAttrMapImpl* list )
{
AttributeImpl *oldId = namedAttrMap ? namedAttrMap->getAttributeItem(ATTR_ID) : 0;
AttributeImpl *newId = list ? list->getAttributeItem(ATTR_ID) : 0;
if (oldId || newId) {
updateId(oldId ? oldId->val() : 0, newId ? newId->val() : 0);
}
if(namedAttrMap)
namedAttrMap->deref();
namedAttrMap = list;
if(namedAttrMap) {
namedAttrMap->ref();
namedAttrMap->element = this;
unsigned int len = namedAttrMap->length();
for(unsigned int i = 0; i < len; i++)
parseAttribute(namedAttrMap->attrs[i]);
}
}
NodeImpl *ElementImpl::cloneNode(bool deep)
{
int exceptioncode;
ElementImpl *clone = getDocument()->createElement(tagName(), exceptioncode);
if (!clone) return 0;
if(namedAttrMap)
*(static_cast<NamedAttrMapImpl*>(clone->attributes())) = *namedAttrMap;
if (m_styleDecls)
*(clone->styleRules()) = *m_styleDecls;
if (deep)
cloneChildNodes(clone);
return clone;
}
DOMString ElementImpl::nodeName() const
{
return tagName();
}
DOMString ElementImpl::tagName() const
{
DOMString tn = getDocument()->tagName(id());
if (m_prefix)
return DOMString(m_prefix) + ":" + tn;
return tn;
}
void ElementImpl::setPrefix( const DOMString &_prefix, int &exceptioncode )
{
checkSetPrefix(_prefix, exceptioncode);
if (exceptioncode)
return;
if (m_prefix)
m_prefix->deref();
m_prefix = _prefix.implementation();
if (m_prefix)
m_prefix->ref();
}
void ElementImpl::createAttributeMap() const
{
namedAttrMap = new NamedAttrMapImpl(const_cast<ElementImpl*>(this));
namedAttrMap->ref();
}
NamedAttrMapImpl* ElementImpl::defaultMap() const
{
return 0;
}
RenderStyle *ElementImpl::styleForRenderer(RenderObject *parentRenderer)
{
return getDocument()->styleSelector()->styleForElement(this);
}
RenderObject *ElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
{
if (getDocument()->documentElement() == this && style->display() == NONE) {
RenderBlock* result = new (arena) RenderBlock(this);
if (result) result->setStyle(style);
return result;
}
return RenderObject::createObject(this, style);
}
void ElementImpl::attach()
{
#if SPEED_DEBUG < 1
createRendererIfNeeded();
#endif
NodeBaseImpl::attach();
NamedAttrMapImpl *attrs = attributes(true);
if (attrs) {
AttributeImpl *idAttr = attrs->getAttributeItem(ATTR_ID);
if (idAttr && idAttr->val()) {
updateId(0, idAttr->val());
}
}
}
void ElementImpl::detach()
{
NamedAttrMapImpl *attrs = attributes(true);
if (attrs) {
AttributeImpl *idAttr = attrs->getAttributeItem(ATTR_ID);
if (idAttr && idAttr->val()) {
updateId(idAttr->val(), 0);
}
}
NodeBaseImpl::detach();
}
void ElementImpl::recalcStyle( StyleChange change )
{
RenderStyle* _style = m_render ? m_render->style() : 0;
bool hasParentRenderer = parent() ? parent()->renderer() : false;
#if 0
const char* debug;
switch(change) {
case NoChange: debug = "NoChange";
break;
case NoInherit: debug= "NoInherit";
break;
case Inherit: debug = "Inherit";
break;
case Force: debug = "Force";
break;
}
qDebug("recalcStyle(%d: %s)[%p: %s]", change, debug, this, tagName().string().latin1());
#endif
if ( hasParentRenderer && (change >= Inherit || changed()) ) {
RenderStyle *newStyle = getDocument()->styleSelector()->styleForElement(this);
newStyle->ref();
StyleChange ch = diff( _style, newStyle );
if (ch == Detach) {
if (attached()) detach();
attach();
setChanged( false );
setHasChangedChild( false );
newStyle->deref();
return;
}
else if (ch != NoChange) {
if( m_render && newStyle ) {
m_render->setStyle(newStyle);
}
}
newStyle->deref();
if ( change != Force) {
if (getDocument()->usesDescendantRules())
change = Force;
else
change = ch;
}
}
NodeImpl *n;
for (n = _first; n; n = n->nextSibling()) {
if ( change >= Inherit || n->isTextNode() ||
n->hasChangedChild() || n->changed() )
n->recalcStyle( change );
}
setChanged( false );
setHasChangedChild( false );
}
bool ElementImpl::childAllowed( NodeImpl *newChild )
{
if (!childTypeAllowed(newChild->nodeType()))
return false;
if (isXMLElementNode() || newChild->isXMLElementNode())
return true;
else
return checkChild(id(), newChild->id());
}
bool ElementImpl::childTypeAllowed( unsigned short type )
{
switch (type) {
case Node::ELEMENT_NODE:
case Node::TEXT_NODE:
case Node::COMMENT_NODE:
case Node::PROCESSING_INSTRUCTION_NODE:
case Node::CDATA_SECTION_NODE:
case Node::ENTITY_REFERENCE_NODE:
return true;
break;
default:
return false;
}
}
void ElementImpl::createDecl( )
{
m_styleDecls = new CSSStyleDeclarationImpl(0);
m_styleDecls->ref();
m_styleDecls->setParent(getDocument()->elementSheet());
m_styleDecls->parent()->ref();
m_styleDecls->setNode(this);
m_styleDecls->setStrictParsing( !getDocument()->inCompatMode() );
}
void ElementImpl::dispatchAttrRemovalEvent(AttributeImpl *attr)
{
if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
return;
}
void ElementImpl::dispatchAttrAdditionEvent(AttributeImpl *attr)
{
if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
return;
}
DOMString ElementImpl::openTagStartToString() const
{
DOMString result = DOMString("<") + tagName();
NamedAttrMapImpl *attrMap = attributes(true);
if (attrMap) {
unsigned long numAttrs = attrMap->length();
for (unsigned long i = 0; i < numAttrs; i++) {
result += " ";
AttributeImpl *attribute = attrMap->attributeItem(i);
AttrImpl *attr = attribute->attrImpl();
if (attr) {
result += attr->toString();
} else {
result += getDocument()->attrName(attribute->id());
if (!attribute->value().isNull()) {
result += "=\"";
result += attribute->value();
result += "\"";
}
}
}
}
return result;
}
DOMString ElementImpl::toString() const
{
DOMString result = openTagStartToString();
if (hasChildNodes()) {
result += ">";
for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
result += child->toString();
}
result += "</";
result += tagName();
result += ">";
} else {
result += " />";
}
return result;
}
void ElementImpl::updateId(DOMStringImpl* oldId, DOMStringImpl* newId)
{
if (!attached())
return;
if (oldId == newId)
return;
DOMString oldIdStr(oldId);
DOMString newIdStr(newId);
DocumentImpl* doc = getDocument();
if (oldIdStr == newIdStr)
return;
if (!oldIdStr.isEmpty())
doc->removeElementById(oldIdStr, this);
if (!newIdStr.isEmpty())
doc->addElementById(newIdStr, this);
}
#ifndef NDEBUG
void ElementImpl::dump(QTextStream *stream, QString ind) const
{
if (namedAttrMap) {
for (uint i = 0; i < namedAttrMap->length(); i++) {
AttributeImpl *attr = namedAttrMap->attributeItem(i);
*stream << " " << DOMString(getDocument()->attrName(attr->id())).string().ascii()
<< "=\"" << DOMString(attr->value()).string().ascii() << "\"";
}
}
NodeBaseImpl::dump(stream,ind);
}
#endif
XMLElementImpl::XMLElementImpl(DocumentPtr *doc, DOMStringImpl *_tagName)
: ElementImpl(doc)
{
m_id = doc->document()->tagId(0 , _tagName, false );
}
XMLElementImpl::XMLElementImpl(DocumentPtr *doc, DOMStringImpl *_qualifiedName, DOMStringImpl *_namespaceURI)
: ElementImpl(doc)
{
int colonpos = -1;
for (uint i = 0; i < _qualifiedName->l; ++i)
if (_qualifiedName->s[i] == ':') {
colonpos = i;
break;
}
if (colonpos >= 0) {
DOMStringImpl* localName = _qualifiedName->copy();
localName->ref();
localName->remove(0,colonpos+1);
m_id = doc->document()->tagId(_namespaceURI, localName, false );
localName->deref();
m_prefix = _qualifiedName->copy();
m_prefix->ref();
m_prefix->truncate(colonpos);
}
else {
m_id = doc->document()->tagId(_namespaceURI, _qualifiedName, false );
m_prefix = 0;
}
}
XMLElementImpl::~XMLElementImpl()
{
}
DOMString XMLElementImpl::localName() const
{
return getDocument()->tagName(m_id);
}
NodeImpl *XMLElementImpl::cloneNode ( bool deep )
{
XMLElementImpl *clone = new XMLElementImpl(docPtr(), getDocument()->tagName(m_id).implementation());
clone->m_id = m_id;
if(namedAttrMap)
*(static_cast<NamedAttrMapImpl*>(clone->attributes())) = *namedAttrMap;
if (m_styleDecls)
*(clone->styleRules()) = *m_styleDecls;
if (deep)
cloneChildNodes(clone);
return clone;
}
NamedAttrMapImpl::NamedAttrMapImpl(ElementImpl *e)
: element(e)
{
attrs = 0;
len = 0;
}
NamedAttrMapImpl::~NamedAttrMapImpl()
{
clearAttributes();
}
AttrImpl *NamedAttrMapImpl::getNamedItem ( NodeImpl::Id id ) const
{
AttributeImpl* a = getAttributeItem(id);
if (!a) return 0;
if (!a->attrImpl())
a->allocateImpl(element);
return a->attrImpl();
}
Node NamedAttrMapImpl::setNamedItem ( NodeImpl* arg, int &exceptioncode )
{
if (!element) {
exceptioncode = DOMException::NOT_FOUND_ERR;
return 0;
}
if (isReadOnly()) {
exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
return 0;
}
if (arg->getDocument() != element->getDocument()) {
exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
return 0;
}
if (!arg->isAttributeNode()) {
exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
return 0;
}
AttrImpl *attr = static_cast<AttrImpl*>(arg);
AttributeImpl* a = attr->attrImpl();
AttributeImpl* old = getAttributeItem(a->id());
if (old == a) return arg;
if (attr->ownerElement()) {
exceptioncode = DOMException::INUSE_ATTRIBUTE_ERR;
return 0;
}
if (a->id() == ATTR_ID) {
element->updateId(old ? old->val() : 0, a->val());
}
Node r;
if (old) {
if (!old->attrImpl())
old->allocateImpl(element);
r = old->_impl;
removeAttribute(a->id());
}
addAttribute(a);
return r;
}
Node NamedAttrMapImpl::removeNamedItem ( NodeImpl::Id id, int &exceptioncode )
{
if (isReadOnly()) {
exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
return Node();
}
AttributeImpl* a = getAttributeItem(id);
if (!a) {
exceptioncode = DOMException::NOT_FOUND_ERR;
return Node();
}
if (!a->attrImpl()) a->allocateImpl(element);
Node r(a->attrImpl());
if (id == ATTR_ID) {
element->updateId(a->val(), 0);
}
removeAttribute(id);
return r;
}
AttrImpl *NamedAttrMapImpl::item ( unsigned long index ) const
{
if (index >= len)
return 0;
if (!attrs[index]->attrImpl())
attrs[index]->allocateImpl(element);
return attrs[index]->attrImpl();
}
unsigned long NamedAttrMapImpl::length( ) const
{
return len;
}
AttributeImpl* NamedAttrMapImpl::getAttributeItem(NodeImpl::Id id) const
{
for (unsigned long i = 0; i < len; ++i)
if (attrs[i]->id() == id)
return attrs[i];
return 0;
}
NodeImpl::Id NamedAttrMapImpl::mapId(const DOMString& namespaceURI,
const DOMString& localName, bool readonly)
{
assert(element);
if (!element) return 0;
return element->getDocument()->attrId(namespaceURI.implementation(),
localName.implementation(), readonly);
}
void NamedAttrMapImpl::clearAttributes()
{
if (attrs) {
uint i;
for (i = 0; i < len; i++) {
if (attrs[i]->_impl)
attrs[i]->_impl->m_element = 0;
attrs[i]->deref();
}
delete [] attrs;
attrs = 0;
}
len = 0;
}
void NamedAttrMapImpl::detachFromElement()
{
element = 0;
clearAttributes();
}
NamedAttrMapImpl& NamedAttrMapImpl::operator=(const NamedAttrMapImpl& other)
{
if (!element) return *this;
AttributeImpl *oldId = getAttributeItem(ATTR_ID);
AttributeImpl *newId = other.getAttributeItem(ATTR_ID);
if (oldId || newId) {
element->updateId(oldId ? oldId->val() : 0, newId ? newId->val() : 0);
}
clearAttributes();
len = other.len;
attrs = new AttributeImpl* [len];
for (uint i = 0; i < len; i++) {
attrs[i] = new AttributeImpl(other.attrs[i]->id(), other.attrs[i]->val());
attrs[i]->ref();
}
for(uint i = 0; i < len; i++)
element->parseAttribute(attrs[i]);
return *this;
}
void NamedAttrMapImpl::addAttribute(AttributeImpl *attr)
{
AttributeImpl **newAttrs = new AttributeImpl* [len+1];
if (attrs) {
for (uint i = 0; i < len; i++)
newAttrs[i] = attrs[i];
delete [] attrs;
}
attrs = newAttrs;
attrs[len++] = attr;
attr->ref();
AttrImpl * const attrImpl = attr->_impl;
if (attrImpl)
attrImpl->m_element = element;
if (element) {
element->parseAttribute(attr);
element->dispatchAttrAdditionEvent(attr);
element->dispatchSubtreeModifiedEvent();
}
}
void NamedAttrMapImpl::removeAttribute(NodeImpl::Id id)
{
unsigned long index = len+1;
for (unsigned long i = 0; i < len; ++i)
if (attrs[i]->id() == id) {
index = i;
break;
}
if (index >= len) return;
AttributeImpl* attr = attrs[index];
if (attrs[index]->_impl)
attrs[index]->_impl->m_element = 0;
if (len == 1) {
delete [] attrs;
attrs = 0;
len = 0;
}
else {
AttributeImpl **newAttrs = new AttributeImpl* [len-1];
uint i;
for (i = 0; i < uint(index); i++)
newAttrs[i] = attrs[i];
len--;
for (; i < len; i++)
newAttrs[i] = attrs[i+1];
delete [] attrs;
attrs = newAttrs;
}
if (element && attr->_value) {
DOMStringImpl* value = attr->_value;
attr->_value = 0;
element->parseAttribute(attr);
attr->_value = value;
}
if (element) {
element->dispatchAttrRemovalEvent(attr);
element->dispatchSubtreeModifiedEvent();
}
attr->deref();
}