File indexing completed on 2024-05-05 16:11:38
0001 /** 0002 * This file is part of the DOM implementation for KDE. 0003 * 0004 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 0005 * (C) 1999 Antti Koivisto (koivisto@kde.org) 0006 * (C) 2001 Peter Kelly (pmk@post.com) 0007 * (C) 2001 Dirk Mueller (mueller@kde.org) 0008 * (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. 0009 * (C) 2005, 2008 Maksim Orlovich (maksim@kde.org) 0010 * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) 0011 * 0012 * This library is free software; you can redistribute it and/or 0013 * modify it under the terms of the GNU Library General Public 0014 * License as published by the Free Software Foundation; either 0015 * version 2 of the License, or (at your option) any later version. 0016 * 0017 * This library is distributed in the hope that it will be useful, 0018 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0019 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0020 * Library General Public License for more details. 0021 * 0022 * You should have received a copy of the GNU Library General Public License 0023 * along with this library; see the file COPYING.LIB. If not, write to 0024 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0025 * Boston, MA 02110-1301, USA. 0026 */ 0027 0028 //#define EVENT_DEBUG 0029 0030 #include "dom_elementimpl.h" 0031 0032 #include <dom/dom_exception.h> 0033 #include <dom/dom_node.h> 0034 #include <dom/html_image.h> 0035 #include "dom_textimpl.h" 0036 #include "dom_docimpl.h" 0037 #include "dom2_eventsimpl.h" 0038 #include "dom_restyler.h" 0039 #include "dom_xmlimpl.h" 0040 0041 #include <html/dtd.h> 0042 #include <html/htmlparser.h> 0043 #include <html/html_imageimpl.h> 0044 0045 #include <rendering/render_canvas.h> 0046 #include <css/css_valueimpl.h> 0047 #include <css/css_stylesheetimpl.h> 0048 #include <css/cssstyleselector.h> 0049 #include <css/cssvalues.h> 0050 #include <css/cssproperties.h> 0051 #include <khtml_part.h> 0052 #include <khtmlview.h> 0053 0054 #include <editing/editing_p.h> 0055 #include <editing/editor.h> 0056 0057 #include <QTextStream> 0058 #include <QTextDocument> 0059 #include "khtml_debug.h" 0060 #include <stdlib.h> 0061 0062 #include <wtf/HashMap.h> 0063 0064 // ### support default attributes 0065 // ### dispatch mutation events 0066 // ### check for INVALID_CHARACTER_ERR where appropriate 0067 0068 using namespace khtml; 0069 0070 namespace DOM 0071 { 0072 0073 AttrImpl::AttrImpl(ElementImpl *element, DocumentImpl *docPtr, NamespaceName namespacename, LocalName localName, PrefixName prefix, DOMStringImpl *value) 0074 : NodeBaseImpl(docPtr) 0075 { 0076 m_value = value; 0077 m_value->ref(); 0078 0079 m_namespace = namespacename; 0080 m_localName = localName; 0081 m_prefix = prefix; 0082 0083 // When creating the text node initially, we want element = 0, 0084 // so we don't attempt to update the getElementById cache or 0085 // call parseAttribute, etc. This is because we're normally lazily, 0086 // from previous attributes, so there is nothing really changing 0087 m_element = nullptr; 0088 createTextChild(); 0089 m_element = element; 0090 } 0091 0092 AttrImpl::~AttrImpl() 0093 { 0094 m_value->deref(); 0095 } 0096 0097 DOMString AttrImpl::nodeName() const 0098 { 0099 return name(); 0100 } 0101 0102 unsigned short AttrImpl::nodeType() const 0103 { 0104 return Node::ATTRIBUTE_NODE; 0105 } 0106 0107 DOMString AttrImpl::prefix() const 0108 { 0109 return m_prefix.toString(); 0110 } 0111 0112 void AttrImpl::setPrefix(const DOMString &_prefix, int &exceptioncode) 0113 { 0114 checkSetPrefix(_prefix, exceptioncode); 0115 if (exceptioncode) { 0116 return; 0117 } 0118 0119 m_prefix = PrefixName::fromString(_prefix); 0120 } 0121 0122 DOMString AttrImpl::namespaceURI() const 0123 { 0124 if (m_htmlCompat) { 0125 return DOMString(); 0126 } 0127 return m_namespace.toString(); 0128 } 0129 0130 DOMString AttrImpl::localName() const 0131 { 0132 return m_localName.toString(); 0133 } 0134 0135 DOMString AttrImpl::nodeValue() const 0136 { 0137 return m_value; 0138 } 0139 0140 DOMString AttrImpl::name() const 0141 { 0142 DOMString n = m_localName.toString(); 0143 0144 // compat mode always return attribute names in lowercase. 0145 // that's not formally in the specification, but common 0146 // practice - a w3c erratum to DOM L2 is pending. 0147 if (m_htmlCompat) { 0148 n = n.lower(); 0149 } 0150 0151 DOMString p = m_prefix.toString(); 0152 if (!p.isEmpty()) { 0153 return p + DOMString(":") + n; 0154 } 0155 0156 return n; 0157 } 0158 0159 void AttrImpl::createTextChild() 0160 { 0161 // add a text node containing the attribute value 0162 if (m_value->length() > 0) { 0163 TextImpl *textNode = ownerDocument()->createTextNode(m_value); 0164 0165 // We want to use addChild and not appendChild here to avoid triggering 0166 // mutation events. childrenChanged() will still be called. 0167 addChild(textNode); 0168 } 0169 } 0170 0171 void AttrImpl::childrenChanged() 0172 { 0173 NodeBaseImpl::childrenChanged(); 0174 0175 // update value 0176 DOMStringImpl *oldVal = m_value; 0177 m_value = new DOMStringImpl((QChar *)nullptr, 0); 0178 m_value->ref(); 0179 for (NodeImpl *n = firstChild(); n; n = n->nextSibling()) { 0180 DOMStringImpl *data = static_cast<const TextImpl *>(n)->string(); 0181 m_value->append(data); 0182 } 0183 0184 if (m_element) { 0185 int curr = id(); 0186 if (curr == ATTR_ID) { 0187 m_element->updateId(oldVal, m_value); 0188 } 0189 m_element->parseAttribute(this); 0190 m_element->attributeChanged(curr); 0191 } 0192 0193 oldVal->deref(); 0194 } 0195 0196 void AttrImpl::setValue(const DOMString &v, int &exceptioncode) 0197 { 0198 exceptioncode = 0; 0199 0200 // do not interpret entities in the string, it is literal! 0201 0202 // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly 0203 if (isReadOnly()) { 0204 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 0205 return; 0206 } 0207 0208 // ### what to do on 0 ? 0209 if (v.isNull()) { 0210 exceptioncode = DOMException::DOMSTRING_SIZE_ERR; 0211 return; 0212 } 0213 0214 if (m_value == v.implementation()) { 0215 return; 0216 } 0217 0218 int e = 0; 0219 removeChildren(); 0220 appendChild(ownerDocument()->createTextNode(v.implementation()), e); 0221 } 0222 0223 void AttrImpl::rewriteValue(const DOMString &newValue) 0224 { 0225 int ec; 0226 0227 // We want to avoid any notifications, so temporarily set m_element to 0 0228 ElementImpl *saveElement = m_element; 0229 m_element = nullptr; 0230 setValue(newValue, ec); 0231 m_element = saveElement; 0232 } 0233 0234 void AttrImpl::setNodeValue(const DOMString &v, int &exceptioncode) 0235 { 0236 exceptioncode = 0; 0237 // NO_MODIFICATION_ALLOWED_ERR: taken care of by setValue() 0238 setValue(v, exceptioncode); 0239 } 0240 0241 WTF::PassRefPtr<NodeImpl> AttrImpl::cloneNode(bool /*deep*/) 0242 { 0243 AttrImpl *attr = new AttrImpl(nullptr, docPtr(), m_namespace, m_localName, m_prefix, m_value); 0244 attr->setHTMLCompat(m_htmlCompat); 0245 return attr; 0246 } 0247 0248 // DOM Section 1.1.1 0249 bool AttrImpl::childAllowed(NodeImpl *newChild) 0250 { 0251 if (!newChild) { 0252 return false; 0253 } 0254 0255 return childTypeAllowed(newChild->nodeType()); 0256 } 0257 0258 bool AttrImpl::childTypeAllowed(unsigned short type) 0259 { 0260 switch (type) { 0261 case Node::TEXT_NODE: 0262 case Node::ENTITY_REFERENCE_NODE: 0263 return true; 0264 break; 0265 default: 0266 return false; 0267 } 0268 } 0269 0270 DOMString AttrImpl::toString() const 0271 { 0272 DOMString result; 0273 0274 result += nodeName(); 0275 0276 // FIXME: substitute entities for any instances of " or ' -- 0277 // maybe easier to just use text value and ignore existing 0278 // entity refs? 0279 0280 if (!nodeValue().isEmpty()) { 0281 //remove the else once the AttributeImpl changes are merged 0282 result += "=\""; 0283 result += nodeValue(); 0284 result += "\""; 0285 } 0286 0287 return result; 0288 } 0289 0290 void AttrImpl::setElement(ElementImpl *element) 0291 { 0292 m_element = element; 0293 } 0294 0295 // ------------------------------------------------------------------------- 0296 0297 void AttributeImpl::setValue(DOMStringImpl *value, ElementImpl *element) 0298 { 0299 assert(value); 0300 if (m_localName.id()) { 0301 if (m_data.value == value) { 0302 return; 0303 } 0304 0305 if (element && id() == ATTR_ID) { 0306 element->updateId(m_data.value, value); 0307 } 0308 0309 m_data.value->deref(); 0310 m_data.value = value; 0311 m_data.value->ref(); 0312 0313 if (element) { 0314 element->parseAttribute(this); 0315 element->attributeChanged(id()); 0316 } 0317 } else { 0318 int exceptioncode = 0; 0319 m_data.attr->setValue(value, exceptioncode); 0320 // AttrImpl::setValue() calls parseAttribute() 0321 } 0322 } 0323 0324 void AttributeImpl::rewriteValue(const DOMString &newValue) 0325 { 0326 if (m_localName.id()) { 0327 // We may have m_data.value == null if we were given a normalized value 0328 // off a removeAttribute (which would call parseNullAttribute()). 0329 // Ignore such requests. 0330 if (!m_data.value) { 0331 return; 0332 } 0333 0334 DOMStringImpl *value = newValue.implementation(); 0335 if (m_data.value == value) { 0336 return; 0337 } 0338 0339 m_data.value->deref(); 0340 m_data.value = value; 0341 m_data.value->ref(); 0342 } else { 0343 m_data.attr->rewriteValue(newValue); 0344 } 0345 } 0346 0347 AttrImpl *AttributeImpl::createAttr(ElementImpl *element, DocumentImpl *docPtr) 0348 { 0349 if (m_localName.id()) { 0350 AttrImpl *attr = new AttrImpl(element, docPtr, m_namespace, m_localName, m_prefix, m_data.value); 0351 if (!attr) { 0352 return nullptr; 0353 } 0354 attr->setHTMLCompat(element->htmlCompat()); 0355 m_data.value->deref(); 0356 m_data.attr = attr; 0357 m_data.attr->ref(); 0358 m_localName = emptyLocalName; /* "has implementation" flag */ 0359 } 0360 0361 return m_data.attr; 0362 } 0363 0364 void AttributeImpl::free() 0365 { 0366 if (m_localName.id()) { 0367 m_data.value->deref(); 0368 } else { 0369 m_data.attr->setElement(nullptr); 0370 m_data.attr->deref(); 0371 } 0372 } 0373 0374 // ------------------------------------------------------------------------- 0375 0376 class ElementRareDataImpl 0377 { 0378 public: 0379 ElementRareDataImpl(); 0380 void resetComputedStyle(); 0381 short tabIndex() const 0382 { 0383 return m_tabIndex; 0384 } 0385 void setTabIndex(short _tabIndex) 0386 { 0387 m_tabIndex = _tabIndex; 0388 m_hasTabIndex = true; 0389 } 0390 0391 RenderStyle *m_computedStyle; 0392 signed short m_tabIndex; 0393 bool m_hasTabIndex; 0394 }; 0395 0396 typedef WTF::HashMap<const ElementImpl *, ElementRareDataImpl *> ElementRareDataMap; 0397 0398 static ElementRareDataMap &rareDataMap() 0399 { 0400 static ElementRareDataMap *dataMap = new ElementRareDataMap; 0401 return *dataMap; 0402 } 0403 0404 static ElementRareDataImpl *rareDataFromMap(const ElementImpl *element) 0405 { 0406 return rareDataMap().get(element); 0407 } 0408 0409 inline ElementRareDataImpl::ElementRareDataImpl() 0410 : m_computedStyle(nullptr), m_tabIndex(0), m_hasTabIndex(false) 0411 {} 0412 0413 void ElementRareDataImpl::resetComputedStyle() 0414 { 0415 if (!m_computedStyle) { 0416 return; 0417 } 0418 m_computedStyle->deref(); 0419 m_computedStyle = nullptr; 0420 } 0421 0422 // ------------------------------------------------------------------------- 0423 0424 ElementImpl::ElementImpl(DocumentImpl *doc) 0425 : NodeBaseImpl(doc) 0426 { 0427 namedAttrMap = nullptr; 0428 m_style.inlineDecls = nullptr; 0429 m_prefix = emptyPrefixName; 0430 } 0431 0432 ElementImpl::~ElementImpl() 0433 { 0434 if (namedAttrMap) { 0435 namedAttrMap->detachFromElement(); 0436 namedAttrMap->deref(); 0437 } 0438 0439 if (m_style.inlineDecls) { 0440 if (CSSStyleDeclarationImpl *ild = inlineStyleDecls()) { 0441 // remove inline declarations 0442 ild->setNode(nullptr); 0443 ild->setParent(nullptr); 0444 ild->deref(); 0445 } 0446 if (CSSStyleDeclarationImpl *ncd = nonCSSStyleDecls()) { 0447 // remove presentational declarations 0448 ncd->setNode(nullptr); 0449 ncd->setParent(nullptr); 0450 ncd->deref(); 0451 delete m_style.combinedDecls; 0452 } 0453 } 0454 0455 if (!m_elementHasRareData) { 0456 ASSERT(!rareDataMap().contains(this)); 0457 } else { 0458 ElementRareDataMap &dataMap = rareDataMap(); 0459 ElementRareDataMap::iterator it = dataMap.find(this); 0460 ASSERT(it != dataMap.end()); 0461 delete it->second; 0462 dataMap.remove(it); 0463 } 0464 } 0465 0466 ElementRareDataImpl *ElementImpl::rareData() 0467 { 0468 return m_elementHasRareData ? rareDataFromMap(this) : nullptr; 0469 } 0470 0471 const ElementRareDataImpl *ElementImpl::rareData() const 0472 { 0473 return m_elementHasRareData ? rareDataFromMap(this) : nullptr; 0474 } 0475 0476 ElementRareDataImpl *ElementImpl::createRareData() 0477 { 0478 if (m_elementHasRareData) { 0479 return rareDataMap().get(this); 0480 } 0481 ASSERT(!rareDataMap().contains(this)); 0482 ElementRareDataImpl *data = new ElementRareDataImpl(); 0483 rareDataMap().set(this, data); 0484 m_elementHasRareData = true; 0485 return data; 0486 } 0487 0488 void ElementImpl::removeAttribute(NodeImpl::Id id, int &exceptioncode) 0489 { 0490 if (namedAttrMap) { 0491 namedAttrMap->removeNamedItem(id, emptyPrefixName, false, exceptioncode); 0492 if (exceptioncode == DOMException::NOT_FOUND_ERR) { 0493 exceptioncode = 0; 0494 } 0495 } 0496 } 0497 0498 unsigned short ElementImpl::nodeType() const 0499 { 0500 return Node::ELEMENT_NODE; 0501 } 0502 0503 DOMString ElementImpl::localName() const 0504 { 0505 return LocalName::fromId(id()).toString(); 0506 } 0507 0508 DOMString ElementImpl::tagName() const 0509 { 0510 DOMString tn = LocalName::fromId(id()).toString(); 0511 0512 if (m_htmlCompat) { 0513 tn = tn.upper(); 0514 } 0515 0516 DOMString prefix = m_prefix.toString(); 0517 if (!prefix.isEmpty()) { 0518 return prefix + DOMString(":") + tn; 0519 } 0520 0521 return tn; 0522 } 0523 0524 DOMString ElementImpl::nonCaseFoldedTagName() const 0525 { 0526 DOMString tn = LocalName::fromId(id()).toString(); 0527 0528 DOMString prefix = m_prefix.toString(); 0529 if (!prefix.isEmpty()) { 0530 return prefix + DOMString(":") + tn; 0531 } 0532 0533 return tn; 0534 } 0535 0536 /*DOMStringImpl* ElementImpl::getAttributeImpl(NodeImpl::Id id, PrefixName prefix, bool nsAware) const 0537 { 0538 return namedAttrMap ? namedAttrMap->getValue(id, prefix, nsAware) : 0; 0539 }*/ 0540 0541 void ElementImpl::setAttribute(NodeImpl::Id id, const PrefixName &prefix, bool nsAware, const DOMString &value, int &exceptioncode) 0542 { 0543 // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly 0544 if (isReadOnly()) { 0545 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 0546 return; 0547 } 0548 attributes()->setValue(id, value.implementation(), prefix, nsAware); 0549 } 0550 0551 void ElementImpl::setAttributeNS(const DOMString &namespaceURI, const DOMString &qualifiedName, 0552 const DOMString &value, int &exceptioncode) 0553 { 0554 // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly 0555 if (isReadOnly()) { 0556 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 0557 return; 0558 } 0559 int colonPos; 0560 if (!DOM::checkQualifiedName(qualifiedName, namespaceURI, &colonPos, 0561 false/*nameCanBeNull*/, false/*nameCanBeEmpty*/, 0562 &exceptioncode)) { 0563 return; 0564 } 0565 LocalName localname; 0566 PrefixName prefixname; 0567 splitPrefixLocalName(qualifiedName, prefixname, localname, m_htmlCompat, colonPos); 0568 NamespaceName namespacename = NamespaceName::fromString(namespaceURI); 0569 attributes()->setValue(makeId(namespacename.id(), localname.id()), value.implementation(), prefixname, true /*nsAware*/); 0570 } 0571 0572 void ElementImpl::setAttribute(NodeImpl::Id id, const DOMString &value) 0573 { 0574 int exceptioncode = 0; 0575 setAttribute(id, emptyPrefixName, false, value, exceptioncode); 0576 } 0577 0578 void ElementImpl::setBooleanAttribute(NodeImpl::Id id, bool b) 0579 { 0580 if (b) { 0581 setAttribute(id, "1"); 0582 } else { 0583 int ec; 0584 removeAttribute(id, ec); 0585 } 0586 } 0587 0588 void ElementImpl::setAttributeMap(NamedAttrMapImpl *list) 0589 { 0590 // If setting the whole map changes the id attribute, we need to 0591 // call updateId. 0592 DOMStringImpl *oldId = namedAttrMap ? namedAttrMap->getValue(ATTR_ID) : nullptr; 0593 DOMStringImpl *newId = list ? list->getValue(ATTR_ID) : nullptr; 0594 0595 if (oldId || newId) { 0596 updateId(oldId, newId); 0597 } 0598 0599 if (namedAttrMap) { 0600 namedAttrMap->detachFromElement(); 0601 namedAttrMap->deref(); 0602 } 0603 0604 namedAttrMap = list; 0605 0606 if (namedAttrMap) { 0607 namedAttrMap->ref(); 0608 assert(namedAttrMap->m_element == nullptr); 0609 namedAttrMap->setElement(this); 0610 unsigned len = namedAttrMap->length(); 0611 for (unsigned i = 0; i < len; i++) { 0612 parseAttribute(&namedAttrMap->m_attrs[i]); 0613 attributeChanged(namedAttrMap->m_attrs[i].id()); 0614 } 0615 } 0616 } 0617 0618 WTF::PassRefPtr<NodeImpl> ElementImpl::cloneNode(bool deep) 0619 { 0620 WTF::RefPtr<ElementImpl> clone; // Make sure to guard... 0621 clone = document()->createElementNS(namespaceURI(), nonCaseFoldedTagName() /* includes prefix*/); 0622 if (!clone) { 0623 return nullptr; 0624 } 0625 finishCloneNode(clone.get(), deep); 0626 return clone; 0627 } 0628 0629 void ElementImpl::finishCloneNode(ElementImpl *clone, bool deep) 0630 { 0631 // clone attributes 0632 if (namedAttrMap || m_needsStyleAttributeUpdate) { 0633 clone->attributes()->copyAttributes(attributes(true)); 0634 } 0635 0636 assert(!m_needsStyleAttributeUpdate); // ensured by previous line 0637 0638 // clone individual style rules 0639 if (m_style.inlineDecls) { 0640 if (m_hasCombinedStyle) { 0641 if (!clone->m_hasCombinedStyle) { 0642 clone->createNonCSSDecl(); 0643 } 0644 if (m_style.combinedDecls->inlineDecls) { 0645 *(clone->getInlineStyleDecls()) = *m_style.combinedDecls->inlineDecls; 0646 } 0647 *clone->m_style.combinedDecls->nonCSSDecls = *m_style.combinedDecls->nonCSSDecls; 0648 } else { 0649 *(clone->getInlineStyleDecls()) = *m_style.inlineDecls; 0650 } 0651 } 0652 0653 // ### fold above style cloning into this function? 0654 clone->copyNonAttributeProperties(this); 0655 0656 if (deep) { 0657 cloneChildNodes(clone); 0658 } 0659 0660 // copy over our compatibility mode. 0661 clone->setHTMLCompat(htmlCompat()); 0662 } 0663 0664 bool ElementImpl::hasAttributes() const 0665 { 0666 return namedAttrMap && namedAttrMap->length() > 0; 0667 } 0668 0669 bool ElementImpl::hasAttribute(const DOMString &name) const 0670 { 0671 LocalName localname; 0672 PrefixName prefixname; 0673 splitPrefixLocalName(name, prefixname, localname, m_htmlCompat); 0674 if (!localname.id()) { 0675 return false; 0676 } 0677 if (!namedAttrMap) { 0678 return false; 0679 } 0680 return namedAttrMap->getValue(makeId(emptyNamespace, localname.id()), prefixname, false) != nullptr; 0681 } 0682 0683 bool ElementImpl::hasAttributeNS(const DOMString &namespaceURI, 0684 const DOMString &localName) const 0685 { 0686 NamespaceName namespacename = NamespaceName::fromString(namespaceURI); 0687 LocalName localname = LocalName::fromString(localName, m_htmlCompat ? IDS_NormalizeLower : IDS_CaseSensitive); 0688 NodeImpl::Id id = makeId(namespacename.id(), localname.id()); 0689 if (!id) { 0690 return false; 0691 } 0692 if (!namedAttrMap) { 0693 return false; 0694 } 0695 return namedAttrMap->getValue(id, emptyPrefixName, true) != nullptr; 0696 } 0697 0698 DOMString ElementImpl::getAttribute(const DOMString &name) 0699 { 0700 LocalName localname; 0701 PrefixName prefixname; 0702 splitPrefixLocalName(name, prefixname, localname, m_htmlCompat); 0703 if (!localname.id()) { 0704 return DOMString(); 0705 } 0706 return getAttribute(makeId(emptyNamespace, localname.id()), prefixname, false /*nsAware*/); 0707 } 0708 0709 void ElementImpl::setAttribute(const DOMString &name, const DOMString &value, int &exceptioncode) 0710 { 0711 int colon; 0712 if (!DOM::checkQualifiedName(name, "", &colon, false/*nameCanBeNull*/, false/*nameCanBeEmpty*/, &exceptioncode)) { 0713 return; 0714 } 0715 LocalName localname; 0716 PrefixName prefixname; 0717 splitPrefixLocalName(name, prefixname, localname, m_htmlCompat, colon); 0718 setAttribute(makeId(emptyNamespace, localname.id()), prefixname, false, value.implementation(), exceptioncode); 0719 } 0720 0721 void ElementImpl::removeAttribute(const DOMString &name, int &exceptioncode) 0722 { 0723 LocalName localname; 0724 PrefixName prefixname; 0725 splitPrefixLocalName(name, prefixname, localname, m_htmlCompat); 0726 0727 // FIXME what if attributes(false) == 0? 0728 attributes(false)->removeNamedItem(makeId(emptyNamespace, localname.id()), prefixname, false, exceptioncode); 0729 0730 // it's allowed to remove attributes that don't exist. 0731 if (exceptioncode == DOMException::NOT_FOUND_ERR) { 0732 exceptioncode = 0; 0733 } 0734 } 0735 0736 AttrImpl *ElementImpl::getAttributeNode(const DOMString &name) 0737 { 0738 LocalName localname; 0739 PrefixName prefixname; 0740 splitPrefixLocalName(name, prefixname, localname, m_htmlCompat); 0741 0742 if (!localname.id()) { 0743 return nullptr; 0744 } 0745 if (!namedAttrMap) { 0746 return nullptr; 0747 } 0748 0749 return static_cast<AttrImpl *>(attributes()->getNamedItem(makeId(emptyNamespace, localname.id()), prefixname, false)); 0750 } 0751 0752 Attr ElementImpl::setAttributeNode(AttrImpl *newAttr, int &exceptioncode) 0753 { 0754 if (!newAttr) { 0755 exceptioncode = DOMException::NOT_FOUND_ERR; 0756 return nullptr; 0757 } 0758 Attr r = attributes(false)->setNamedItem(newAttr, emptyPrefixName, true, exceptioncode); 0759 if (!exceptioncode) { 0760 newAttr->setOwnerElement(this); 0761 } 0762 return r; 0763 } 0764 0765 Attr ElementImpl::removeAttributeNode(AttrImpl *oldAttr, int &exceptioncode) 0766 { 0767 if (!oldAttr || oldAttr->ownerElement() != this) { 0768 exceptioncode = DOMException::NOT_FOUND_ERR; 0769 return nullptr; 0770 } 0771 0772 if (isReadOnly()) { 0773 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 0774 return nullptr; 0775 } 0776 0777 if (!namedAttrMap) { 0778 exceptioncode = DOMException::NOT_FOUND_ERR; 0779 return nullptr; 0780 } 0781 0782 return attributes(false)->removeAttr(oldAttr); 0783 } 0784 0785 DOMString ElementImpl::getAttributeNS(const DOMString &namespaceURI, 0786 const DOMString &localName, 0787 int &exceptioncode) 0788 { 0789 if (!localName.implementation()) { 0790 exceptioncode = DOMException::NOT_FOUND_ERR; 0791 return DOMString(); 0792 } 0793 0794 LocalName localname = LocalName::fromString(localName, m_htmlCompat ? IDS_NormalizeLower : IDS_CaseSensitive); 0795 NamespaceName namespacename = NamespaceName::fromString(namespaceURI); 0796 0797 NodeImpl::Id id = makeId(namespacename.id(), localname.id()); 0798 return getAttribute(id, emptyPrefixName, true); 0799 } 0800 0801 void ElementImpl::removeAttributeNS(const DOMString &namespaceURI, 0802 const DOMString &localName, 0803 int &exceptioncode) 0804 { 0805 if (!localName.implementation()) { 0806 exceptioncode = DOMException::NOT_FOUND_ERR; 0807 return; 0808 } 0809 0810 NamespaceName namespacename = NamespaceName::fromString(namespaceURI); 0811 LocalName localname = LocalName::fromString(localName, m_htmlCompat ? IDS_NormalizeLower : IDS_CaseSensitive); 0812 0813 NodeImpl::Id id = makeId(namespacename.id(), localname.id()); 0814 attributes(false)->removeNamedItem(id, emptyPrefixName, true, exceptioncode); 0815 } 0816 0817 AttrImpl *ElementImpl::getAttributeNodeNS(const DOMString &namespaceURI, 0818 const DOMString &localName, 0819 int &exceptioncode) 0820 { 0821 if (!localName.implementation()) { 0822 exceptioncode = DOMException::NOT_FOUND_ERR; 0823 return nullptr; 0824 } 0825 0826 NamespaceName namespacename = NamespaceName::fromString(namespaceURI); 0827 LocalName localname = LocalName::fromString(localName, m_htmlCompat ? IDS_NormalizeLower : IDS_CaseSensitive); 0828 0829 NodeImpl::Id id = makeId(namespacename.id(), localname.id()); 0830 if (!attributes(true)) { 0831 return nullptr; 0832 } 0833 return static_cast<AttrImpl *>(attributes()->getNamedItem(id, emptyPrefixName, true)); 0834 } 0835 0836 Attr ElementImpl::setAttributeNodeNS(AttrImpl *newAttr, int &exceptioncode) 0837 { 0838 if (!newAttr) { 0839 exceptioncode = DOMException::NOT_FOUND_ERR; 0840 return nullptr; 0841 } 0842 // WRONG_DOCUMENT_ERR and INUSE_ATTRIBUTE_ERR are already tested & thrown by setNamedItem 0843 Attr r = attributes(false)->setNamedItem(newAttr, emptyPrefixName, true, exceptioncode); 0844 if (!exceptioncode) { 0845 newAttr->setOwnerElement(this); 0846 } 0847 return r; 0848 } 0849 0850 DOMString ElementImpl::nodeName() const 0851 { 0852 return tagName(); 0853 } 0854 0855 DOMString ElementImpl::namespaceURI() const 0856 { 0857 return NamespaceName::fromId(namespacePart(id())).toString(); 0858 } 0859 0860 void ElementImpl::setPrefix(const DOMString &_prefix, int &exceptioncode) 0861 { 0862 checkSetPrefix(_prefix, exceptioncode); 0863 if (exceptioncode) { 0864 return; 0865 } 0866 m_prefix = PrefixName::fromString(_prefix); 0867 } 0868 0869 short ElementImpl::tabIndex() const 0870 { 0871 return m_elementHasRareData ? rareData()->tabIndex() : 0; 0872 } 0873 0874 void ElementImpl::setTabIndex(short _tabIndex) 0875 { 0876 createRareData()->setTabIndex(_tabIndex); 0877 } 0878 0879 void ElementImpl::setNoTabIndex() 0880 { 0881 if (!m_elementHasRareData) { 0882 return; 0883 } 0884 rareData()->m_hasTabIndex = false; 0885 } 0886 0887 bool ElementImpl::hasTabIndex() const 0888 { 0889 return m_elementHasRareData ? rareData()->m_hasTabIndex : false; 0890 } 0891 0892 void ElementImpl::defaultEventHandler(EventImpl *e) 0893 { 0894 if (!e->defaultHandled() && document()->part() && e->id() == EventImpl::KEYPRESS_EVENT && e->isKeyRelatedEvent()) { 0895 const KHTMLPart *part = document()->part(); 0896 bool isContentEditableElement = part->isEditable() || (focused() && isContentEditable()); 0897 if (isContentEditableElement || part->isCaretMode()) { 0898 if (document()->view() && document()->view()->caretKeyPressEvent(static_cast<KeyEventBaseImpl *>(e)->qKeyEvent())) { 0899 e->setDefaultHandled(); 0900 return; 0901 } 0902 if (isContentEditableElement && part->editor()->handleKeyEvent(static_cast<KeyEventBaseImpl *>(e)->qKeyEvent())) { 0903 e->setDefaultHandled(); 0904 return; 0905 } 0906 } 0907 } 0908 0909 if (m_render && m_render->scrollsOverflow()) { 0910 switch (e->id()) { 0911 case EventImpl::KEYDOWN_EVENT: 0912 case EventImpl::KEYUP_EVENT: 0913 case EventImpl::KEYPRESS_EVENT: 0914 if (!focused() || e->target() != this) { 0915 break; 0916 } 0917 // fall through 0918 case EventImpl::KHTML_MOUSEWHEEL_EVENT: 0919 if (m_render->handleEvent(*e)) { 0920 e->setDefaultHandled(); 0921 } 0922 default: 0923 break; 0924 } 0925 } 0926 } 0927 0928 void ElementImpl::createAttributeMap() const 0929 { 0930 namedAttrMap = new NamedAttrMapImpl(const_cast<ElementImpl *>(this)); 0931 namedAttrMap->ref(); 0932 } 0933 0934 RenderStyle *ElementImpl::styleForRenderer(RenderObject * /*parentRenderer*/) 0935 { 0936 return document()->styleSelector()->styleForElement(this); 0937 } 0938 0939 RenderObject *ElementImpl::createRenderer(RenderArena *arena, RenderStyle *style) 0940 { 0941 if (document()->documentElement() == this && style->display() == NONE) { 0942 // Ignore display: none on root elements. Force a display of block in that case. 0943 RenderBlock *result = new(arena) RenderBlock(this); 0944 if (result) { 0945 result->setStyle(style); 0946 } 0947 return result; 0948 } 0949 return RenderObject::createObject(this, style); 0950 } 0951 0952 void ElementImpl::attach() 0953 { 0954 assert(!attached()); 0955 assert(!m_render); 0956 assert(parentNode()); 0957 0958 #if SPEED_DEBUG < 1 0959 createRendererIfNeeded(); 0960 #endif 0961 0962 NodeBaseImpl::attach(); 0963 } 0964 0965 void ElementImpl::close() 0966 { 0967 NodeImpl::close(); 0968 0969 // Trigger all the addChild changes as one large dynamic appendChildren change 0970 if (attached()) { 0971 backwardsStructureChanged(); 0972 } 0973 } 0974 0975 void ElementImpl::detach() 0976 { 0977 document()->dynamicDomRestyler().resetDependencies(this); 0978 0979 if (ElementRareDataImpl *rd = rareData()) { 0980 rd->resetComputedStyle(); 0981 } 0982 0983 NodeBaseImpl::detach(); 0984 } 0985 0986 void ElementImpl::structureChanged() 0987 { 0988 NodeBaseImpl::structureChanged(); 0989 0990 if (!document()->renderer()) { 0991 return; // the document is about to be destroyed 0992 } 0993 0994 document()->dynamicDomRestyler().restyleDependent(this, StructuralDependency); 0995 // In theory BackwardsStructurualDependencies are indifferent to prepend, 0996 // but it's too rare to optimize. 0997 document()->dynamicDomRestyler().restyleDependent(this, BackwardsStructuralDependency); 0998 } 0999 1000 void ElementImpl::backwardsStructureChanged() 1001 { 1002 NodeBaseImpl::backwardsStructureChanged(); 1003 1004 if (!document()->renderer()) { 1005 return; // the document is about to be destroyed 1006 } 1007 1008 // Most selectors are not affected by append. Fire the few that are. 1009 document()->dynamicDomRestyler().restyleDependent(this, BackwardsStructuralDependency); 1010 } 1011 1012 void ElementImpl::attributeChanged(NodeImpl::Id id) 1013 { 1014 if (!document()->renderer()) { 1015 return; // the document is about to be destroyed 1016 } 1017 1018 #if 0 // one-one dependencies for attributes disabled 1019 document()->dynamicDomRestyler().restyleDependent(this, AttributeDependency); 1020 #endif 1021 if (document()->dynamicDomRestyler().checkDependency(id, PersonalDependency)) { 1022 setChanged(true); 1023 } 1024 if (document()->dynamicDomRestyler().checkDependency(id, AncestorDependency)) { 1025 setChangedAscendentAttribute(true); 1026 } 1027 if (document()->dynamicDomRestyler().checkDependency(id, PredecessorDependency) && parent()) 1028 // Any element that dependt on a predecessors attribute, also depend structurally on parent 1029 { 1030 parent()->structureChanged(); 1031 } 1032 } 1033 1034 void ElementImpl::recalcStyle(StyleChange change) 1035 { 1036 // ### should go away and be done in renderobject 1037 RenderStyle *_style = m_render ? m_render->style() : nullptr; 1038 bool hasParentRenderer = parent() ? parent()->attached() : false; 1039 1040 if ((change > NoChange || changed())) { 1041 if (ElementRareDataImpl *rd = rareData()) { 1042 rd->resetComputedStyle(); 1043 } 1044 } 1045 1046 #if 0 1047 const char *debug; 1048 switch (change) { 1049 case NoChange: debug = "NoChange"; 1050 break; 1051 case NoInherit: debug = "NoInherit"; 1052 break; 1053 case Inherit: debug = "Inherit"; 1054 break; 1055 case Force: debug = "Force"; 1056 break; 1057 } 1058 qDebug("recalcStyle(%d: %s, changed: %d)[%p: %s]", change, debug, changed(), this, tagName().string().toLatin1().constData()); 1059 #endif 1060 if (hasParentRenderer && (change >= Inherit || changed() || (change == NoInherit && affectedByNoInherit()))) { 1061 RenderStyle *newStyle = document()->styleSelector()->styleForElement(this); 1062 newStyle->ref(); 1063 StyleChange ch = diff(_style, newStyle); 1064 if (ch == Detach) { 1065 if (attached()) { 1066 detach(); 1067 } 1068 // ### Suboptimal. Style gets calculated again. 1069 attach(); 1070 // attach recalulates the style for all children. No need to do it twice. 1071 setChanged(false); 1072 setHasChangedChild(false); 1073 newStyle->deref(); 1074 return; 1075 } else if (ch != NoChange) { 1076 if (m_render) { 1077 m_render->setStyle(newStyle); 1078 } 1079 } 1080 newStyle->deref(); 1081 1082 if (change != Force) { 1083 change = ch; 1084 } 1085 } 1086 // If a changed attribute has ancestor dependencies, restyle all children 1087 if (changedAscendentAttribute()) { 1088 change = Force; 1089 setChangedAscendentAttribute(false); 1090 } 1091 1092 NodeImpl *n; 1093 for (n = _first; n; n = n->nextSibling()) { 1094 if (change >= Inherit || n->hasChangedChild() || n->changed() || 1095 (change == NoInherit && n->affectedByNoInherit()) 1096 ) { 1097 //qDebug(" (%p) calling recalcStyle on child %p/%s, change=%d", this, n, n->isElementNode() ? ((ElementImpl *)n)->tagName().string().toLatin1().constData() : n->isTextNode() ? "text" : "unknown", change ); 1098 n->recalcStyle(change); 1099 } 1100 } 1101 1102 setChanged(false); 1103 setHasChangedChild(false); 1104 } 1105 1106 bool ElementImpl::isFocusableImpl(FocusType ft) const 1107 { 1108 if (m_render && m_render->scrollsOverflow()) { 1109 return true; 1110 } 1111 1112 // See WAI-ARIA 1.0, UA implementor's guide, 3.1 for the rules this 1113 // implements. 1114 if (hasTabIndex()) { 1115 int ti = tabIndex(); 1116 1117 // Negative things are focusable, but not in taborder 1118 if (ti < 0) { 1119 return (ft != FT_Tab); 1120 } else { // ... while everything else is completely focusable 1121 return true; 1122 } 1123 } 1124 1125 // Only make editable elements selectable if its parent element 1126 // is not editable. FIXME: this is not 100% right as non-editable elements 1127 // within editable elements are focusable too. 1128 return isContentEditable() && !(parentNode() && parentNode()->isContentEditable()); 1129 } 1130 1131 bool ElementImpl::isContentEditable() const 1132 { 1133 if (document()->part() && document()->part()->isEditable()) { 1134 return true; 1135 } 1136 1137 // document()->updateRendering(); 1138 1139 if (!renderer()) { 1140 if (parentNode()) { 1141 return parentNode()->isContentEditable(); 1142 } else { 1143 return false; 1144 } 1145 } 1146 1147 return renderer()->style()->userInput() == UI_ENABLED; 1148 } 1149 1150 void ElementImpl::setContentEditable(bool enabled) 1151 { 1152 // FIXME: the approach is flawed, better use an enum instead of bool 1153 int value; 1154 if (enabled) { 1155 value = CSS_VAL_ENABLED; 1156 } else { 1157 // Intelligently use "none" or "disabled", depending on the type of 1158 // element 1159 // FIXME: intelligence not impl'd yet 1160 value = CSS_VAL_NONE; 1161 1162 // FIXME: reset caret if it is in this node or a child 1163 }/*end if*/ 1164 // FIXME: use addCSSProperty when I get permission to move it here 1165 // qCDebug(KHTML_LOG) << "CSS_PROP__KHTML_USER_INPUT: "<< value; 1166 getInlineStyleDecls()->setProperty(CSS_PROP__KHTML_USER_INPUT, value, false); 1167 setChanged(); 1168 } 1169 1170 // DOM Section 1.1.1 1171 bool ElementImpl::childAllowed(NodeImpl *newChild) 1172 { 1173 if (!childTypeAllowed(newChild->nodeType())) { 1174 return false; 1175 } 1176 1177 // ### check xml element allowedness according to DTD 1178 1179 // If either this node or the other node is an XML element node, allow regardless (we don't do DTD checks for XML 1180 // yet) 1181 if (isXMLElementNode() || newChild->isXMLElementNode()) { 1182 return true; 1183 } else { 1184 return checkChild(id(), newChild->id(), document()->inStrictMode()); 1185 } 1186 } 1187 1188 bool ElementImpl::childTypeAllowed(unsigned short type) 1189 { 1190 switch (type) { 1191 case Node::ELEMENT_NODE: 1192 case Node::TEXT_NODE: 1193 case Node::COMMENT_NODE: 1194 case Node::PROCESSING_INSTRUCTION_NODE: 1195 case Node::CDATA_SECTION_NODE: 1196 case Node::ENTITY_REFERENCE_NODE: 1197 return true; 1198 break; 1199 default: 1200 return false; 1201 } 1202 } 1203 1204 void ElementImpl::scrollIntoView(bool /*alignToTop*/) 1205 { 1206 // ### 1207 qCWarning(KHTML_LOG) << "non-standard scrollIntoView() not implemented"; 1208 } 1209 1210 void ElementImpl::createNonCSSDecl() 1211 { 1212 assert(!m_hasCombinedStyle); 1213 CSSInlineStyleDeclarationImpl *ild = m_style.inlineDecls; 1214 m_style.combinedDecls = new CombinedStyleDecl; 1215 m_style.combinedDecls->inlineDecls = ild; 1216 CSSStyleDeclarationImpl *ncd = new CSSStyleDeclarationImpl(nullptr); 1217 m_style.combinedDecls->nonCSSDecls = ncd; 1218 ncd->ref(); 1219 ncd->setParent(document()->elementSheet()); 1220 ncd->setNode(this); 1221 ncd->setStrictParsing(false); 1222 m_hasCombinedStyle = true; 1223 } 1224 1225 CSSInlineStyleDeclarationImpl *ElementImpl::getInlineStyleDecls() 1226 { 1227 if (!inlineStyleDecls()) { 1228 createInlineDecl(); 1229 } 1230 return inlineStyleDecls(); 1231 } 1232 1233 void ElementImpl::createInlineDecl() 1234 { 1235 assert(!m_style.inlineDecls || (m_hasCombinedStyle && !m_style.combinedDecls->inlineDecls)); 1236 1237 CSSInlineStyleDeclarationImpl *dcl = new CSSInlineStyleDeclarationImpl(nullptr); 1238 dcl->ref(); 1239 dcl->setParent(document()->elementSheet()); 1240 dcl->setNode(this); 1241 dcl->setStrictParsing(!document()->inCompatMode()); 1242 if (m_hasCombinedStyle) { 1243 m_style.combinedDecls->inlineDecls = dcl; 1244 } else { 1245 m_style.inlineDecls = dcl; 1246 } 1247 } 1248 1249 void ElementImpl::dispatchAttrRemovalEvent(NodeImpl::Id /*id*/, DOMStringImpl * /*value*/) 1250 { 1251 // ### enable this stuff again 1252 if (!document()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER)) { 1253 return; 1254 } 1255 //int exceptioncode = 0; 1256 //dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(), 1257 //attr->value(), document()->attrName(attr->id()),MutationEvent::REMOVAL),exceptioncode); 1258 } 1259 1260 void ElementImpl::dispatchAttrAdditionEvent(NodeImpl::Id /*id*/, DOMStringImpl * /*value*/) 1261 { 1262 // ### enable this stuff again 1263 if (!document()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER)) { 1264 return; 1265 } 1266 //int exceptioncode = 0; 1267 //dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(), 1268 //attr->value(),document()->attrName(attr->id()),MutationEvent::ADDITION),exceptioncode); 1269 } 1270 1271 void ElementImpl::updateId(DOMStringImpl *oldId, DOMStringImpl *newId) 1272 { 1273 if (!inDocument()) { 1274 return; 1275 } 1276 1277 if (oldId && oldId->l) { 1278 removeId(DOMString(oldId)); 1279 } 1280 1281 if (newId && newId->l) { 1282 addId(DOMString(newId)); 1283 } 1284 } 1285 1286 void ElementImpl::removeId(const DOMString &id) 1287 { 1288 document()->getElementByIdCache().remove(id, this); 1289 } 1290 1291 void ElementImpl::addId(const DOMString &id) 1292 { 1293 document()->getElementByIdCache().add(id, this); 1294 } 1295 1296 void ElementImpl::insertedIntoDocument() 1297 { 1298 // need to do superclass processing first so inDocument() is true 1299 // by the time we reach updateId 1300 NodeBaseImpl::insertedIntoDocument(); 1301 1302 if (hasID()) { 1303 DOMString id = getAttribute(ATTR_ID); 1304 updateId(nullptr, id.implementation()); 1305 } 1306 } 1307 1308 void ElementImpl::removedFromDocument() 1309 { 1310 if (hasID()) { 1311 DOMString id = getAttribute(ATTR_ID); 1312 updateId(id.implementation(), nullptr); 1313 } 1314 1315 NodeBaseImpl::removedFromDocument(); 1316 } 1317 1318 DOMString ElementImpl::openTagStartToString(bool expandurls) const 1319 { 1320 DOMString result = DOMString("<") + nonCaseFoldedTagName(); 1321 1322 NamedAttrMapImpl *attrMap = attributes(true); 1323 1324 if (attrMap) { 1325 unsigned numAttrs = attrMap->length(); 1326 for (unsigned i = 0; i < numAttrs; i++) { 1327 result += " "; 1328 1329 const AttributeImpl &attribute = attrMap->attributeAt(i); 1330 AttrImpl *attr = attribute.attr(); 1331 1332 if (attr) { 1333 result += attr->toString(); 1334 } else { 1335 //FIXME: should use prefix too and depends on html/xhtml case 1336 PrefixName prefix = attribute.m_prefix; 1337 DOMString current; 1338 if (prefix.id()) { 1339 current = prefix.toString() + DOMString(":") + attribute.localName(); 1340 } else { 1341 current = attribute.localName(); 1342 } 1343 if (m_htmlCompat) { 1344 current = current.lower(); 1345 } 1346 result += current; 1347 if (!attribute.value().isNull()) { 1348 result += "=\""; 1349 // FIXME: substitute entities for any instances of " or ' 1350 // Expand out all urls, i.e. the src and href attributes 1351 if (expandurls && (attribute.id() == ATTR_SRC || attribute.id() == ATTR_HREF)) 1352 if (document()) { 1353 //We need to sanitize the urls - strip out the passwords. 1354 //FIXME: are src= and href= the only places that might have a password and need to be sanitized? 1355 QUrl safeURL(document()->completeURL(attribute.value().string())); 1356 safeURL.setPassword(QString()); 1357 result += safeURL.toDisplayString().toHtmlEscaped(); 1358 } else { 1359 qCWarning(KHTML_LOG) << "document() returned false"; 1360 result += attribute.value(); 1361 } 1362 else { 1363 result += attribute.value(); 1364 } 1365 result += "\""; 1366 } 1367 } 1368 } 1369 } 1370 1371 return result; 1372 } 1373 DOMString ElementImpl::selectionToString(NodeImpl *selectionStart, NodeImpl *selectionEnd, int startOffset, int endOffset, bool &found) const 1374 { 1375 DOMString result = openTagStartToString(); 1376 1377 if (hasChildNodes()) { 1378 result += ">"; 1379 1380 for (NodeImpl *child = firstChild(); child != nullptr; child = child->nextSibling()) { 1381 result += child->selectionToString(selectionStart, selectionEnd, startOffset, endOffset, found); // this might set found to true 1382 if (child == selectionEnd) { 1383 found = true; 1384 } 1385 if (found) { 1386 break; 1387 } 1388 } 1389 1390 result += "</"; 1391 result += nonCaseFoldedTagName(); 1392 result += ">"; 1393 } else { 1394 result += " />"; 1395 } 1396 1397 return result; 1398 } 1399 1400 DOMString ElementImpl::toString() const 1401 { 1402 QString result = openTagStartToString().string(); //Accumulate in QString, since DOMString can't append well. 1403 1404 if (hasChildNodes()) { 1405 result += ">"; 1406 1407 for (NodeImpl *child = firstChild(); child != nullptr; child = child->nextSibling()) { 1408 DOMString kid = child->toString(); 1409 result += QString::fromRawData(kid.unicode(), kid.length()); 1410 } 1411 1412 result += "</"; 1413 result += nonCaseFoldedTagName().string(); 1414 result += ">"; 1415 } else if (result.length() == 1) { 1416 // ensure we do not get results like < /> can happen when serialize document 1417 result = ""; 1418 } else { 1419 result += " />"; 1420 } 1421 1422 return result; 1423 } 1424 1425 RenderStyle *ElementImpl::computedStyle() 1426 { 1427 if (m_render && m_render->style()) { 1428 return m_render->style(); 1429 } 1430 1431 if (!attached()) 1432 // FIXME: Try to do better than this. Ensure that styleForElement() works for elements that are not in the 1433 // document tree and figure out when to destroy the computed style for such elements. 1434 { 1435 return nullptr; 1436 } 1437 1438 ElementRareDataImpl *rd = createRareData(); 1439 if (!rd->m_computedStyle) { 1440 rd->m_computedStyle = document()->styleSelector()->styleForElement(this, parent() ? parent()->computedStyle() : nullptr); 1441 rd->m_computedStyle->ref(); 1442 } 1443 return rd->m_computedStyle; 1444 } 1445 1446 // ElementTraversal API 1447 ElementImpl *ElementImpl::firstElementChild() const 1448 { 1449 NodeImpl *n = firstChild(); 1450 while (n && !n->isElementNode()) { 1451 n = n->nextSibling(); 1452 } 1453 return static_cast<ElementImpl *>(n); 1454 } 1455 1456 ElementImpl *ElementImpl::lastElementChild() const 1457 { 1458 NodeImpl *n = lastChild(); 1459 while (n && !n->isElementNode()) { 1460 n = n->previousSibling(); 1461 } 1462 return static_cast<ElementImpl *>(n); 1463 } 1464 1465 ElementImpl *ElementImpl::previousElementSibling() const 1466 { 1467 NodeImpl *n = previousSibling(); 1468 while (n && !n->isElementNode()) { 1469 n = n->previousSibling(); 1470 } 1471 return static_cast<ElementImpl *>(n); 1472 } 1473 1474 ElementImpl *ElementImpl::nextElementSibling() const 1475 { 1476 NodeImpl *n = nextSibling(); 1477 while (n && !n->isElementNode()) { 1478 n = n->nextSibling(); 1479 } 1480 return static_cast<ElementImpl *>(n); 1481 } 1482 1483 unsigned ElementImpl::childElementCount() const 1484 { 1485 unsigned count = 0; 1486 NodeImpl *n = firstChild(); 1487 while (n) { 1488 count += n->isElementNode(); 1489 n = n->nextSibling(); 1490 } 1491 return count; 1492 } 1493 1494 void ElementImpl::blur() 1495 { 1496 if (document()->focusNode() == this) { 1497 document()->setFocusNode(nullptr); 1498 } 1499 } 1500 1501 void ElementImpl::focus() 1502 { 1503 document()->setFocusNode(this); 1504 } 1505 1506 void ElementImpl::synchronizeStyleAttribute() const 1507 { 1508 assert(inlineStyleDecls() && m_needsStyleAttributeUpdate); 1509 m_needsStyleAttributeUpdate = false; 1510 DOMString value = inlineStyleDecls()->cssText(); 1511 attributes()->setValueWithoutElementUpdate(ATTR_STYLE, value.implementation()); 1512 } 1513 1514 // ------------------------------------------------------------------------- 1515 1516 XMLElementImpl::XMLElementImpl(DocumentImpl *doc, NamespaceName namespacename, LocalName localName, PrefixName prefix) 1517 : ElementImpl(doc) 1518 { 1519 // Called from createElement(). In this case localName, prefix, and namespaceURI all need to be null. 1520 m_localName = localName; 1521 m_namespace = namespacename; 1522 m_prefix = prefix; 1523 } 1524 1525 XMLElementImpl::~XMLElementImpl() 1526 { 1527 } 1528 1529 WTF::PassRefPtr<NodeImpl> XMLElementImpl::cloneNode(bool deep) 1530 { 1531 WTF::RefPtr<ElementImpl> clone = new XMLElementImpl(docPtr(), NamespaceName::fromId(namespacePart(id())), LocalName::fromId(localNamePart(id())), m_prefix); 1532 finishCloneNode(clone.get(), deep); 1533 return clone; 1534 } 1535 1536 void XMLElementImpl::parseAttribute(AttributeImpl *attr) 1537 { 1538 if (attr->id() == ATTR_ID) { 1539 setHasID(); 1540 document()->incDOMTreeVersion(DocumentImpl::TV_IDNameHref); 1541 } 1542 1543 // Note: we do not want to handle ATTR_CLASS here, since the 1544 // class concept applies only to specific languages, like 1545 // HTML and SVG, not generic XML. 1546 } 1547 1548 // ------------------------------------------------------------------------- 1549 1550 NamedAttrMapImpl::NamedAttrMapImpl(ElementImpl *element) 1551 : m_element(element) 1552 { 1553 } 1554 1555 NamedAttrMapImpl::~NamedAttrMapImpl() 1556 { 1557 unsigned len = m_attrs.size(); 1558 for (unsigned i = 0; i < len; i++) { 1559 m_attrs[i].free(); 1560 } 1561 m_attrs.clear(); 1562 } 1563 1564 NodeImpl *NamedAttrMapImpl::getNamedItem(NodeImpl::Id id, const PrefixName &prefix, bool nsAware) 1565 { 1566 if (!m_element) { 1567 return nullptr; 1568 } 1569 1570 int index = find(id, prefix, nsAware); 1571 return (index < 0) ? nullptr : m_attrs[index].createAttr(m_element, m_element->docPtr()); 1572 } 1573 1574 Node NamedAttrMapImpl::removeNamedItem(NodeImpl::Id id, const PrefixName &prefix, bool nsAware, int &exceptioncode) 1575 { 1576 if (!m_element) { 1577 exceptioncode = DOMException::NOT_FOUND_ERR; 1578 return nullptr; 1579 } 1580 1581 // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly 1582 if (isReadOnly()) { 1583 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 1584 return nullptr; 1585 } 1586 int index = find(id, prefix, nsAware); 1587 if (index < 0) { 1588 exceptioncode = DOMException::NOT_FOUND_ERR; 1589 return nullptr; 1590 } 1591 1592 id = m_attrs[index].id(); 1593 if (id == ATTR_ID) { 1594 m_element->updateId(m_attrs[index].val(), nullptr); 1595 } 1596 Node removed(m_attrs[index].createAttr(m_element, m_element->docPtr())); 1597 m_attrs[index].free(); // Also sets the remove'd ownerElement to 0 1598 m_attrs.remove(index); 1599 m_element->parseNullAttribute(id, prefix); 1600 m_element->attributeChanged(id); 1601 return removed; 1602 } 1603 1604 Node NamedAttrMapImpl::setNamedItem(NodeImpl *arg, const PrefixName &prefix, bool nsAware, int &exceptioncode) 1605 { 1606 if (!m_element || !arg) { 1607 exceptioncode = DOMException::NOT_FOUND_ERR; 1608 return nullptr; 1609 } 1610 1611 // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly. 1612 if (isReadOnly()) { 1613 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; 1614 return nullptr; 1615 } 1616 1617 // WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map. 1618 if (arg->document() != m_element->document()) { 1619 exceptioncode = DOMException::WRONG_DOCUMENT_ERR; 1620 return nullptr; 1621 } 1622 1623 // HIERARCHY_REQUEST_ERR: Raised if an attempt is made to add a node doesn't belong in this NamedNodeMap 1624 if (!arg->isAttributeNode()) { 1625 exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; 1626 return nullptr; 1627 } 1628 AttrImpl *attr = static_cast<AttrImpl *>(arg); 1629 1630 // INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object. 1631 // The DOM user must explicitly clone Attr nodes to re-use them in other elements. 1632 if (attr->ownerElement() && attr->ownerElement() != m_element) { 1633 exceptioncode = DOMException::INUSE_ATTRIBUTE_ERR; 1634 return nullptr; 1635 } 1636 1637 if (attr->ownerElement() == m_element) { 1638 // Already have this attribute. 1639 // DOMTS core-1 test "hc_elementreplaceattributewithself" says we should return it. 1640 return attr; 1641 } 1642 1643 int index = find(attr->id(), prefix, nsAware); 1644 if (index >= 0) { 1645 if (attr->id() == ATTR_ID) { 1646 m_element->updateId(m_attrs[index].val(), attr->val()); 1647 } 1648 1649 Node replaced = m_attrs[index].createAttr(m_element, m_element->docPtr()); 1650 m_attrs[index].free(); 1651 m_attrs[index].m_localName = emptyLocalName; /* "has implementation" flag */ 1652 m_attrs[index].m_data.attr = attr; 1653 m_attrs[index].m_data.attr->ref(); 1654 attr->setElement(m_element); 1655 m_element->parseAttribute(&m_attrs[index]); 1656 m_element->attributeChanged(m_attrs[index].id()); 1657 // ### dispatch mutation events 1658 return replaced; 1659 } 1660 1661 // No existing attribute; add to list 1662 AttributeImpl attribute; 1663 attribute.m_localName = emptyLocalName; /* "has implementation" flag */ 1664 attribute.m_namespace = NamespaceName::fromId(0); 1665 attribute.m_prefix = emptyPrefixName; 1666 attribute.m_data.attr = attr; 1667 attribute.m_data.attr->ref(); 1668 m_attrs.append(attribute); 1669 1670 attr->setElement(m_element); 1671 if (attr->id() == ATTR_ID) { 1672 m_element->updateId(nullptr, attr->val()); 1673 } 1674 m_element->parseAttribute(&m_attrs.last()); 1675 m_element->attributeChanged(m_attrs.last().id()); 1676 // ### dispatch mutation events 1677 1678 return nullptr; 1679 } 1680 1681 NodeImpl *NamedAttrMapImpl::item(unsigned index) 1682 { 1683 return (index >= m_attrs.size()) ? nullptr : m_attrs[index].createAttr(m_element, m_element->docPtr()); 1684 } 1685 1686 NodeImpl::Id NamedAttrMapImpl::idAt(unsigned index) const 1687 { 1688 assert(index < m_attrs.size()); 1689 return m_attrs[index].id(); 1690 } 1691 1692 DOMStringImpl *NamedAttrMapImpl::valueAt(unsigned index) const 1693 { 1694 assert(index < m_attrs.size()); 1695 return m_attrs[index].val(); 1696 } 1697 1698 DOMStringImpl *NamedAttrMapImpl::getValue(NodeImpl::Id id, const PrefixName &prefix, bool nsAware) const 1699 { 1700 int index = find(id, prefix, nsAware); 1701 return index < 0 ? nullptr : m_attrs[index].val(); 1702 } 1703 1704 void NamedAttrMapImpl::setValue(NodeImpl::Id id, DOMStringImpl *value, const PrefixName &prefix, bool nsAware) 1705 { 1706 if (!id) { 1707 return; 1708 } 1709 // Passing in a null value here causes the attribute to be removed. This is a khtml extension 1710 // (the spec does not specify what to do in this situation). 1711 int exceptioncode = 0; 1712 if (!value) { 1713 removeNamedItem(id, prefix, nsAware, exceptioncode); 1714 return; 1715 } 1716 int index = find(id, prefix, nsAware); 1717 if (index >= 0) { 1718 if (m_attrs[index].attr()) { 1719 m_attrs[index].attr()->setPrefix(prefix.toString(), exceptioncode); 1720 } else { 1721 m_attrs[index].m_prefix = prefix; 1722 } 1723 m_attrs[index].setValue(value, m_element); 1724 // ### dispatch mutation events 1725 return; 1726 } 1727 1728 AttributeImpl attr; 1729 attr.m_localName = LocalName::fromId(localNamePart(id)); 1730 attr.m_namespace = NamespaceName::fromId(namespacePart(id)); 1731 attr.m_prefix = prefix; 1732 attr.m_data.value = value; 1733 attr.m_data.value->ref(); 1734 m_attrs.append(attr); 1735 1736 if (m_element) { 1737 if (id == ATTR_ID) { 1738 m_element->updateId(nullptr, value); 1739 } 1740 m_element->parseAttribute(&m_attrs.last()); 1741 m_element->attributeChanged(m_attrs.last().id()); 1742 } 1743 // ### dispatch mutation events 1744 } 1745 1746 Attr NamedAttrMapImpl::removeAttr(AttrImpl *attr) 1747 { 1748 unsigned len = m_attrs.size(); 1749 for (unsigned i = 0; i < len; i++) { 1750 if (m_attrs[i].attr() == attr) { 1751 NodeImpl::Id id = m_attrs[i].id(); 1752 if (id == ATTR_ID) { 1753 m_element->updateId(attr->val(), nullptr); 1754 } 1755 Node removed(m_attrs[i].createAttr(m_element, m_element->docPtr())); 1756 m_attrs[i].free(); 1757 m_attrs.remove(i); 1758 m_element->parseNullAttribute(id, PrefixName::fromString(attr->prefix())); 1759 m_element->attributeChanged(id); 1760 // ### dispatch mutation events 1761 return removed; 1762 } 1763 } 1764 1765 return nullptr; 1766 } 1767 1768 void NamedAttrMapImpl::copyAttributes(NamedAttrMapImpl *other) 1769 { 1770 assert(m_element); 1771 unsigned i; 1772 unsigned len = m_attrs.size(); 1773 for (i = 0; i < len; i++) { 1774 if (m_attrs[i].id() == ATTR_ID) { 1775 m_element->updateId(m_attrs[i].val(), nullptr); 1776 } 1777 m_attrs[i].free(); 1778 } 1779 m_attrs.resize(other->length()); 1780 len = m_attrs.size(); 1781 for (i = 0; i < len; i++) { 1782 m_attrs[i].m_localName = other->m_attrs[i].m_localName; 1783 m_attrs[i].m_prefix = other->m_attrs[i].m_prefix; 1784 m_attrs[i].m_namespace = other->m_attrs[i].m_namespace; 1785 if (m_attrs[i].m_localName.id()) { 1786 m_attrs[i].m_data.value = other->m_attrs[i].m_data.value; 1787 m_attrs[i].m_data.value->ref(); 1788 } else { 1789 WTF::RefPtr<NodeImpl> clonedAttr = other->m_attrs[i].m_data.attr->cloneNode(true); 1790 m_attrs[i].m_data.attr = static_cast<AttrImpl *>(clonedAttr.get()); 1791 m_attrs[i].m_data.attr->ref(); 1792 m_attrs[i].m_data.attr->setElement(m_element); 1793 } 1794 if (m_attrs[i].id() == ATTR_ID) { 1795 m_element->updateId(nullptr, m_attrs[i].val()); 1796 } 1797 m_element->parseAttribute(&m_attrs[i]); 1798 m_element->attributeChanged(m_attrs[i].id()); 1799 } 1800 } 1801 1802 int NamedAttrMapImpl::find(NodeImpl::Id id, const PrefixName &prefix, bool nsAware) const 1803 { 1804 //qCDebug(KHTML_LOG) << "In find:" << getPrintableName(id) << "[" << prefix.toString() << prefix.id() << "]" << nsAware; 1805 //qCDebug(KHTML_LOG) << "m_attrs.size()" << m_attrs.size(); 1806 unsigned len = m_attrs.size(); 1807 for (unsigned i = 0; i < len; ++i) { 1808 //qCDebug(KHTML_LOG) << "check attr[" << i << "]" << getPrintableName(m_attrs[i].id()) << "prefix:" << m_attrs[i].prefix(); 1809 if (nsAware && namespacePart(id) == anyNamespace && localNamePart(id) == localNamePart(m_attrs[i].id())) { 1810 return i; 1811 } 1812 if ((nsAware && id == m_attrs[i].id()) || (!nsAware && localNamePart(id) == localNamePart(m_attrs[i].id()) && prefix == m_attrs[i].prefixName())) { 1813 //qCDebug(KHTML_LOG) << "attribute matched: exiting..."; 1814 return i; 1815 } 1816 } 1817 //qCDebug(KHTML_LOG) << "attribute doesn't match: exiting... with -1"; 1818 return -1; 1819 } 1820 1821 void NamedAttrMapImpl::setElement(ElementImpl *element) 1822 { 1823 assert(!m_element); 1824 m_element = element; 1825 1826 unsigned len = m_attrs.size(); 1827 for (unsigned i = 0; i < len; i++) 1828 if (m_attrs[i].attr()) { 1829 m_attrs[i].attr()->setElement(element); 1830 } 1831 } 1832 1833 void NamedAttrMapImpl::detachFromElement() 1834 { 1835 // This makes the map invalid; nothing can really be done with it now since it's not 1836 // associated with an element. But we have to keep it around in memory in case there 1837 // are still references to it. 1838 m_element = nullptr; 1839 unsigned len = m_attrs.size(); 1840 for (unsigned i = 0; i < len; i++) { 1841 m_attrs[i].free(); 1842 } 1843 m_attrs.clear(); 1844 } 1845 1846 void NamedAttrMapImpl::setClass(const DOMString &string) 1847 { 1848 if (!m_element) { 1849 return; 1850 } 1851 1852 if (!m_element->hasClass()) { 1853 m_classNames.clear(); 1854 return; 1855 } 1856 1857 m_classNames.parseClassAttribute(string, m_element->document()->htmlMode() != DocumentImpl::XHtml && 1858 m_element->document()->inCompatMode()); 1859 } 1860 1861 void NamedAttrMapImpl::setValueWithoutElementUpdate(NodeImpl::Id id, DOMStringImpl *value) 1862 { 1863 // FIXME properly fix case value == 0 1864 int index = find(id, emptyPrefixName, true); 1865 if (index >= 0) { 1866 m_attrs[index].rewriteValue(value ? value : DOMStringImpl::empty()); 1867 return; 1868 } 1869 1870 if (!value) { 1871 return; 1872 } 1873 1874 AttributeImpl attr; 1875 attr.m_localName = LocalName::fromId(localNamePart(id)); 1876 attr.m_namespace = NamespaceName::fromId(namespacePart(id)); 1877 attr.m_prefix = emptyPrefixName; 1878 attr.m_data.value = value; 1879 attr.m_data.value->ref(); 1880 m_attrs.append(attr); 1881 } 1882 1883 }