KHtml

dom_elementimpl.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll ([email protected])
5  * (C) 1999 Antti Koivisto ([email protected])
6  * (C) 2001 Peter Kelly ([email protected])
7  * (C) 2001 Dirk Mueller ([email protected])
8  * (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
9  * (C) 2005, 2008 Maksim Orlovich ([email protected])
10  * (C) 2006 Allan Sandfeld Jensen ([email protected])
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 
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 
92 AttrImpl::~AttrImpl()
93 {
94  m_value->deref();
95 }
96 
97 DOMString AttrImpl::nodeName() const
98 {
99  return name();
100 }
101 
102 unsigned short AttrImpl::nodeType() const
103 {
104  return Node::ATTRIBUTE_NODE;
105 }
106 
107 DOMString AttrImpl::prefix() const
108 {
109  return m_prefix.toString();
110 }
111 
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 
122 DOMString AttrImpl::namespaceURI() const
123 {
124  if (m_htmlCompat) {
125  return DOMString();
126  }
127  return m_namespace.toString();
128 }
129 
130 DOMString AttrImpl::localName() const
131 {
132  return m_localName.toString();
133 }
134 
135 DOMString AttrImpl::nodeValue() const
136 {
137  return m_value;
138 }
139 
140 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 
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 
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 
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 
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 
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 
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
249 bool AttrImpl::childAllowed(NodeImpl *newChild)
250 {
251  if (!newChild) {
252  return false;
253  }
254 
255  return childTypeAllowed(newChild->nodeType());
256 }
257 
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 
270 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 
290 void AttrImpl::setElement(ElementImpl *element)
291 {
292  m_element = element;
293 }
294 
295 // -------------------------------------------------------------------------
296 
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 
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 
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 
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();
381  short tabIndex() const
382  {
383  return m_tabIndex;
384  }
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 
398 static ElementRareDataMap &rareDataMap()
399 {
400  static ElementRareDataMap *dataMap = new ElementRareDataMap;
401  return *dataMap;
402 }
403 
404 static ElementRareDataImpl *rareDataFromMap(const ElementImpl *element)
405 {
406  return rareDataMap().get(element);
407 }
408 
409 inline ElementRareDataImpl::ElementRareDataImpl()
410  : m_computedStyle(nullptr), m_tabIndex(0), m_hasTabIndex(false)
411 {}
412 
413 void ElementRareDataImpl::resetComputedStyle()
414 {
415  if (!m_computedStyle) {
416  return;
417  }
418  m_computedStyle->deref();
419  m_computedStyle = nullptr;
420 }
421 
422 // -------------------------------------------------------------------------
423 
424 ElementImpl::ElementImpl(DocumentImpl *doc)
425  : NodeBaseImpl(doc)
426 {
427  namedAttrMap = nullptr;
428  m_style.inlineDecls = nullptr;
429  m_prefix = emptyPrefixName;
430 }
431 
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 
466 ElementRareDataImpl *ElementImpl::rareData()
467 {
468  return m_elementHasRareData ? rareDataFromMap(this) : nullptr;
469 }
470 
471 const ElementRareDataImpl *ElementImpl::rareData() const
472 {
473  return m_elementHasRareData ? rareDataFromMap(this) : nullptr;
474 }
475 
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 
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 
498 unsigned short ElementImpl::nodeType() const
499 {
500  return Node::ELEMENT_NODE;
501 }
502 
503 DOMString ElementImpl::localName() const
504 {
505  return LocalName::fromId(id()).toString();
506 }
507 
508 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 
524 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 
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 
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 
572 void ElementImpl::setAttribute(NodeImpl::Id id, const DOMString &value)
573 {
574  int exceptioncode = 0;
575  setAttribute(id, emptyPrefixName, false, value, exceptioncode);
576 }
577 
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 
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 
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 
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 
664 bool ElementImpl::hasAttributes() const
665 {
666  return namedAttrMap && namedAttrMap->length() > 0;
667 }
668 
669 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 
683 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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
850 DOMString ElementImpl::nodeName() const
851 {
852  return tagName();
853 }
854 
855 DOMString ElementImpl::namespaceURI() const
856 {
857  return NamespaceName::fromId(namespacePart(id())).toString();
858 }
859 
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 
869 short ElementImpl::tabIndex() const
870 {
871  return m_elementHasRareData ? rareData()->tabIndex() : 0;
872 }
873 
874 void ElementImpl::setTabIndex(short _tabIndex)
875 {
876  createRareData()->setTabIndex(_tabIndex);
877 }
878 
879 void ElementImpl::setNoTabIndex()
880 {
881  if (!m_elementHasRareData) {
882  return;
883  }
884  rareData()->m_hasTabIndex = false;
885 }
886 
887 bool ElementImpl::hasTabIndex() const
888 {
889  return m_elementHasRareData ? rareData()->m_hasTabIndex : false;
890 }
891 
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 
928 void ElementImpl::createAttributeMap() const
929 {
930  namedAttrMap = new NamedAttrMapImpl(const_cast<ElementImpl *>(this));
931  namedAttrMap->ref();
932 }
933 
934 RenderStyle *ElementImpl::styleForRenderer(RenderObject * /*parentRenderer*/)
935 {
936  return document()->styleSelector()->styleForElement(this);
937 }
938 
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 
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 
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 
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 
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 
1000 void ElementImpl::backwardsStructureChanged()
1001 {
1002  NodeBaseImpl::backwardsStructureChanged();
1003 
1004  if (!document()->renderer()) {
1005  return; // the document is about to be destroyed
1006  }
1007 
1008  // Most selectors are not affected by append. Fire the few that are.
1009  document()->dynamicDomRestyler().restyleDependent(this, BackwardsStructuralDependency);
1010 }
1011 
1012 void ElementImpl::attributeChanged(NodeImpl::Id id)
1013 {
1014  if (!document()->renderer()) {
1015  return; // the document is about to be destroyed
1016  }
1017 
1018 #if 0 // one-one dependencies for attributes disabled
1019  document()->dynamicDomRestyler().restyleDependent(this, AttributeDependency);
1020 #endif
1021  if (document()->dynamicDomRestyler().checkDependency(id, PersonalDependency)) {
1022  setChanged(true);
1023  }
1024  if (document()->dynamicDomRestyler().checkDependency(id, AncestorDependency)) {
1025  setChangedAscendentAttribute(true);
1026  }
1027  if (document()->dynamicDomRestyler().checkDependency(id, PredecessorDependency) && parent())
1028  // Any element that dependt on a predecessors attribute, also depend structurally on parent
1029  {
1030  parent()->structureChanged();
1031  }
1032 }
1033 
1034 void ElementImpl::recalcStyle(StyleChange change)
1035 {
1036  // ### should go away and be done in renderobject
1037  RenderStyle *_style = m_render ? m_render->style() : nullptr;
1038  bool hasParentRenderer = parent() ? parent()->attached() : false;
1039 
1040  if ((change > NoChange || changed())) {
1041  if (ElementRareDataImpl *rd = rareData()) {
1042  rd->resetComputedStyle();
1043  }
1044  }
1045 
1046 #if 0
1047  const char *debug;
1048  switch (change) {
1049  case NoChange: debug = "NoChange";
1050  break;
1051  case NoInherit: debug = "NoInherit";
1052  break;
1053  case Inherit: debug = "Inherit";
1054  break;
1055  case Force: debug = "Force";
1056  break;
1057  }
1058  qDebug("recalcStyle(%d: %s, changed: %d)[%p: %s]", change, debug, changed(), this, tagName().string().toLatin1().constData());
1059 #endif
1060  if (hasParentRenderer && (change >= Inherit || changed() || (change == NoInherit && affectedByNoInherit()))) {
1061  RenderStyle *newStyle = document()->styleSelector()->styleForElement(this);
1062  newStyle->ref();
1063  StyleChange ch = diff(_style, newStyle);
1064  if (ch == Detach) {
1065  if (attached()) {
1066  detach();
1067  }
1068  // ### Suboptimal. Style gets calculated again.
1069  attach();
1070  // attach recalulates the style for all children. No need to do it twice.
1071  setChanged(false);
1072  setHasChangedChild(false);
1073  newStyle->deref();
1074  return;
1075  } else if (ch != NoChange) {
1076  if (m_render) {
1077  m_render->setStyle(newStyle);
1078  }
1079  }
1080  newStyle->deref();
1081 
1082  if (change != Force) {
1083  change = ch;
1084  }
1085  }
1086  // If a changed attribute has ancestor dependencies, restyle all children
1087  if (changedAscendentAttribute()) {
1088  change = Force;
1089  setChangedAscendentAttribute(false);
1090  }
1091 
1092  NodeImpl *n;
1093  for (n = _first; n; n = n->nextSibling()) {
1094  if (change >= Inherit || n->hasChangedChild() || n->changed() ||
1095  (change == NoInherit && n->affectedByNoInherit())
1096  ) {
1097  //qDebug(" (%p) calling recalcStyle on child %p/%s, change=%d", this, n, n->isElementNode() ? ((ElementImpl *)n)->tagName().string().toLatin1().constData() : n->isTextNode() ? "text" : "unknown", change );
1098  n->recalcStyle(change);
1099  }
1100  }
1101 
1102  setChanged(false);
1103  setHasChangedChild(false);
1104 }
1105 
1106 bool ElementImpl::isFocusableImpl(FocusType ft) const
1107 {
1108  if (m_render && m_render->scrollsOverflow()) {
1109  return true;
1110  }
1111 
1112  // See WAI-ARIA 1.0, UA implementor's guide, 3.1 for the rules this
1113  // implements.
1114  if (hasTabIndex()) {
1115  int ti = tabIndex();
1116 
1117  // Negative things are focusable, but not in taborder
1118  if (ti < 0) {
1119  return (ft != FT_Tab);
1120  } else { // ... while everything else is completely focusable
1121  return true;
1122  }
1123  }
1124 
1125  // Only make editable elements selectable if its parent element
1126  // is not editable. FIXME: this is not 100% right as non-editable elements
1127  // within editable elements are focusable too.
1128  return isContentEditable() && !(parentNode() && parentNode()->isContentEditable());
1129 }
1130 
1131 bool ElementImpl::isContentEditable() const
1132 {
1133  if (document()->part() && document()->part()->isEditable()) {
1134  return true;
1135  }
1136 
1137  // document()->updateRendering();
1138 
1139  if (!renderer()) {
1140  if (parentNode()) {
1141  return parentNode()->isContentEditable();
1142  } else {
1143  return false;
1144  }
1145  }
1146 
1147  return renderer()->style()->userInput() == UI_ENABLED;
1148 }
1149 
1150 void ElementImpl::setContentEditable(bool enabled)
1151 {
1152  // FIXME: the approach is flawed, better use an enum instead of bool
1153  int value;
1154  if (enabled) {
1155  value = CSS_VAL_ENABLED;
1156  } else {
1157  // Intelligently use "none" or "disabled", depending on the type of
1158  // element
1159  // FIXME: intelligence not impl'd yet
1160  value = CSS_VAL_NONE;
1161 
1162  // FIXME: reset caret if it is in this node or a child
1163  }/*end if*/
1164  // FIXME: use addCSSProperty when I get permission to move it here
1165 // qCDebug(KHTML_LOG) << "CSS_PROP__KHTML_USER_INPUT: "<< value;
1166  getInlineStyleDecls()->setProperty(CSS_PROP__KHTML_USER_INPUT, value, false);
1167  setChanged();
1168 }
1169 
1170 // DOM Section 1.1.1
1171 bool ElementImpl::childAllowed(NodeImpl *newChild)
1172 {
1173  if (!childTypeAllowed(newChild->nodeType())) {
1174  return false;
1175  }
1176 
1177  // ### check xml element allowedness according to DTD
1178 
1179  // If either this node or the other node is an XML element node, allow regardless (we don't do DTD checks for XML
1180  // yet)
1181  if (isXMLElementNode() || newChild->isXMLElementNode()) {
1182  return true;
1183  } else {
1184  return checkChild(id(), newChild->id(), document()->inStrictMode());
1185  }
1186 }
1187 
1188 bool ElementImpl::childTypeAllowed(unsigned short type)
1189 {
1190  switch (type) {
1191  case Node::ELEMENT_NODE:
1192  case Node::TEXT_NODE:
1193  case Node::COMMENT_NODE:
1194  case Node::PROCESSING_INSTRUCTION_NODE:
1195  case Node::CDATA_SECTION_NODE:
1196  case Node::ENTITY_REFERENCE_NODE:
1197  return true;
1198  break;
1199  default:
1200  return false;
1201  }
1202 }
1203 
1204 void ElementImpl::scrollIntoView(bool /*alignToTop*/)
1205 {
1206  // ###
1207  qCWarning(KHTML_LOG) << "non-standard scrollIntoView() not implemented";
1208 }
1209 
1210 void ElementImpl::createNonCSSDecl()
1211 {
1212  assert(!m_hasCombinedStyle);
1213  CSSInlineStyleDeclarationImpl *ild = m_style.inlineDecls;
1214  m_style.combinedDecls = new CombinedStyleDecl;
1215  m_style.combinedDecls->inlineDecls = ild;
1216  CSSStyleDeclarationImpl *ncd = new CSSStyleDeclarationImpl(nullptr);
1217  m_style.combinedDecls->nonCSSDecls = ncd;
1218  ncd->ref();
1219  ncd->setParent(document()->elementSheet());
1220  ncd->setNode(this);
1221  ncd->setStrictParsing(false);
1222  m_hasCombinedStyle = true;
1223 }
1224 
1225 CSSInlineStyleDeclarationImpl *ElementImpl::getInlineStyleDecls()
1226 {
1227  if (!inlineStyleDecls()) {
1228  createInlineDecl();
1229  }
1230  return inlineStyleDecls();
1231 }
1232 
1233 void ElementImpl::createInlineDecl()
1234 {
1235  assert(!m_style.inlineDecls || (m_hasCombinedStyle && !m_style.combinedDecls->inlineDecls));
1236 
1237  CSSInlineStyleDeclarationImpl *dcl = new CSSInlineStyleDeclarationImpl(nullptr);
1238  dcl->ref();
1239  dcl->setParent(document()->elementSheet());
1240  dcl->setNode(this);
1241  dcl->setStrictParsing(!document()->inCompatMode());
1242  if (m_hasCombinedStyle) {
1243  m_style.combinedDecls->inlineDecls = dcl;
1244  } else {
1245  m_style.inlineDecls = dcl;
1246  }
1247 }
1248 
1249 void ElementImpl::dispatchAttrRemovalEvent(NodeImpl::Id /*id*/, DOMStringImpl * /*value*/)
1250 {
1251  // ### enable this stuff again
1252  if (!document()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER)) {
1253  return;
1254  }
1255  //int exceptioncode = 0;
1256  //dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(),
1257  //attr->value(), document()->attrName(attr->id()),MutationEvent::REMOVAL),exceptioncode);
1258 }
1259 
1260 void ElementImpl::dispatchAttrAdditionEvent(NodeImpl::Id /*id*/, DOMStringImpl * /*value*/)
1261 {
1262  // ### enable this stuff again
1263  if (!document()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER)) {
1264  return;
1265  }
1266  //int exceptioncode = 0;
1267  //dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(),
1268  //attr->value(),document()->attrName(attr->id()),MutationEvent::ADDITION),exceptioncode);
1269 }
1270 
1271 void ElementImpl::updateId(DOMStringImpl *oldId, DOMStringImpl *newId)
1272 {
1273  if (!inDocument()) {
1274  return;
1275  }
1276 
1277  if (oldId && oldId->l) {
1278  removeId(DOMString(oldId));
1279  }
1280 
1281  if (newId && newId->l) {
1282  addId(DOMString(newId));
1283  }
1284 }
1285 
1286 void ElementImpl::removeId(const DOMString &id)
1287 {
1288  document()->getElementByIdCache().remove(id, this);
1289 }
1290 
1291 void ElementImpl::addId(const DOMString &id)
1292 {
1293  document()->getElementByIdCache().add(id, this);
1294 }
1295 
1296 void ElementImpl::insertedIntoDocument()
1297 {
1298  // need to do superclass processing first so inDocument() is true
1299  // by the time we reach updateId
1300  NodeBaseImpl::insertedIntoDocument();
1301 
1302  if (hasID()) {
1303  DOMString id = getAttribute(ATTR_ID);
1304  updateId(nullptr, id.implementation());
1305  }
1306 }
1307 
1308 void ElementImpl::removedFromDocument()
1309 {
1310  if (hasID()) {
1311  DOMString id = getAttribute(ATTR_ID);
1312  updateId(id.implementation(), nullptr);
1313  }
1314 
1315  NodeBaseImpl::removedFromDocument();
1316 }
1317 
1318 DOMString ElementImpl::openTagStartToString(bool expandurls) const
1319 {
1320  DOMString result = DOMString("<") + nonCaseFoldedTagName();
1321 
1322  NamedAttrMapImpl *attrMap = attributes(true);
1323 
1324  if (attrMap) {
1325  unsigned numAttrs = attrMap->length();
1326  for (unsigned i = 0; i < numAttrs; i++) {
1327  result += " ";
1328 
1329  const AttributeImpl &attribute = attrMap->attributeAt(i);
1330  AttrImpl *attr = attribute.attr();
1331 
1332  if (attr) {
1333  result += attr->toString();
1334  } else {
1335  //FIXME: should use prefix too and depends on html/xhtml case
1336  PrefixName prefix = attribute.m_prefix;
1337  DOMString current;
1338  if (prefix.id()) {
1339  current = prefix.toString() + DOMString(":") + attribute.localName();
1340  } else {
1341  current = attribute.localName();
1342  }
1343  if (m_htmlCompat) {
1344  current = current.lower();
1345  }
1346  result += current;
1347  if (!attribute.value().isNull()) {
1348  result += "=\"";
1349  // FIXME: substitute entities for any instances of " or '
1350  // Expand out all urls, i.e. the src and href attributes
1351  if (expandurls && (attribute.id() == ATTR_SRC || attribute.id() == ATTR_HREF))
1352  if (document()) {
1353  //We need to sanitize the urls - strip out the passwords.
1354  //FIXME: are src= and href= the only places that might have a password and need to be sanitized?
1355  QUrl safeURL(document()->completeURL(attribute.value().string()));
1356  safeURL.setPassword(QString());
1357  result += safeURL.toDisplayString().toHtmlEscaped();
1358  } else {
1359  qCWarning(KHTML_LOG) << "document() returned false";
1360  result += attribute.value();
1361  }
1362  else {
1363  result += attribute.value();
1364  }
1365  result += "\"";
1366  }
1367  }
1368  }
1369  }
1370 
1371  return result;
1372 }
1373 DOMString ElementImpl::selectionToString(NodeImpl *selectionStart, NodeImpl *selectionEnd, int startOffset, int endOffset, bool &found) const
1374 {
1375  DOMString result = openTagStartToString();
1376 
1377  if (hasChildNodes()) {
1378  result += ">";
1379 
1380  for (NodeImpl *child = firstChild(); child != nullptr; child = child->nextSibling()) {
1381  result += child->selectionToString(selectionStart, selectionEnd, startOffset, endOffset, found); // this might set found to true
1382  if (child == selectionEnd) {
1383  found = true;
1384  }
1385  if (found) {
1386  break;
1387  }
1388  }
1389 
1390  result += "</";
1391  result += nonCaseFoldedTagName();
1392  result += ">";
1393  } else {
1394  result += " />";
1395  }
1396 
1397  return result;
1398 }
1399 
1400 DOMString ElementImpl::toString() const
1401 {
1402  QString result = openTagStartToString().string(); //Accumulate in QString, since DOMString can't append well.
1403 
1404  if (hasChildNodes()) {
1405  result += ">";
1406 
1407  for (NodeImpl *child = firstChild(); child != nullptr; child = child->nextSibling()) {
1408  DOMString kid = child->toString();
1409  result += QString::fromRawData(kid.unicode(), kid.length());
1410  }
1411 
1412  result += "</";
1413  result += nonCaseFoldedTagName().string();
1414  result += ">";
1415  } else if (result.length() == 1) {
1416  // ensure we do not get results like < /> can happen when serialize document
1417  result = "";
1418  } else {
1419  result += " />";
1420  }
1421 
1422  return result;
1423 }
1424 
1425 RenderStyle *ElementImpl::computedStyle()
1426 {
1427  if (m_render && m_render->style()) {
1428  return m_render->style();
1429  }
1430 
1431  if (!attached())
1432  // FIXME: Try to do better than this. Ensure that styleForElement() works for elements that are not in the
1433  // document tree and figure out when to destroy the computed style for such elements.
1434  {
1435  return nullptr;
1436  }
1437 
1438  ElementRareDataImpl *rd = createRareData();
1439  if (!rd->m_computedStyle) {
1440  rd->m_computedStyle = document()->styleSelector()->styleForElement(this, parent() ? parent()->computedStyle() : nullptr);
1441  rd->m_computedStyle->ref();
1442  }
1443  return rd->m_computedStyle;
1444 }
1445 
1446 // ElementTraversal API
1447 ElementImpl *ElementImpl::firstElementChild() const
1448 {
1449  NodeImpl *n = firstChild();
1450  while (n && !n->isElementNode()) {
1451  n = n->nextSibling();
1452  }
1453  return static_cast<ElementImpl *>(n);
1454 }
1455 
1456 ElementImpl *ElementImpl::lastElementChild() const
1457 {
1458  NodeImpl *n = lastChild();
1459  while (n && !n->isElementNode()) {
1460  n = n->previousSibling();
1461  }
1462  return static_cast<ElementImpl *>(n);
1463 }
1464 
1465 ElementImpl *ElementImpl::previousElementSibling() const
1466 {
1467  NodeImpl *n = previousSibling();
1468  while (n && !n->isElementNode()) {
1469  n = n->previousSibling();
1470  }
1471  return static_cast<ElementImpl *>(n);
1472 }
1473 
1474 ElementImpl *ElementImpl::nextElementSibling() const
1475 {
1476  NodeImpl *n = nextSibling();
1477  while (n && !n->isElementNode()) {
1478  n = n->nextSibling();
1479  }
1480  return static_cast<ElementImpl *>(n);
1481 }
1482 
1483 unsigned ElementImpl::childElementCount() const
1484 {
1485  unsigned count = 0;
1486  NodeImpl *n = firstChild();
1487  while (n) {
1488  count += n->isElementNode();
1489  n = n->nextSibling();
1490  }
1491  return count;
1492 }
1493 
1494 void ElementImpl::blur()
1495 {
1496  if (document()->focusNode() == this) {
1497  document()->setFocusNode(nullptr);
1498  }
1499 }
1500 
1501 void ElementImpl::focus()
1502 {
1503  document()->setFocusNode(this);
1504 }
1505 
1506 void ElementImpl::synchronizeStyleAttribute() const
1507 {
1508  assert(inlineStyleDecls() && m_needsStyleAttributeUpdate);
1509  m_needsStyleAttributeUpdate = false;
1510  DOMString value = inlineStyleDecls()->cssText();
1511  attributes()->setValueWithoutElementUpdate(ATTR_STYLE, value.implementation());
1512 }
1513 
1514 // -------------------------------------------------------------------------
1515 
1516 XMLElementImpl::XMLElementImpl(DocumentImpl *doc, NamespaceName namespacename, LocalName localName, PrefixName prefix)
1517  : ElementImpl(doc)
1518 {
1519  // Called from createElement(). In this case localName, prefix, and namespaceURI all need to be null.
1520  m_localName = localName;
1521  m_namespace = namespacename;
1522  m_prefix = prefix;
1523 }
1524 
1525 XMLElementImpl::~XMLElementImpl()
1526 {
1527 }
1528 
1529 WTF::PassRefPtr<NodeImpl> XMLElementImpl::cloneNode(bool deep)
1530 {
1531  WTF::RefPtr<ElementImpl> clone = new XMLElementImpl(docPtr(), NamespaceName::fromId(namespacePart(id())), LocalName::fromId(localNamePart(id())), m_prefix);
1532  finishCloneNode(clone.get(), deep);
1533  return clone;
1534 }
1535 
1536 void XMLElementImpl::parseAttribute(AttributeImpl *attr)
1537 {
1538  if (attr->id() == ATTR_ID) {
1539  setHasID();
1540  document()->incDOMTreeVersion(DocumentImpl::TV_IDNameHref);
1541  }
1542 
1543  // Note: we do not want to handle ATTR_CLASS here, since the
1544  // class concept applies only to specific languages, like
1545  // HTML and SVG, not generic XML.
1546 }
1547 
1548 // -------------------------------------------------------------------------
1549 
1550 NamedAttrMapImpl::NamedAttrMapImpl(ElementImpl *element)
1551  : m_element(element)
1552 {
1553 }
1554 
1555 NamedAttrMapImpl::~NamedAttrMapImpl()
1556 {
1557  unsigned len = m_attrs.size();
1558  for (unsigned i = 0; i < len; i++) {
1559  m_attrs[i].free();
1560  }
1561  m_attrs.clear();
1562 }
1563 
1564 NodeImpl *NamedAttrMapImpl::getNamedItem(NodeImpl::Id id, const PrefixName &prefix, bool nsAware)
1565 {
1566  if (!m_element) {
1567  return nullptr;
1568  }
1569 
1570  int index = find(id, prefix, nsAware);
1571  return (index < 0) ? nullptr : m_attrs[index].createAttr(m_element, m_element->docPtr());
1572 }
1573 
1574 Node NamedAttrMapImpl::removeNamedItem(NodeImpl::Id id, const PrefixName &prefix, bool nsAware, int &exceptioncode)
1575 {
1576  if (!m_element) {
1577  exceptioncode = DOMException::NOT_FOUND_ERR;
1578  return nullptr;
1579  }
1580 
1581  // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
1582  if (isReadOnly()) {
1583  exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
1584  return nullptr;
1585  }
1586  int index = find(id, prefix, nsAware);
1587  if (index < 0) {
1588  exceptioncode = DOMException::NOT_FOUND_ERR;
1589  return nullptr;
1590  }
1591 
1592  id = m_attrs[index].id();
1593  if (id == ATTR_ID) {
1594  m_element->updateId(m_attrs[index].val(), nullptr);
1595  }
1596  Node removed(m_attrs[index].createAttr(m_element, m_element->docPtr()));
1597  m_attrs[index].free(); // Also sets the remove'd ownerElement to 0
1598  m_attrs.remove(index);
1599  m_element->parseNullAttribute(id, prefix);
1600  m_element->attributeChanged(id);
1601  return removed;
1602 }
1603 
1604 Node NamedAttrMapImpl::setNamedItem(NodeImpl *arg, const PrefixName &prefix, bool nsAware, int &exceptioncode)
1605 {
1606  if (!m_element || !arg) {
1607  exceptioncode = DOMException::NOT_FOUND_ERR;
1608  return nullptr;
1609  }
1610 
1611  // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly.
1612  if (isReadOnly()) {
1613  exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
1614  return nullptr;
1615  }
1616 
1617  // WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
1618  if (arg->document() != m_element->document()) {
1619  exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
1620  return nullptr;
1621  }
1622 
1623  // HIERARCHY_REQUEST_ERR: Raised if an attempt is made to add a node doesn't belong in this NamedNodeMap
1624  if (!arg->isAttributeNode()) {
1625  exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
1626  return nullptr;
1627  }
1628  AttrImpl *attr = static_cast<AttrImpl *>(arg);
1629 
1630  // INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
1631  // The DOM user must explicitly clone Attr nodes to re-use them in other elements.
1632  if (attr->ownerElement() && attr->ownerElement() != m_element) {
1633  exceptioncode = DOMException::INUSE_ATTRIBUTE_ERR;
1634  return nullptr;
1635  }
1636 
1637  if (attr->ownerElement() == m_element) {
1638  // Already have this attribute.
1639  // DOMTS core-1 test "hc_elementreplaceattributewithself" says we should return it.
1640  return attr;
1641  }
1642 
1643  int index = find(attr->id(), prefix, nsAware);
1644  if (index >= 0) {
1645  if (attr->id() == ATTR_ID) {
1646  m_element->updateId(m_attrs[index].val(), attr->val());
1647  }
1648 
1649  Node replaced = m_attrs[index].createAttr(m_element, m_element->docPtr());
1650  m_attrs[index].free();
1651  m_attrs[index].m_localName = emptyLocalName; /* "has implementation" flag */
1652  m_attrs[index].m_data.attr = attr;
1653  m_attrs[index].m_data.attr->ref();
1654  attr->setElement(m_element);
1655  m_element->parseAttribute(&m_attrs[index]);
1656  m_element->attributeChanged(m_attrs[index].id());
1657  // ### dispatch mutation events
1658  return replaced;
1659  }
1660 
1661  // No existing attribute; add to list
1662  AttributeImpl attribute;
1663  attribute.m_localName = emptyLocalName; /* "has implementation" flag */
1664  attribute.m_namespace = NamespaceName::fromId(0);
1665  attribute.m_prefix = emptyPrefixName;
1666  attribute.m_data.attr = attr;
1667  attribute.m_data.attr->ref();
1668  m_attrs.append(attribute);
1669 
1670  attr->setElement(m_element);
1671  if (attr->id() == ATTR_ID) {
1672  m_element->updateId(nullptr, attr->val());
1673  }
1674  m_element->parseAttribute(&m_attrs.last());
1675  m_element->attributeChanged(m_attrs.last().id());
1676  // ### dispatch mutation events
1677 
1678  return nullptr;
1679 }
1680 
1681 NodeImpl *NamedAttrMapImpl::item(unsigned index)
1682 {
1683  return (index >= m_attrs.size()) ? nullptr : m_attrs[index].createAttr(m_element, m_element->docPtr());
1684 }
1685 
1686 NodeImpl::Id NamedAttrMapImpl::idAt(unsigned index) const
1687 {
1688  assert(index < m_attrs.size());
1689  return m_attrs[index].id();
1690 }
1691 
1692 DOMStringImpl *NamedAttrMapImpl::valueAt(unsigned index) const
1693 {
1694  assert(index < m_attrs.size());
1695  return m_attrs[index].val();
1696 }
1697 
1698 DOMStringImpl *NamedAttrMapImpl::getValue(NodeImpl::Id id, const PrefixName &prefix, bool nsAware) const
1699 {
1700  int index = find(id, prefix, nsAware);
1701  return index < 0 ? nullptr : m_attrs[index].val();
1702 }
1703 
1704 void NamedAttrMapImpl::setValue(NodeImpl::Id id, DOMStringImpl *value, const PrefixName &prefix, bool nsAware)
1705 {
1706  if (!id) {
1707  return;
1708  }
1709  // Passing in a null value here causes the attribute to be removed. This is a khtml extension
1710  // (the spec does not specify what to do in this situation).
1711  int exceptioncode = 0;
1712  if (!value) {
1713  removeNamedItem(id, prefix, nsAware, exceptioncode);
1714  return;
1715  }
1716  int index = find(id, prefix, nsAware);
1717  if (index >= 0) {
1718  if (m_attrs[index].attr()) {
1719  m_attrs[index].attr()->setPrefix(prefix.toString(), exceptioncode);
1720  } else {
1721  m_attrs[index].m_prefix = prefix;
1722  }
1723  m_attrs[index].setValue(value, m_element);
1724  // ### dispatch mutation events
1725  return;
1726  }
1727 
1728  AttributeImpl attr;
1729  attr.m_localName = LocalName::fromId(localNamePart(id));
1730  attr.m_namespace = NamespaceName::fromId(namespacePart(id));
1731  attr.m_prefix = prefix;
1732  attr.m_data.value = value;
1733  attr.m_data.value->ref();
1734  m_attrs.append(attr);
1735 
1736  if (m_element) {
1737  if (id == ATTR_ID) {
1738  m_element->updateId(nullptr, value);
1739  }
1740  m_element->parseAttribute(&m_attrs.last());
1741  m_element->attributeChanged(m_attrs.last().id());
1742  }
1743  // ### dispatch mutation events
1744 }
1745 
1746 Attr NamedAttrMapImpl::removeAttr(AttrImpl *attr)
1747 {
1748  unsigned len = m_attrs.size();
1749  for (unsigned i = 0; i < len; i++) {
1750  if (m_attrs[i].attr() == attr) {
1751  NodeImpl::Id id = m_attrs[i].id();
1752  if (id == ATTR_ID) {
1753  m_element->updateId(attr->val(), nullptr);
1754  }
1755  Node removed(m_attrs[i].createAttr(m_element, m_element->docPtr()));
1756  m_attrs[i].free();
1757  m_attrs.remove(i);
1758  m_element->parseNullAttribute(id, PrefixName::fromString(attr->prefix()));
1759  m_element->attributeChanged(id);
1760  // ### dispatch mutation events
1761  return removed;
1762  }
1763  }
1764 
1765  return nullptr;
1766 }
1767 
1768 void NamedAttrMapImpl::copyAttributes(NamedAttrMapImpl *other)
1769 {
1770  assert(m_element);
1771  unsigned i;
1772  unsigned len = m_attrs.size();
1773  for (i = 0; i < len; i++) {
1774  if (m_attrs[i].id() == ATTR_ID) {
1775  m_element->updateId(m_attrs[i].val(), nullptr);
1776  }
1777  m_attrs[i].free();
1778  }
1779  m_attrs.resize(other->length());
1780  len = m_attrs.size();
1781  for (i = 0; i < len; i++) {
1782  m_attrs[i].m_localName = other->m_attrs[i].m_localName;
1783  m_attrs[i].m_prefix = other->m_attrs[i].m_prefix;
1784  m_attrs[i].m_namespace = other->m_attrs[i].m_namespace;
1785  if (m_attrs[i].m_localName.id()) {
1786  m_attrs[i].m_data.value = other->m_attrs[i].m_data.value;
1787  m_attrs[i].m_data.value->ref();
1788  } else {
1789  WTF::RefPtr<NodeImpl> clonedAttr = other->m_attrs[i].m_data.attr->cloneNode(true);
1790  m_attrs[i].m_data.attr = static_cast<AttrImpl *>(clonedAttr.get());
1791  m_attrs[i].m_data.attr->ref();
1792  m_attrs[i].m_data.attr->setElement(m_element);
1793  }
1794  if (m_attrs[i].id() == ATTR_ID) {
1795  m_element->updateId(nullptr, m_attrs[i].val());
1796  }
1797  m_element->parseAttribute(&m_attrs[i]);
1798  m_element->attributeChanged(m_attrs[i].id());
1799  }
1800 }
1801 
1802 int NamedAttrMapImpl::find(NodeImpl::Id id, const PrefixName &prefix, bool nsAware) const
1803 {
1804  //qCDebug(KHTML_LOG) << "In find:" << getPrintableName(id) << "[" << prefix.toString() << prefix.id() << "]" << nsAware;
1805  //qCDebug(KHTML_LOG) << "m_attrs.size()" << m_attrs.size();
1806  unsigned len = m_attrs.size();
1807  for (unsigned i = 0; i < len; ++i) {
1808  //qCDebug(KHTML_LOG) << "check attr[" << i << "]" << getPrintableName(m_attrs[i].id()) << "prefix:" << m_attrs[i].prefix();
1809  if (nsAware && namespacePart(id) == anyNamespace && localNamePart(id) == localNamePart(m_attrs[i].id())) {
1810  return i;
1811  }
1812  if ((nsAware && id == m_attrs[i].id()) || (!nsAware && localNamePart(id) == localNamePart(m_attrs[i].id()) && prefix == m_attrs[i].prefixName())) {
1813  //qCDebug(KHTML_LOG) << "attribute matched: exiting...";
1814  return i;
1815  }
1816  }
1817  //qCDebug(KHTML_LOG) << "attribute doesn't match: exiting... with -1";
1818  return -1;
1819 }
1820 
1821 void NamedAttrMapImpl::setElement(ElementImpl *element)
1822 {
1823  assert(!m_element);
1824  m_element = element;
1825 
1826  unsigned len = m_attrs.size();
1827  for (unsigned i = 0; i < len; i++)
1828  if (m_attrs[i].attr()) {
1829  m_attrs[i].attr()->setElement(element);
1830  }
1831 }
1832 
1833 void NamedAttrMapImpl::detachFromElement()
1834 {
1835  // This makes the map invalid; nothing can really be done with it now since it's not
1836  // associated with an element. But we have to keep it around in memory in case there
1837  // are still references to it.
1838  m_element = nullptr;
1839  unsigned len = m_attrs.size();
1840  for (unsigned i = 0; i < len; i++) {
1841  m_attrs[i].free();
1842  }
1843  m_attrs.clear();
1844 }
1845 
1846 void NamedAttrMapImpl::setClass(const DOMString &string)
1847 {
1848  if (!m_element) {
1849  return;
1850  }
1851 
1852  if (!m_element->hasClass()) {
1853  m_classNames.clear();
1854  return;
1855  }
1856 
1857  m_classNames.parseClassAttribute(string, m_element->document()->htmlMode() != DocumentImpl::XHtml &&
1858  m_element->document()->inCompatMode());
1859 }
1860 
1861 void NamedAttrMapImpl::setValueWithoutElementUpdate(NodeImpl::Id id, DOMStringImpl *value)
1862 {
1863  // FIXME properly fix case value == 0
1864  int index = find(id, emptyPrefixName, true);
1865  if (index >= 0) {
1866  m_attrs[index].rewriteValue(value ? value : DOMStringImpl::empty());
1867  return;
1868  }
1869 
1870  if (!value) {
1871  return;
1872  }
1873 
1874  AttributeImpl attr;
1875  attr.m_localName = LocalName::fromId(localNamePart(id));
1876  attr.m_namespace = NamespaceName::fromId(namespacePart(id));
1877  attr.m_prefix = emptyPrefixName;
1878  attr.m_data.value = value;
1879  attr.m_data.value->ref();
1880  m_attrs.append(attr);
1881 }
1882 
1883 }
DOMString lower() const
Returns a lowercase version of the string.
Definition: dom_string.cpp:232
DOMString tagName() const
The name of the element.
Node lastChild() const
The last child of this node.
Definition: dom_node.cpp:274
The Node interface is the primary datatype for the entire Document Object Model.
Definition: dom_node.h:278
QString name(const QVariant &location)
DOMString nodeName() const
The name of this node, depending on its type; see the table above.
Definition: dom_node.cpp:210
Node removeNamedItem(const DOMString &name)
Removes a node specified by name.
Definition: dom_node.cpp:98
DOMString prefix() const
Introduced in DOM Level 2.
Definition: dom_node.cpp:413
CSSStyleDeclaration style()
Introduced in DOM Level 2 This method is from the CSSStyleDeclaration interface.
Node previousSibling() const
The node immediately preceding this node.
Definition: dom_node.cpp:282
Node appendChild(const Node &newChild)
Adds the node newChild to the end of the list of children of this node.
Definition: dom_node.cpp:354
This file is part of the HTML rendering engine for KDE.
This class is khtml&#39;s main class.
Definition: khtml_part.h:208
StandardShortcut find(const QKeySequence &keySeq)
NamedNodeMap attributes() const
A NamedNodeMap containing the attributes of this node (if it is an Element ) or null otherwise...
Definition: dom_node.cpp:298
bool hasChildNodes()
This is a convenience method to allow easy determination of whether a node has any children...
Definition: dom_node.cpp:375
unsigned long index() const
Definition: dom_node.cpp:517
void removeAttribute(const DOMString &name)
Removes an attribute by name.
void setAttribute(const DOMString &name, const DOMString &value)
Adds a new attribute.
QString fromRawData(const QChar *unicode, int size)
Node parentNode() const
The parent of this node.
Definition: dom_node.cpp:250
DOM::Editor * editor() const
Returns the instance of the attached html editor interface.
The Attr interface represents an attribute in an Element object.
Definition: dom_element.h:89
Text createTextNode(const DOMString &data)
Creates a Text node given the specified string.
Definition: dom_doc.cpp:287
DOMString localName() const
Introduced in DOM Level 2.
Definition: dom_node.cpp:433
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
DOMString nodeValue() const
The value of this node, depending on its type; see the table above.
Definition: dom_node.cpp:218
Document ownerDocument() const
The Document object associated with this node.
Definition: dom_node.cpp:306
Node firstChild() const
The first child of this node.
Definition: dom_node.cpp:266
This library provides a full-featured HTML parser and widget.
Node getNamedItem(const DOMString &name) const
Retrieves a node specified by name.
Definition: dom_node.cpp:76
bool isEditable() const
Returns true if the document is editable, false otherwise.
DOMString getAttribute(const DOMString &name)
Retrieves an attribute value by name.
Base Class for all rendering tree objects.
bool isCaretMode() const
Returns whether caret mode is on/off.
DOMStringImpl * implementation() const
Definition: dom_string.h:145
DOMString namespaceURI() const
Introduced in DOM Level 2.
Definition: dom_node.cpp:405
Node nextSibling() const
The node immediately following this node.
Definition: dom_node.cpp:290
Node setNamedItem(const Node &arg)
Adds a node using its nodeName attribute.
Definition: dom_node.cpp:84
DOMString upper() const
Returns an uppercase version of the string.
Definition: dom_string.cpp:240
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:00 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.