1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2001 Peter Kelly (pmk@post.com)
7  *           (C) 2001 Dirk Mueller (mueller@kde.org)
8  *           (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
9  *           (C) 2005, 2008 Maksim Orlovich (maksim@kde.org)
10  *           (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com)
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25  * Boston, MA 02110-1301, USA.
26  */
27 
28 //#define EVENT_DEBUG
29 
30 #include "dom_elementimpl.h"
31 
32 #include <dom/dom_exception.h>
33 #include <dom/dom_node.h>
34 #include <dom/html_image.h>
35 #include "dom_textimpl.h"
36 #include "dom_docimpl.h"
37 #include "dom2_eventsimpl.h"
38 #include "dom_restyler.h"
39 #include "dom_xmlimpl.h"
40 
41 #include <html/dtd.h>
42 #include <html/htmlparser.h>
43 #include <html/html_imageimpl.h>
44 
45 #include <rendering/render_canvas.h>
46 #include <css/css_valueimpl.h>
47 #include <css/css_stylesheetimpl.h>
48 #include <css/cssstyleselector.h>
49 #include <css/cssvalues.h>
50 #include <css/cssproperties.h>
51 #include <khtml_part.h>
52 #include <khtmlview.h>
53 
54 #include <editing/editing_p.h>
55 #include <editing/editor.h>
56 
57 #include <QTextStream>
58 #include <QTextDocument>
59 #include "khtml_debug.h"
60 #include <stdlib.h>
61 
62 #include <wtf/HashMap.h>
63 
64 // ### support default attributes
65 // ### dispatch mutation events
66 // ### check for INVALID_CHARACTER_ERR where appropriate
67 
68 using namespace khtml;
69 
70 namespace DOM
71 {
72 
AttrImpl(ElementImpl * element,DocumentImpl * docPtr,NamespaceName namespacename,LocalName localName,PrefixName prefix,DOMStringImpl * value)73 AttrImpl::AttrImpl(ElementImpl *element, DocumentImpl *docPtr, NamespaceName namespacename, LocalName localName, PrefixName prefix, DOMStringImpl *value)
74     : NodeBaseImpl(docPtr)
75 {
76     m_value = value;
77     m_value->ref();
78 
79     m_namespace = namespacename;
80     m_localName = localName;
81     m_prefix = prefix;
82 
83     // When creating the text node initially, we want element = 0,
84     // so we don't attempt to update the getElementById cache or
85     // call parseAttribute, etc. This is because we're normally lazily,
86     // from previous attributes, so there is nothing really changing
87     m_element = nullptr;
88     createTextChild();
89     m_element = element;
90 }
91 
~AttrImpl()92 AttrImpl::~AttrImpl()
93 {
94     m_value->deref();
95 }
96 
nodeName() const97 DOMString AttrImpl::nodeName() const
98 {
99     return name();
100 }
101 
nodeType() const102 unsigned short AttrImpl::nodeType() const
103 {
104     return Node::ATTRIBUTE_NODE;
105 }
106 
prefix() const107 DOMString AttrImpl::prefix() const
108 {
109     return m_prefix.toString();
110 }
111 
setPrefix(const DOMString & _prefix,int & exceptioncode)112 void AttrImpl::setPrefix(const DOMString &_prefix, int &exceptioncode)
113 {
114     checkSetPrefix(_prefix, exceptioncode);
115     if (exceptioncode) {
116         return;
117     }
118 
119     m_prefix = PrefixName::fromString(_prefix);
120 }
121 
namespaceURI() const122 DOMString AttrImpl::namespaceURI() const
123 {
124     if (m_htmlCompat) {
125         return DOMString();
126     }
127     return m_namespace.toString();
128 }
129 
localName() const130 DOMString AttrImpl::localName() const
131 {
132     return m_localName.toString();
133 }
134 
nodeValue() const135 DOMString AttrImpl::nodeValue() const
136 {
137     return m_value;
138 }
139 
name() const140 DOMString AttrImpl::name() const
141 {
142     DOMString n = m_localName.toString();
143 
144     // compat mode always return attribute names in lowercase.
145     // that's not formally in the specification, but common
146     // practice - a w3c erratum to DOM L2 is pending.
147     if (m_htmlCompat) {
148         n = n.lower();
149     }
150 
151     DOMString p = m_prefix.toString();
152     if (!p.isEmpty()) {
153         return p + DOMString(":") + n;
154     }
155 
156     return n;
157 }
158 
createTextChild()159 void AttrImpl::createTextChild()
160 {
161     // add a text node containing the attribute value
162     if (m_value->length() > 0) {
163         TextImpl *textNode = ownerDocument()->createTextNode(m_value);
164 
165         // We want to use addChild and not appendChild here to avoid triggering
166         // mutation events. childrenChanged() will still be called.
167         addChild(textNode);
168     }
169 }
170 
childrenChanged()171 void AttrImpl::childrenChanged()
172 {
173     NodeBaseImpl::childrenChanged();
174 
175     // update value
176     DOMStringImpl *oldVal = m_value;
177     m_value = new DOMStringImpl((QChar *)nullptr, 0);
178     m_value->ref();
179     for (NodeImpl *n = firstChild(); n; n = n->nextSibling()) {
180         DOMStringImpl *data = static_cast<const TextImpl *>(n)->string();
181         m_value->append(data);
182     }
183 
184     if (m_element) {
185         int curr = id();
186         if (curr == ATTR_ID) {
187             m_element->updateId(oldVal, m_value);
188         }
189         m_element->parseAttribute(this);
190         m_element->attributeChanged(curr);
191     }
192 
193     oldVal->deref();
194 }
195 
setValue(const DOMString & v,int & exceptioncode)196 void AttrImpl::setValue(const DOMString &v, int &exceptioncode)
197 {
198     exceptioncode = 0;
199 
200     // do not interpret entities in the string, it is literal!
201 
202     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
203     if (isReadOnly()) {
204         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
205         return;
206     }
207 
208     // ### what to do on 0 ?
209     if (v.isNull()) {
210         exceptioncode = DOMException::DOMSTRING_SIZE_ERR;
211         return;
212     }
213 
214     if (m_value == v.implementation()) {
215         return;
216     }
217 
218     int e = 0;
219     removeChildren();
220     appendChild(ownerDocument()->createTextNode(v.implementation()), e);
221 }
222 
rewriteValue(const DOMString & newValue)223 void AttrImpl::rewriteValue(const DOMString &newValue)
224 {
225     int ec;
226 
227     // We want to avoid any notifications, so temporarily set m_element to 0
228     ElementImpl *saveElement = m_element;
229     m_element = nullptr;
230     setValue(newValue, ec);
231     m_element = saveElement;
232 }
233 
setNodeValue(const DOMString & v,int & exceptioncode)234 void AttrImpl::setNodeValue(const DOMString &v, int &exceptioncode)
235 {
236     exceptioncode = 0;
237     // NO_MODIFICATION_ALLOWED_ERR: taken care of by setValue()
238     setValue(v, exceptioncode);
239 }
240 
cloneNode(bool)241 WTF::PassRefPtr<NodeImpl> AttrImpl::cloneNode(bool /*deep*/)
242 {
243     AttrImpl *attr = new AttrImpl(nullptr, docPtr(), m_namespace, m_localName, m_prefix, m_value);
244     attr->setHTMLCompat(m_htmlCompat);
245     return attr;
246 }
247 
248 // DOM Section 1.1.1
childAllowed(NodeImpl * newChild)249 bool AttrImpl::childAllowed(NodeImpl *newChild)
250 {
251     if (!newChild) {
252         return false;
253     }
254 
255     return childTypeAllowed(newChild->nodeType());
256 }
257 
childTypeAllowed(unsigned short type)258 bool AttrImpl::childTypeAllowed(unsigned short type)
259 {
260     switch (type) {
261     case Node::TEXT_NODE:
262     case Node::ENTITY_REFERENCE_NODE:
263         return true;
264         break;
265     default:
266         return false;
267     }
268 }
269 
toString() const270 DOMString AttrImpl::toString() const
271 {
272     DOMString result;
273 
274     result += nodeName();
275 
276     // FIXME: substitute entities for any instances of " or ' --
277     // maybe easier to just use text value and ignore existing
278     // entity refs?
279 
280     if (!nodeValue().isEmpty()) {
281         //remove the else once the AttributeImpl changes are merged
282         result += "=\"";
283         result += nodeValue();
284         result += "\"";
285     }
286 
287     return result;
288 }
289 
setElement(ElementImpl * element)290 void AttrImpl::setElement(ElementImpl *element)
291 {
292     m_element = element;
293 }
294 
295 // -------------------------------------------------------------------------
296 
setValue(DOMStringImpl * value,ElementImpl * element)297 void AttributeImpl::setValue(DOMStringImpl *value, ElementImpl *element)
298 {
299     assert(value);
300     if (m_localName.id()) {
301         if (m_data.value == value) {
302             return;
303         }
304 
305         if (element && id() == ATTR_ID) {
306             element->updateId(m_data.value, value);
307         }
308 
309         m_data.value->deref();
310         m_data.value = value;
311         m_data.value->ref();
312 
313         if (element) {
314             element->parseAttribute(this);
315             element->attributeChanged(id());
316         }
317     } else {
318         int exceptioncode = 0;
319         m_data.attr->setValue(value, exceptioncode);
320         // AttrImpl::setValue() calls parseAttribute()
321     }
322 }
323 
rewriteValue(const DOMString & newValue)324 void AttributeImpl::rewriteValue(const DOMString &newValue)
325 {
326     if (m_localName.id()) {
327         // We may have m_data.value == null if we were given a normalized value
328         // off a removeAttribute (which would call parseNullAttribute()).
329         // Ignore such requests.
330         if (!m_data.value) {
331             return;
332         }
333 
334         DOMStringImpl *value = newValue.implementation();
335         if (m_data.value == value) {
336             return;
337         }
338 
339         m_data.value->deref();
340         m_data.value = value;
341         m_data.value->ref();
342     } else {
343         m_data.attr->rewriteValue(newValue);
344     }
345 }
346 
createAttr(ElementImpl * element,DocumentImpl * docPtr)347 AttrImpl *AttributeImpl::createAttr(ElementImpl *element, DocumentImpl *docPtr)
348 {
349     if (m_localName.id()) {
350         AttrImpl *attr = new AttrImpl(element, docPtr, m_namespace, m_localName, m_prefix, m_data.value);
351         if (!attr) {
352             return nullptr;
353         }
354         attr->setHTMLCompat(element->htmlCompat());
355         m_data.value->deref();
356         m_data.attr = attr;
357         m_data.attr->ref();
358         m_localName = emptyLocalName; /* "has implementation" flag */
359     }
360 
361     return m_data.attr;
362 }
363 
free()364 void AttributeImpl::free()
365 {
366     if (m_localName.id()) {
367         m_data.value->deref();
368     } else {
369         m_data.attr->setElement(nullptr);
370         m_data.attr->deref();
371     }
372 }
373 
374 // -------------------------------------------------------------------------
375 
376 class ElementRareDataImpl
377 {
378 public:
379     ElementRareDataImpl();
380     void resetComputedStyle();
tabIndex() const381     short tabIndex() const
382     {
383         return m_tabIndex;
384     }
setTabIndex(short _tabIndex)385     void setTabIndex(short _tabIndex)
386     {
387         m_tabIndex = _tabIndex;
388         m_hasTabIndex = true;
389     }
390 
391     RenderStyle *m_computedStyle;
392     signed short m_tabIndex;
393     bool m_hasTabIndex;
394 };
395 
396 typedef WTF::HashMap<const ElementImpl *, ElementRareDataImpl *> ElementRareDataMap;
397 
rareDataMap()398 static ElementRareDataMap &rareDataMap()
399 {
400     static ElementRareDataMap *dataMap = new ElementRareDataMap;
401     return *dataMap;
402 }
403 
rareDataFromMap(const ElementImpl * element)404 static ElementRareDataImpl *rareDataFromMap(const ElementImpl *element)
405 {
406     return rareDataMap().get(element);
407 }
408 
ElementRareDataImpl()409 inline ElementRareDataImpl::ElementRareDataImpl()
410     : m_computedStyle(nullptr), m_tabIndex(0), m_hasTabIndex(false)
411 {}
412 
resetComputedStyle()413 void ElementRareDataImpl::resetComputedStyle()
414 {
415     if (!m_computedStyle) {
416         return;
417     }
418     m_computedStyle->deref();
419     m_computedStyle = nullptr;
420 }
421 
422 // -------------------------------------------------------------------------
423 
ElementImpl(DocumentImpl * doc)424 ElementImpl::ElementImpl(DocumentImpl *doc)
425     : NodeBaseImpl(doc)
426 {
427     namedAttrMap = nullptr;
428     m_style.inlineDecls = nullptr;
429     m_prefix = emptyPrefixName;
430 }
431 
~ElementImpl()432 ElementImpl::~ElementImpl()
433 {
434     if (namedAttrMap) {
435         namedAttrMap->detachFromElement();
436         namedAttrMap->deref();
437     }
438 
439     if (m_style.inlineDecls) {
440         if (CSSStyleDeclarationImpl *ild = inlineStyleDecls()) {
441             // remove inline declarations
442             ild->setNode(nullptr);
443             ild->setParent(nullptr);
444             ild->deref();
445         }
446         if (CSSStyleDeclarationImpl *ncd = nonCSSStyleDecls()) {
447             // remove presentational declarations
448             ncd->setNode(nullptr);
449             ncd->setParent(nullptr);
450             ncd->deref();
451             delete m_style.combinedDecls;
452         }
453     }
454 
455     if (!m_elementHasRareData) {
456         ASSERT(!rareDataMap().contains(this));
457     } else {
458         ElementRareDataMap &dataMap = rareDataMap();
459         ElementRareDataMap::iterator it = dataMap.find(this);
460         ASSERT(it != dataMap.end());
461         delete it->second;
462         dataMap.remove(it);
463     }
464 }
465 
rareData()466 ElementRareDataImpl *ElementImpl::rareData()
467 {
468     return m_elementHasRareData ? rareDataFromMap(this) : nullptr;
469 }
470 
rareData() const471 const ElementRareDataImpl *ElementImpl::rareData() const
472 {
473     return m_elementHasRareData ? rareDataFromMap(this) : nullptr;
474 }
475 
createRareData()476 ElementRareDataImpl *ElementImpl::createRareData()
477 {
478     if (m_elementHasRareData) {
479         return rareDataMap().get(this);
480     }
481     ASSERT(!rareDataMap().contains(this));
482     ElementRareDataImpl *data = new ElementRareDataImpl();
483     rareDataMap().set(this, data);
484     m_elementHasRareData = true;
485     return data;
486 }
487 
removeAttribute(NodeImpl::Id id,int & exceptioncode)488 void ElementImpl::removeAttribute(NodeImpl::Id id, int &exceptioncode)
489 {
490     if (namedAttrMap) {
491         namedAttrMap->removeNamedItem(id, emptyPrefixName, false, exceptioncode);
492         if (exceptioncode == DOMException::NOT_FOUND_ERR) {
493             exceptioncode = 0;
494         }
495     }
496 }
497 
nodeType() const498 unsigned short ElementImpl::nodeType() const
499 {
500     return Node::ELEMENT_NODE;
501 }
502 
localName() const503 DOMString ElementImpl::localName() const
504 {
505     return LocalName::fromId(id()).toString();
506 }
507 
tagName() const508 DOMString ElementImpl::tagName() const
509 {
510     DOMString tn = LocalName::fromId(id()).toString();
511 
512     if (m_htmlCompat) {
513         tn = tn.upper();
514     }
515 
516     DOMString prefix = m_prefix.toString();
517     if (!prefix.isEmpty()) {
518         return prefix + DOMString(":") + tn;
519     }
520 
521     return tn;
522 }
523 
nonCaseFoldedTagName() const524 DOMString ElementImpl::nonCaseFoldedTagName() const
525 {
526     DOMString tn = LocalName::fromId(id()).toString();
527 
528     DOMString prefix = m_prefix.toString();
529     if (!prefix.isEmpty()) {
530         return prefix + DOMString(":") + tn;
531     }
532 
533     return tn;
534 }
535 
536 /*DOMStringImpl* ElementImpl::getAttributeImpl(NodeImpl::Id id, PrefixName prefix, bool nsAware) const
537 {
538     return namedAttrMap ? namedAttrMap->getValue(id, prefix, nsAware) : 0;
539 }*/
540 
setAttribute(NodeImpl::Id id,const PrefixName & prefix,bool nsAware,const DOMString & value,int & exceptioncode)541 void ElementImpl::setAttribute(NodeImpl::Id id, const PrefixName &prefix, bool nsAware, const DOMString &value, int &exceptioncode)
542 {
543     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
544     if (isReadOnly()) {
545         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
546         return;
547     }
548     attributes()->setValue(id, value.implementation(), prefix, nsAware);
549 }
550 
setAttributeNS(const DOMString & namespaceURI,const DOMString & qualifiedName,const DOMString & value,int & exceptioncode)551 void ElementImpl::setAttributeNS(const DOMString &namespaceURI, const DOMString &qualifiedName,
552                                  const DOMString &value, int &exceptioncode)
553 {
554     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
555     if (isReadOnly()) {
556         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
557         return;
558     }
559     int colonPos;
560     if (!DOM::checkQualifiedName(qualifiedName, namespaceURI, &colonPos,
561                                  false/*nameCanBeNull*/, false/*nameCanBeEmpty*/,
562                                  &exceptioncode)) {
563         return;
564     }
565     LocalName localname;
566     PrefixName prefixname;
567     splitPrefixLocalName(qualifiedName, prefixname, localname, m_htmlCompat, colonPos);
568     NamespaceName namespacename = NamespaceName::fromString(namespaceURI);
569     attributes()->setValue(makeId(namespacename.id(), localname.id()), value.implementation(), prefixname, true /*nsAware*/);
570 }
571 
setAttribute(NodeImpl::Id id,const DOMString & value)572 void ElementImpl::setAttribute(NodeImpl::Id id, const DOMString &value)
573 {
574     int exceptioncode = 0;
575     setAttribute(id, emptyPrefixName, false, value, exceptioncode);
576 }
577 
setBooleanAttribute(NodeImpl::Id id,bool b)578 void ElementImpl::setBooleanAttribute(NodeImpl::Id id, bool b)
579 {
580     if (b) {
581         setAttribute(id, "1");
582     } else {
583         int ec;
584         removeAttribute(id, ec);
585     }
586 }
587 
setAttributeMap(NamedAttrMapImpl * list)588 void ElementImpl::setAttributeMap(NamedAttrMapImpl *list)
589 {
590     // If setting the whole map changes the id attribute, we need to
591     // call updateId.
592     DOMStringImpl *oldId = namedAttrMap ? namedAttrMap->getValue(ATTR_ID) : nullptr;
593     DOMStringImpl *newId = list ? list->getValue(ATTR_ID) : nullptr;
594 
595     if (oldId || newId) {
596         updateId(oldId, newId);
597     }
598 
599     if (namedAttrMap) {
600         namedAttrMap->detachFromElement();
601         namedAttrMap->deref();
602     }
603 
604     namedAttrMap = list;
605 
606     if (namedAttrMap) {
607         namedAttrMap->ref();
608         assert(namedAttrMap->m_element == nullptr);
609         namedAttrMap->setElement(this);
610         unsigned len = namedAttrMap->length();
611         for (unsigned i = 0; i < len; i++) {
612             parseAttribute(&namedAttrMap->m_attrs[i]);
613             attributeChanged(namedAttrMap->m_attrs[i].id());
614         }
615     }
616 }
617 
cloneNode(bool deep)618 WTF::PassRefPtr<NodeImpl> ElementImpl::cloneNode(bool deep)
619 {
620     WTF::RefPtr<ElementImpl> clone; // Make sure to guard...
621     clone = document()->createElementNS(namespaceURI(), nonCaseFoldedTagName() /* includes prefix*/);
622     if (!clone) {
623         return nullptr;
624     }
625     finishCloneNode(clone.get(), deep);
626     return clone;
627 }
628 
finishCloneNode(ElementImpl * clone,bool deep)629 void ElementImpl::finishCloneNode(ElementImpl *clone, bool deep)
630 {
631     // clone attributes
632     if (namedAttrMap || m_needsStyleAttributeUpdate) {
633         clone->attributes()->copyAttributes(attributes(true));
634     }
635 
636     assert(!m_needsStyleAttributeUpdate);   // ensured by previous line
637 
638     // clone individual style rules
639     if (m_style.inlineDecls) {
640         if (m_hasCombinedStyle) {
641             if (!clone->m_hasCombinedStyle) {
642                 clone->createNonCSSDecl();
643             }
644             if (m_style.combinedDecls->inlineDecls) {
645                 *(clone->getInlineStyleDecls()) = *m_style.combinedDecls->inlineDecls;
646             }
647             *clone->m_style.combinedDecls->nonCSSDecls = *m_style.combinedDecls->nonCSSDecls;
648         } else {
649             *(clone->getInlineStyleDecls()) = *m_style.inlineDecls;
650         }
651     }
652 
653     // ### fold above style cloning into this function?
654     clone->copyNonAttributeProperties(this);
655 
656     if (deep) {
657         cloneChildNodes(clone);
658     }
659 
660     // copy over our compatibility mode.
661     clone->setHTMLCompat(htmlCompat());
662 }
663 
hasAttributes() const664 bool ElementImpl::hasAttributes() const
665 {
666     return namedAttrMap && namedAttrMap->length() > 0;
667 }
668 
hasAttribute(const DOMString & name) const669 bool ElementImpl::hasAttribute(const DOMString &name) const
670 {
671     LocalName localname;
672     PrefixName prefixname;
673     splitPrefixLocalName(name, prefixname, localname, m_htmlCompat);
674     if (!localname.id()) {
675         return false;
676     }
677     if (!namedAttrMap) {
678         return false;
679     }
680     return namedAttrMap->getValue(makeId(emptyNamespace, localname.id()), prefixname, false) != nullptr;
681 }
682 
hasAttributeNS(const DOMString & namespaceURI,const DOMString & localName) const683 bool ElementImpl::hasAttributeNS(const DOMString &namespaceURI,
684                                  const DOMString &localName) const
685 {
686     NamespaceName namespacename = NamespaceName::fromString(namespaceURI);
687     LocalName localname = LocalName::fromString(localName, m_htmlCompat ? IDS_NormalizeLower : IDS_CaseSensitive);
688     NodeImpl::Id id = makeId(namespacename.id(), localname.id());
689     if (!id) {
690         return false;
691     }
692     if (!namedAttrMap) {
693         return false;
694     }
695     return namedAttrMap->getValue(id, emptyPrefixName, true) != nullptr;
696 }
697 
getAttribute(const DOMString & name)698 DOMString ElementImpl::getAttribute(const DOMString &name)
699 {
700     LocalName localname;
701     PrefixName prefixname;
702     splitPrefixLocalName(name, prefixname, localname, m_htmlCompat);
703     if (!localname.id()) {
704         return DOMString();
705     }
706     return getAttribute(makeId(emptyNamespace, localname.id()), prefixname, false /*nsAware*/);
707 }
708 
setAttribute(const DOMString & name,const DOMString & value,int & exceptioncode)709 void ElementImpl::setAttribute(const DOMString &name, const DOMString &value, int &exceptioncode)
710 {
711     int colon;
712     if (!DOM::checkQualifiedName(name, "", &colon, false/*nameCanBeNull*/, false/*nameCanBeEmpty*/, &exceptioncode)) {
713         return;
714     }
715     LocalName localname;
716     PrefixName prefixname;
717     splitPrefixLocalName(name, prefixname, localname, m_htmlCompat, colon);
718     setAttribute(makeId(emptyNamespace, localname.id()), prefixname, false, value.implementation(), exceptioncode);
719 }
720 
removeAttribute(const DOMString & name,int & exceptioncode)721 void ElementImpl::removeAttribute(const DOMString &name, int &exceptioncode)
722 {
723     LocalName localname;
724     PrefixName prefixname;
725     splitPrefixLocalName(name, prefixname, localname, m_htmlCompat);
726 
727     // FIXME what if attributes(false) == 0?
728     attributes(false)->removeNamedItem(makeId(emptyNamespace, localname.id()), prefixname, false, exceptioncode);
729 
730     // it's allowed to remove attributes that don't exist.
731     if (exceptioncode == DOMException::NOT_FOUND_ERR) {
732         exceptioncode = 0;
733     }
734 }
735 
getAttributeNode(const DOMString & name)736 AttrImpl *ElementImpl::getAttributeNode(const DOMString &name)
737 {
738     LocalName localname;
739     PrefixName prefixname;
740     splitPrefixLocalName(name, prefixname, localname, m_htmlCompat);
741 
742     if (!localname.id()) {
743         return nullptr;
744     }
745     if (!namedAttrMap) {
746         return nullptr;
747     }
748 
749     return static_cast<AttrImpl *>(attributes()->getNamedItem(makeId(emptyNamespace, localname.id()), prefixname, false));
750 }
751 
setAttributeNode(AttrImpl * newAttr,int & exceptioncode)752 Attr ElementImpl::setAttributeNode(AttrImpl *newAttr, int &exceptioncode)
753 {
754     if (!newAttr) {
755         exceptioncode = DOMException::NOT_FOUND_ERR;
756         return nullptr;
757     }
758     Attr r = attributes(false)->setNamedItem(newAttr, emptyPrefixName, true, exceptioncode);
759     if (!exceptioncode) {
760         newAttr->setOwnerElement(this);
761     }
762     return r;
763 }
764 
removeAttributeNode(AttrImpl * oldAttr,int & exceptioncode)765 Attr ElementImpl::removeAttributeNode(AttrImpl *oldAttr, int &exceptioncode)
766 {
767     if (!oldAttr || oldAttr->ownerElement() != this) {
768         exceptioncode = DOMException::NOT_FOUND_ERR;
769         return nullptr;
770     }
771 
772     if (isReadOnly()) {
773         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
774         return nullptr;
775     }
776 
777     if (!namedAttrMap) {
778         exceptioncode = DOMException::NOT_FOUND_ERR;
779         return nullptr;
780     }
781 
782     return attributes(false)->removeAttr(oldAttr);
783 }
784 
getAttributeNS(const DOMString & namespaceURI,const DOMString & localName,int & exceptioncode)785 DOMString ElementImpl::getAttributeNS(const DOMString &namespaceURI,
786                                       const DOMString &localName,
787                                       int &exceptioncode)
788 {
789     if (!localName.implementation()) {
790         exceptioncode = DOMException::NOT_FOUND_ERR;
791         return DOMString();
792     }
793 
794     LocalName localname = LocalName::fromString(localName, m_htmlCompat ? IDS_NormalizeLower : IDS_CaseSensitive);
795     NamespaceName namespacename = NamespaceName::fromString(namespaceURI);
796 
797     NodeImpl::Id id = makeId(namespacename.id(), localname.id());
798     return getAttribute(id, emptyPrefixName, true);
799 }
800 
removeAttributeNS(const DOMString & namespaceURI,const DOMString & localName,int & exceptioncode)801 void ElementImpl::removeAttributeNS(const DOMString &namespaceURI,
802                                     const DOMString &localName,
803                                     int &exceptioncode)
804 {
805     if (!localName.implementation()) {
806         exceptioncode = DOMException::NOT_FOUND_ERR;
807         return;
808     }
809 
810     NamespaceName namespacename = NamespaceName::fromString(namespaceURI);
811     LocalName localname = LocalName::fromString(localName, m_htmlCompat ? IDS_NormalizeLower : IDS_CaseSensitive);
812 
813     NodeImpl::Id id = makeId(namespacename.id(), localname.id());
814     attributes(false)->removeNamedItem(id, emptyPrefixName, true, exceptioncode);
815 }
816 
getAttributeNodeNS(const DOMString & namespaceURI,const DOMString & localName,int & exceptioncode)817 AttrImpl *ElementImpl::getAttributeNodeNS(const DOMString &namespaceURI,
818         const DOMString &localName,
819         int &exceptioncode)
820 {
821     if (!localName.implementation()) {
822         exceptioncode = DOMException::NOT_FOUND_ERR;
823         return nullptr;
824     }
825 
826     NamespaceName namespacename = NamespaceName::fromString(namespaceURI);
827     LocalName localname = LocalName::fromString(localName, m_htmlCompat ? IDS_NormalizeLower : IDS_CaseSensitive);
828 
829     NodeImpl::Id id = makeId(namespacename.id(), localname.id());
830     if (!attributes(true)) {
831         return nullptr;
832     }
833     return static_cast<AttrImpl *>(attributes()->getNamedItem(id, emptyPrefixName, true));
834 }
835 
setAttributeNodeNS(AttrImpl * newAttr,int & exceptioncode)836 Attr ElementImpl::setAttributeNodeNS(AttrImpl *newAttr, int &exceptioncode)
837 {
838     if (!newAttr) {
839         exceptioncode = DOMException::NOT_FOUND_ERR;
840         return nullptr;
841     }
842     // WRONG_DOCUMENT_ERR and INUSE_ATTRIBUTE_ERR are already tested & thrown by setNamedItem
843     Attr r = attributes(false)->setNamedItem(newAttr, emptyPrefixName, true, exceptioncode);
844     if (!exceptioncode) {
845         newAttr->setOwnerElement(this);
846     }
847     return r;
848 }
849 
nodeName() const850 DOMString ElementImpl::nodeName() const
851 {
852     return tagName();
853 }
854 
namespaceURI() const855 DOMString ElementImpl::namespaceURI() const
856 {
857     return NamespaceName::fromId(namespacePart(id())).toString();
858 }
859 
setPrefix(const DOMString & _prefix,int & exceptioncode)860 void ElementImpl::setPrefix(const DOMString &_prefix, int &exceptioncode)
861 {
862     checkSetPrefix(_prefix, exceptioncode);
863     if (exceptioncode) {
864         return;
865     }
866     m_prefix = PrefixName::fromString(_prefix);
867 }
868 
tabIndex() const869 short ElementImpl::tabIndex() const
870 {
871     return m_elementHasRareData ? rareData()->tabIndex() : 0;
872 }
873 
setTabIndex(short _tabIndex)874 void ElementImpl::setTabIndex(short _tabIndex)
875 {
876     createRareData()->setTabIndex(_tabIndex);
877 }
878 
setNoTabIndex()879 void ElementImpl::setNoTabIndex()
880 {
881     if (!m_elementHasRareData) {
882         return;
883     }
884     rareData()->m_hasTabIndex = false;
885 }
886 
hasTabIndex() const887 bool ElementImpl::hasTabIndex() const
888 {
889     return m_elementHasRareData ? rareData()->m_hasTabIndex : false;
890 }
891 
defaultEventHandler(EventImpl * e)892 void ElementImpl::defaultEventHandler(EventImpl *e)
893 {
894     if (!e->defaultHandled() && document()->part() && e->id() == EventImpl::KEYPRESS_EVENT && e->isKeyRelatedEvent()) {
895         const KHTMLPart *part = document()->part();
896         bool isContentEditableElement = part->isEditable() || (focused() && isContentEditable());
897         if (isContentEditableElement || part->isCaretMode()) {
898             if (document()->view() && document()->view()->caretKeyPressEvent(static_cast<KeyEventBaseImpl *>(e)->qKeyEvent())) {
899                 e->setDefaultHandled();
900                 return;
901             }
902             if (isContentEditableElement && part->editor()->handleKeyEvent(static_cast<KeyEventBaseImpl *>(e)->qKeyEvent())) {
903                 e->setDefaultHandled();
904                 return;
905             }
906         }
907     }
908 
909     if (m_render && m_render->scrollsOverflow()) {
910         switch (e->id()) {
911         case EventImpl::KEYDOWN_EVENT:
912         case EventImpl::KEYUP_EVENT:
913         case EventImpl::KEYPRESS_EVENT:
914             if (!focused() || e->target() != this) {
915                 break;
916             }
917         // fall through
918         case EventImpl::KHTML_MOUSEWHEEL_EVENT:
919             if (m_render->handleEvent(*e)) {
920                 e->setDefaultHandled();
921             }
922         default:
923             break;
924         }
925     }
926 }
927 
createAttributeMap() const928 void ElementImpl::createAttributeMap() const
929 {
930     namedAttrMap = new NamedAttrMapImpl(const_cast<ElementImpl *>(this));
931     namedAttrMap->ref();
932 }
933 
styleForRenderer(RenderObject *)934 RenderStyle *ElementImpl::styleForRenderer(RenderObject * /*parentRenderer*/)
935 {
936     return document()->styleSelector()->styleForElement(this);
937 }
938 
createRenderer(RenderArena * arena,RenderStyle * style)939 RenderObject *ElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
940 {
941     if (document()->documentElement() == this && style->display() == NONE) {
942         // Ignore display: none on root elements.  Force a display of block in that case.
943         RenderBlock *result = new(arena) RenderBlock(this);
944         if (result) {
945             result->setStyle(style);
946         }
947         return result;
948     }
949     return RenderObject::createObject(this, style);
950 }
951 
attach()952 void ElementImpl::attach()
953 {
954     assert(!attached());
955     assert(!m_render);
956     assert(parentNode());
957 
958 #if SPEED_DEBUG < 1
959     createRendererIfNeeded();
960 #endif
961 
962     NodeBaseImpl::attach();
963 }
964 
close()965 void ElementImpl::close()
966 {
967     NodeImpl::close();
968 
969     // Trigger all the addChild changes as one large dynamic appendChildren change
970     if (attached()) {
971         backwardsStructureChanged();
972     }
973 }
974 
detach()975 void ElementImpl::detach()
976 {
977     document()->dynamicDomRestyler().resetDependencies(this);
978 
979     if (ElementRareDataImpl *rd = rareData()) {
980         rd->resetComputedStyle();
981     }
982 
983     NodeBaseImpl::detach();
984 }
985 
structureChanged()986 void ElementImpl::structureChanged()
987 {
988     NodeBaseImpl::structureChanged();
989 
990     if (!document()->renderer()) {
991         return;    // the document is about to be destroyed
992     }
993 
994     document()->dynamicDomRestyler().restyleDependent(this, StructuralDependency);
995     // In theory BackwardsStructurualDependencies are indifferent to prepend,
996     // but it's too rare to optimize.
997     document()->dynamicDomRestyler().restyleDependent(this, BackwardsStructuralDependency);
998 }
999 
backwardsStructureChanged()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 
attributeChanged(NodeImpl::Id id)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 
recalcStyle(StyleChange change)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 
isFocusableImpl(FocusType ft) const1106 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 
isContentEditable() const1131 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 
setContentEditable(bool enabled)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
childAllowed(NodeImpl * newChild)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 
childTypeAllowed(unsigned short type)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 
scrollIntoView(bool)1204 void ElementImpl::scrollIntoView(bool /*alignToTop*/)
1205 {
1206     // ###
1207     qCWarning(KHTML_LOG) << "non-standard scrollIntoView() not implemented";
1208 }
1209 
createNonCSSDecl()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 
getInlineStyleDecls()1225 CSSInlineStyleDeclarationImpl *ElementImpl::getInlineStyleDecls()
1226 {
1227     if (!inlineStyleDecls()) {
1228         createInlineDecl();
1229     }
1230     return inlineStyleDecls();
1231 }
1232 
createInlineDecl()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 
dispatchAttrRemovalEvent(NodeImpl::Id,DOMStringImpl *)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 
dispatchAttrAdditionEvent(NodeImpl::Id,DOMStringImpl *)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 
updateId(DOMStringImpl * oldId,DOMStringImpl * newId)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 
removeId(const DOMString & id)1286 void ElementImpl::removeId(const DOMString &id)
1287 {
1288     document()->getElementByIdCache().remove(id, this);
1289 }
1290 
addId(const DOMString & id)1291 void ElementImpl::addId(const DOMString &id)
1292 {
1293     document()->getElementByIdCache().add(id, this);
1294 }
1295 
insertedIntoDocument()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 
removedFromDocument()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 
openTagStartToString(bool expandurls) const1318 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 }
selectionToString(NodeImpl * selectionStart,NodeImpl * selectionEnd,int startOffset,int endOffset,bool & found) const1373 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 
toString() const1400 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 
computedStyle()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
firstElementChild() const1447 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 
lastElementChild() const1456 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 
previousElementSibling() const1465 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 
nextElementSibling() const1474 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 
childElementCount() const1483 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 
blur()1494 void ElementImpl::blur()
1495 {
1496     if (document()->focusNode() == this) {
1497         document()->setFocusNode(nullptr);
1498     }
1499 }
1500 
focus()1501 void ElementImpl::focus()
1502 {
1503     document()->setFocusNode(this);
1504 }
1505 
synchronizeStyleAttribute() const1506 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 
XMLElementImpl(DocumentImpl * doc,NamespaceName namespacename,LocalName localName,PrefixName prefix)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 
~XMLElementImpl()1525 XMLElementImpl::~XMLElementImpl()
1526 {
1527 }
1528 
cloneNode(bool deep)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 
parseAttribute(AttributeImpl * attr)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 
NamedAttrMapImpl(ElementImpl * element)1550 NamedAttrMapImpl::NamedAttrMapImpl(ElementImpl *element)
1551     : m_element(element)
1552 {
1553 }
1554 
~NamedAttrMapImpl()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 
getNamedItem(NodeImpl::Id id,const PrefixName & prefix,bool nsAware)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 
removeNamedItem(NodeImpl::Id id,const PrefixName & prefix,bool nsAware,int & exceptioncode)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 
setNamedItem(NodeImpl * arg,const PrefixName & prefix,bool nsAware,int & exceptioncode)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 
item(unsigned index)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 
idAt(unsigned index) const1686 NodeImpl::Id NamedAttrMapImpl::idAt(unsigned index) const
1687 {
1688     assert(index < m_attrs.size());
1689     return m_attrs[index].id();
1690 }
1691 
valueAt(unsigned index) const1692 DOMStringImpl *NamedAttrMapImpl::valueAt(unsigned index) const
1693 {
1694     assert(index < m_attrs.size());
1695     return m_attrs[index].val();
1696 }
1697 
getValue(NodeImpl::Id id,const PrefixName & prefix,bool nsAware) const1698 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 
setValue(NodeImpl::Id id,DOMStringImpl * value,const PrefixName & prefix,bool nsAware)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 
removeAttr(AttrImpl * attr)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 
copyAttributes(NamedAttrMapImpl * other)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 
find(NodeImpl::Id id,const PrefixName & prefix,bool nsAware) const1802 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 
setElement(ElementImpl * element)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 
detachFromElement()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 
setClass(const DOMString & string)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 
setValueWithoutElementUpdate(NodeImpl::Id id,DOMStringImpl * value)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 }
1884