📄 dom_elementimpl.cpp
字号:
/**
* This file is part of the DOM implementation for KDE.
*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Peter Kelly (pmk@post.com)
* (C) 2001 Dirk Mueller (mueller@kde.org)
* Copyright (C) 2004 Apple Computer, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
//#define EVENT_DEBUG
#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 "khtml_part.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;
AttributeImpl* AttributeImpl::clone(bool) const
{
AttributeImpl* result = new AttributeImpl(m_id, _value);
result->setPrefix(_prefix);
return result;
}
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->value();
}
void AttrImpl::setValue( const DOMString &v, int &exceptioncode )
{
exceptioncode = 0;
// ### according to the DOM docs, we should create an unparsed Text child
// node here
// do not interprete entities in the string, its literal!
// NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
if (isReadOnly()) {
exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
return;
}
// ### what to do on 0 ?
if (v.isNull()) {
exceptioncode = DOMException::DOMSTRING_SIZE_ERR;
return;
}
m_attribute->setValue(v.implementation());
if (m_element)
m_element->attributeChanged(m_attribute);
}
void AttrImpl::setNodeValue( const DOMString &v, int &exceptioncode )
{
exceptioncode = 0;
// NO_MODIFICATION_ALLOWED_ERR: taken care of by setValue()
setValue(v, exceptioncode);
}
NodeImpl *AttrImpl::cloneNode ( bool /*deep*/)
{
return new AttrImpl(0, docPtr(), m_attribute->clone());
}
// DOM Section 1.1.1
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();
// FIXME: substitute entities for any instances of " or ' --
// maybe easier to just use text value and ignore existing
// entity refs?
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_prefix = 0;
}
ElementImpl::~ElementImpl()
{
if (namedAttrMap) {
namedAttrMap->detachFromElement();
namedAttrMap->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);
}
NamedAttrMapImpl* ElementImpl::attributes(bool readonly) const
{
updateStyleAttributeIfNeeded();
if (!readonly && !namedAttrMap) createAttributeMap();
return namedAttrMap;
}
unsigned short ElementImpl::nodeType() const
{
return Node::ELEMENT_NODE;
}
const AtomicStringList* ElementImpl::getClassList() const
{
return 0;
}
const AtomicString& ElementImpl::getIDAttribute() const
{
return namedAttrMap ? namedAttrMap->id() : nullAtom;
}
const AtomicString& ElementImpl::getAttribute(NodeImpl::Id id) const
{
if (id == ATTR_STYLE)
updateStyleAttributeIfNeeded();
if (namedAttrMap) {
AttributeImpl* a = namedAttrMap->getAttributeItem(id);
if (a) return a->value();
}
return nullAtom;
}
const AtomicString& ElementImpl::getAttributeNS(const DOMString &namespaceURI,
const DOMString &localName) const
{
NodeImpl::Id id = getDocument()->attrId(namespaceURI.implementation(),
localName.implementation(), true);
if (!id) return nullAtom;
return getAttribute(id);
}
void ElementImpl::setAttribute(NodeImpl::Id id, DOMStringImpl* value, int &exceptioncode )
{
if (inDocument())
getDocument()->incDOMTreeVersion();
// allocate attributemap if necessary
AttributeImpl* old = attributes(false)->getAttributeItem(id);
// NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
if (namedAttrMap->isReadOnly()) {
exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
return;
}
if (id == ATTR_ID) {
updateId(old ? old->value() : nullAtom, value);
}
if (old && !value)
namedAttrMap->removeAttribute(id);
else if (!old && value)
namedAttrMap->addAttribute(createAttribute(id, value));
else if (old && value) {
old->setValue(value);
attributeChanged(old);
}
}
AttributeImpl* ElementImpl::createAttribute(NodeImpl::Id id, DOMStringImpl* value)
{
return new AttributeImpl(id, value);
}
void ElementImpl::setAttributeMap( NamedAttrMapImpl* list )
{
if (inDocument())
getDocument()->incDOMTreeVersion();
// If setting the whole map changes the id attribute, we need to
// call updateId.
AttributeImpl *oldId = namedAttrMap ? namedAttrMap->getAttributeItem(ATTR_ID) : 0;
AttributeImpl *newId = list ? list->getAttributeItem(ATTR_ID) : 0;
if (oldId || newId) {
updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom);
}
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++)
attributeChanged(namedAttrMap->attrs[i]);
}
}
bool ElementImpl::hasAttributes() const
{
updateStyleAttributeIfNeeded();
return namedAttrMap && namedAttrMap->length() > 0;
}
NodeImpl *ElementImpl::cloneNode(bool deep)
{
// ### we lose the namespace here ... FIXME
int exceptioncode;
ElementImpl *clone = getDocument()->createElement(tagName(), exceptioncode);
if (!clone) return 0;
// clone attributes
if (namedAttrMap)
*clone->attributes() = *namedAttrMap;
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();
}
bool ElementImpl::isURLAttribute(AttributeImpl *attr) const
{
return false;
}
RenderStyle *ElementImpl::styleForRenderer(RenderObject *parentRenderer)
{
return getDocument()->styleSelector()->styleForElement(this);
}
RenderObject *ElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
{
if (getDocument()->documentElement() == this && style->display() == NONE) {
// Ignore display: none on root elements. Force a display of block in that case.
RenderBlock* result = new (arena) RenderBlock(this);
if (result) result->setStyle(style);
return result;
}
return RenderObject::createObject(this, style);
}
void ElementImpl::insertedIntoDocument()
{
// need to do superclass processing first so inDocument() is true
// by the time we reach updateId
NodeBaseImpl::insertedIntoDocument();
if (hasID()) {
NamedAttrMapImpl *attrs = attributes(true);
if (attrs) {
AttributeImpl *idAttr = attrs->getAttributeItem(ATTR_ID);
if (idAttr && !idAttr->isNull()) {
updateId(nullAtom, idAttr->value());
}
}
}
}
void ElementImpl::removedFromDocument()
{
if (hasID()) {
NamedAttrMapImpl *attrs = attributes(true);
if (attrs) {
AttributeImpl *idAttr = attrs->getAttributeItem(ATTR_ID);
if (idAttr && !idAttr->isNull()) {
updateId(idAttr->value(), nullAtom);
}
}
}
NodeBaseImpl::removedFromDocument();
}
void ElementImpl::attach()
{
#if SPEED_DEBUG < 1
createRendererIfNeeded();
#endif
NodeBaseImpl::attach();
}
void ElementImpl::recalcStyle( StyleChange change )
{
// ### should go away and be done in renderobject
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();
// ### Suboptimal. Style gets calculated again.
attach();
// attach recalulates the style for all children. No need to do it twice.
setChanged( false );
setHasChangedChild( false );
newStyle->deref(getDocument()->renderArena());
return;
}
else if (ch != NoChange) {
if( m_render && newStyle ) {
//qDebug("--> setting style on render element bgcolor=%s", newStyle->backgroundColor().name().latin1());
m_render->setStyle(newStyle);
}
}
else if (changed() && m_render && newStyle && (getDocument()->usesSiblingRules() || getDocument()->usesDescendantRules())) {
// Although no change occurred, we use the new style so that the cousin style sharing code won't get
// fooled into believing this style is the same. This is only necessary if the document actually uses
// sibling/descendant rules, since otherwise it isn't possible for ancestor styles to affect sharing of
// descendants.
m_render->setStyleInternal(newStyle);
}
newStyle->deref(getDocument()->renderArena());
if ( change != Force) {
if (getDocument()->usesDescendantRules())
change = Force;
else
change = ch;
}
}
NodeImpl *n;
for (n = _first; n; n = n->nextSibling()) {
//qDebug(" (%p) calling recalcStyle on child %s/%p, change=%d", this, n, n->isElementNode() ? ((ElementImpl *)n)->tagName().string().latin1() : n->isTextNode() ? "text" : "unknown", change );
if ( change >= Inherit || n->isTextNode() ||
n->hasChangedChild() || n->changed() )
n->recalcStyle( change );
}
setChanged( false );
setHasChangedChild( false );
}
// DOM Section 1.1.1
bool ElementImpl::childAllowed( NodeImpl *newChild )
{
if (!childTypeAllowed(newChild->nodeType()))
return false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -