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