Reviewed by Chris Blumenberg.
[WebKit-https.git] / WebCore / khtml / xml / dom_elementimpl.cpp
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  * Copyright (C) 2004 Apple Computer, Inc.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  */
25
26 //#define EVENT_DEBUG
27 #include "dom/dom_exception.h"
28 #include "dom/dom_node.h"
29 #include "xml/dom_textimpl.h"
30 #include "xml/dom_docimpl.h"
31 #include "xml/dom2_eventsimpl.h"
32 #include "xml/dom_elementimpl.h"
33
34 #include "khtml_part.h"
35
36 #include "html/dtd.h"
37 #include "html/htmlparser.h"
38
39 #include "rendering/render_canvas.h"
40 #include "misc/htmlhashes.h"
41 #include "css/css_valueimpl.h"
42 #include "css/css_stylesheetimpl.h"
43 #include "css/cssstyleselector.h"
44 #include "xml/dom_xmlimpl.h"
45
46 #include <qtextstream.h>
47 #include <kdebug.h>
48
49 using namespace DOM;
50 using namespace khtml;
51
52 AttributeImpl* AttributeImpl::clone(bool) const
53 {
54     AttributeImpl* result = new AttributeImpl(m_id, _value);
55     result->setPrefix(_prefix);
56     return result;
57 }
58
59 void AttributeImpl::allocateImpl(ElementImpl* e) {
60     _impl = new AttrImpl(e, e->docPtr(), this);
61 }
62
63 AttrImpl::AttrImpl(ElementImpl* element, DocumentPtr* docPtr, AttributeImpl* a)
64     : NodeBaseImpl(docPtr),
65       m_element(element),
66       m_attribute(a)
67 {
68     assert(!m_attribute->_impl);
69     m_attribute->_impl = this;
70     m_attribute->ref();
71     m_specified = true;
72 }
73
74 AttrImpl::~AttrImpl()
75 {
76     assert(m_attribute->_impl == this);
77     m_attribute->_impl = 0;
78     m_attribute->deref();
79 }
80
81 DOMString AttrImpl::nodeName() const
82 {
83     return getDocument()->attrName(m_attribute->id());
84 }
85
86 unsigned short AttrImpl::nodeType() const
87 {
88     return Node::ATTRIBUTE_NODE;
89 }
90
91 DOMString AttrImpl::prefix() const
92 {
93     return m_attribute->prefix();
94 }
95
96 void AttrImpl::setPrefix(const DOMString &_prefix, int &exceptioncode )
97 {
98     checkSetPrefix(_prefix, exceptioncode);
99     if (exceptioncode)
100         return;
101
102     m_attribute->setPrefix(_prefix.implementation());
103 }
104
105 DOMString AttrImpl::nodeValue() const {
106     return m_attribute->value();
107 }
108
109 void AttrImpl::setValue( const DOMString &v, int &exceptioncode )
110 {
111     exceptioncode = 0;
112
113     // ### according to the DOM docs, we should create an unparsed Text child
114     // node here
115     // do not interprete entities in the string, its literal!
116
117     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
118     if (isReadOnly()) {
119         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
120         return;
121     }
122
123     // ### what to do on 0 ?
124     if (v.isNull()) {
125         exceptioncode = DOMException::DOMSTRING_SIZE_ERR;
126         return;
127     }
128
129     m_attribute->setValue(v.implementation());
130     if (m_element)
131         m_element->attributeChanged(m_attribute);
132 }
133
134 void AttrImpl::setNodeValue( const DOMString &v, int &exceptioncode )
135 {
136     exceptioncode = 0;
137     // NO_MODIFICATION_ALLOWED_ERR: taken care of by setValue()
138     setValue(v, exceptioncode);
139 }
140
141 NodeImpl *AttrImpl::cloneNode ( bool /*deep*/)
142 {
143     return new AttrImpl(0, docPtr(), m_attribute->clone());
144 }
145
146 // DOM Section 1.1.1
147 bool AttrImpl::childAllowed( NodeImpl *newChild )
148 {
149     if(!newChild)
150         return false;
151
152     return childTypeAllowed(newChild->nodeType());
153 }
154
155 bool AttrImpl::childTypeAllowed( unsigned short type )
156 {
157     switch (type) {
158         case Node::TEXT_NODE:
159         case Node::ENTITY_REFERENCE_NODE:
160             return true;
161             break;
162         default:
163             return false;
164     }
165 }
166
167 DOMString AttrImpl::toString() const
168 {
169     DOMString result;
170
171     result += nodeName();
172
173     // FIXME: substitute entities for any instances of " or ' --
174     // maybe easier to just use text value and ignore existing
175     // entity refs?
176
177     if (firstChild() != NULL) {
178         result += "=\"";
179
180         for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
181             result += child->toString();
182         }
183         
184         result += "\"";
185     }
186
187     return result;
188 }
189
190 // -------------------------------------------------------------------------
191
192 ElementImpl::ElementImpl(DocumentPtr *doc)
193     : NodeBaseImpl(doc)
194 {
195     namedAttrMap = 0;
196     m_prefix = 0;
197 }
198
199 ElementImpl::~ElementImpl()
200 {
201     if (namedAttrMap) {
202         namedAttrMap->detachFromElement();
203         namedAttrMap->deref();
204     }
205
206     if (m_prefix)
207         m_prefix->deref();
208 }
209
210 void ElementImpl::removeAttribute( NodeImpl::Id id, int &exceptioncode )
211 {
212     if (namedAttrMap) {
213         namedAttrMap->removeNamedItem(id, exceptioncode);
214         if (exceptioncode == DOMException::NOT_FOUND_ERR) {
215             exceptioncode = 0;
216         }
217     }
218 }
219
220 void ElementImpl::setAttribute(NodeImpl::Id id, const DOMString &value)
221 {
222     int exceptioncode = 0;
223     setAttribute(id,value.implementation(),exceptioncode);
224 }
225
226 NamedAttrMapImpl* ElementImpl::attributes(bool readonly) const
227 {
228     updateStyleAttributeIfNeeded();
229
230     if (!readonly && !namedAttrMap) createAttributeMap();
231     return namedAttrMap;
232 }
233
234 unsigned short ElementImpl::nodeType() const
235 {
236     return Node::ELEMENT_NODE;
237 }
238
239 const AtomicStringList* ElementImpl::getClassList() const
240 {
241     return 0;
242 }
243
244 const AtomicString& ElementImpl::getIDAttribute() const
245 {
246     return namedAttrMap ? namedAttrMap->id() : nullAtom;
247 }
248
249 const AtomicString& ElementImpl::getAttribute(NodeImpl::Id id) const
250 {
251     if (id == ATTR_STYLE)
252         updateStyleAttributeIfNeeded();
253
254     if (namedAttrMap) {
255         AttributeImpl* a = namedAttrMap->getAttributeItem(id);
256         if (a) return a->value();
257     }
258     return nullAtom;
259 }
260
261 const AtomicString& ElementImpl::getAttributeNS(const DOMString &namespaceURI,
262                                                 const DOMString &localName) const
263 {   
264     NodeImpl::Id id = getDocument()->attrId(namespaceURI.implementation(),
265                                             localName.implementation(), true);
266     if (!id) return nullAtom;
267     return getAttribute(id);
268 }
269
270 void ElementImpl::setAttribute(NodeImpl::Id id, DOMStringImpl* value, int &exceptioncode )
271 {
272     if (inDocument())
273         getDocument()->incDOMTreeVersion();
274
275     // allocate attributemap if necessary
276     AttributeImpl* old = attributes(false)->getAttributeItem(id);
277
278     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
279     if (namedAttrMap->isReadOnly()) {
280         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
281         return;
282     }
283
284     if (id == ATTR_ID) {
285         updateId(old ? old->value() : nullAtom, value);
286     }
287     
288     if (old && !value)
289         namedAttrMap->removeAttribute(id);
290     else if (!old && value)
291         namedAttrMap->addAttribute(createAttribute(id, value));
292     else if (old && value) {
293         old->setValue(value);
294         attributeChanged(old);
295     }
296 }
297
298 AttributeImpl* ElementImpl::createAttribute(NodeImpl::Id id, DOMStringImpl* value)
299 {
300     return new AttributeImpl(id, value);
301 }
302
303 void ElementImpl::setAttributeMap( NamedAttrMapImpl* list )
304 {
305     if (inDocument())
306         getDocument()->incDOMTreeVersion();
307
308     // If setting the whole map changes the id attribute, we need to
309     // call updateId.
310
311     AttributeImpl *oldId = namedAttrMap ? namedAttrMap->getAttributeItem(ATTR_ID) : 0;
312     AttributeImpl *newId = list ? list->getAttributeItem(ATTR_ID) : 0;
313
314     if (oldId || newId) {
315         updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom);
316     }
317
318     if(namedAttrMap)
319         namedAttrMap->deref();
320
321     namedAttrMap = list;
322
323     if(namedAttrMap) {
324         namedAttrMap->ref();
325         namedAttrMap->element = this;
326         unsigned int len = namedAttrMap->length();
327         for(unsigned int i = 0; i < len; i++)
328             attributeChanged(namedAttrMap->attrs[i]);
329     }
330 }
331
332 bool ElementImpl::hasAttributes() const
333 {
334     updateStyleAttributeIfNeeded();
335
336     return namedAttrMap && namedAttrMap->length() > 0;
337 }
338
339 DOMString ElementImpl::nodeName() const
340 {
341     return tagName();
342 }
343
344 DOMString ElementImpl::tagName() const
345 {
346     DOMString tn = getDocument()->tagName(id());
347
348     if (m_prefix)
349         return DOMString(m_prefix) + ":" + tn;
350
351     return tn;
352 }
353
354 void ElementImpl::setPrefix( const DOMString &_prefix, int &exceptioncode )
355 {
356     checkSetPrefix(_prefix, exceptioncode);
357     if (exceptioncode)
358         return;
359
360     if (m_prefix)
361         m_prefix->deref();
362     m_prefix = _prefix.implementation();
363     if (m_prefix)
364         m_prefix->ref();
365 }
366
367 void ElementImpl::createAttributeMap() const
368 {
369     namedAttrMap = new NamedAttrMapImpl(const_cast<ElementImpl*>(this));
370     namedAttrMap->ref();
371 }
372
373 bool ElementImpl::isURLAttribute(AttributeImpl *attr) const
374 {
375     return false;
376     
377 }
378
379 RenderStyle *ElementImpl::styleForRenderer(RenderObject *parentRenderer)
380 {
381     return getDocument()->styleSelector()->styleForElement(this);
382 }
383
384 RenderObject *ElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
385 {
386     if (getDocument()->documentElement() == this && style->display() == NONE) {
387         // Ignore display: none on root elements.  Force a display of block in that case.
388         RenderBlock* result = new (arena) RenderBlock(this);
389         if (result) result->setStyle(style);
390         return result;
391     }
392     return RenderObject::createObject(this, style);
393 }
394
395
396 void ElementImpl::insertedIntoDocument()
397 {
398     // need to do superclass processing first so inDocument() is true
399     // by the time we reach updateId
400     NodeBaseImpl::insertedIntoDocument();
401
402     if (hasID()) {
403         NamedAttrMapImpl *attrs = attributes(true);
404         if (attrs) {
405             AttributeImpl *idAttr = attrs->getAttributeItem(ATTR_ID);
406             if (idAttr && !idAttr->isNull()) {
407                 updateId(nullAtom, idAttr->value());
408             }
409         }
410     }
411 }
412
413 void ElementImpl::removedFromDocument()
414 {
415     if (hasID()) {
416         NamedAttrMapImpl *attrs = attributes(true);
417         if (attrs) {
418             AttributeImpl *idAttr = attrs->getAttributeItem(ATTR_ID);
419             if (idAttr && !idAttr->isNull()) {
420                 updateId(idAttr->value(), nullAtom);
421             }
422         }
423     }
424
425     NodeBaseImpl::removedFromDocument();
426 }
427
428 void ElementImpl::attach()
429 {
430 #if SPEED_DEBUG < 1
431     createRendererIfNeeded();
432 #endif
433     NodeBaseImpl::attach();
434 }
435
436 void ElementImpl::recalcStyle( StyleChange change )
437 {
438     // ### should go away and be done in renderobject
439     RenderStyle* _style = m_render ? m_render->style() : 0;
440     bool hasParentRenderer = parent() ? parent()->renderer() : false;
441     
442 #if 0
443     const char* debug;
444     switch(change) {
445     case NoChange: debug = "NoChange";
446         break;
447     case NoInherit: debug= "NoInherit";
448         break;
449     case Inherit: debug = "Inherit";
450         break;
451     case Force: debug = "Force";
452         break;
453     }
454     qDebug("recalcStyle(%d: %s)[%p: %s]", change, debug, this, tagName().string().latin1());
455 #endif
456     if ( hasParentRenderer && (change >= Inherit || changed()) ) {
457         RenderStyle *newStyle = getDocument()->styleSelector()->styleForElement(this);
458         newStyle->ref();
459         StyleChange ch = diff( _style, newStyle );
460         if (ch == Detach) {
461             if (attached()) detach();
462             // ### Suboptimal. Style gets calculated again.
463             attach();
464             // attach recalulates the style for all children. No need to do it twice.
465             setChanged( false );
466             setHasChangedChild( false );
467             newStyle->deref(getDocument()->renderArena());
468             return;
469         }
470         else if (ch != NoChange) {
471             if( m_render && newStyle ) {
472                 //qDebug("--> setting style on render element bgcolor=%s", newStyle->backgroundColor().name().latin1());
473                 m_render->setStyle(newStyle);
474             }
475         }
476         else if (changed() && m_render && newStyle && (getDocument()->usesSiblingRules() || getDocument()->usesDescendantRules())) {
477             // Although no change occurred, we use the new style so that the cousin style sharing code won't get
478             // fooled into believing this style is the same.  This is only necessary if the document actually uses
479             // sibling/descendant rules, since otherwise it isn't possible for ancestor styles to affect sharing of
480             // descendants.
481             m_render->setStyleInternal(newStyle);
482         }
483
484         newStyle->deref(getDocument()->renderArena());
485
486         if ( change != Force) {
487             if (getDocument()->usesDescendantRules())
488                 change = Force;
489             else
490                 change = ch;
491         }
492     }
493
494     NodeImpl *n;
495     for (n = _first; n; n = n->nextSibling()) {
496         //qDebug("    (%p) calling recalcStyle on child %s/%p, change=%d", this, n, n->isElementNode() ? ((ElementImpl *)n)->tagName().string().latin1() : n->isTextNode() ? "text" : "unknown", change );
497         if ( change >= Inherit || n->isTextNode() ||
498              n->hasChangedChild() || n->changed() )
499             n->recalcStyle( change );
500     }
501
502     setChanged( false );
503     setHasChangedChild( false );
504 }
505
506 // DOM Section 1.1.1
507 bool ElementImpl::childAllowed( NodeImpl *newChild )
508 {
509     if (!childTypeAllowed(newChild->nodeType()))
510         return false;
511
512     // For XML documents, we are non-validating and do not check against a DTD, even for HTML elements.
513     if (getDocument()->isHTMLDocument())
514         return checkChild(id(), newChild->id(), !getDocument()->inCompatMode());
515     return true;
516 }
517
518 bool ElementImpl::childTypeAllowed( unsigned short type )
519 {
520     switch (type) {
521         case Node::ELEMENT_NODE:
522         case Node::TEXT_NODE:
523         case Node::COMMENT_NODE:
524         case Node::PROCESSING_INSTRUCTION_NODE:
525         case Node::CDATA_SECTION_NODE:
526         case Node::ENTITY_REFERENCE_NODE:
527             return true;
528             break;
529         default:
530             return false;
531     }
532 }
533
534 void ElementImpl::dispatchAttrRemovalEvent(AttributeImpl *attr)
535 {
536     if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
537         return;
538     //int exceptioncode = 0;
539 //     dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(),
540 //                attr->value(), getDocument()->attrName(attr->id()),MutationEvent::REMOVAL),exceptioncode);
541 }
542
543 void ElementImpl::dispatchAttrAdditionEvent(AttributeImpl *attr)
544 {
545     if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
546         return;
547    // int exceptioncode = 0;
548 //     dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(),
549 //                                         attr->value(),getDocument()->attrName(attr->id()),MutationEvent::ADDITION),exceptioncode);
550 }
551
552 DOMString ElementImpl::openTagStartToString() const
553 {
554     DOMString result = DOMString("<") + tagName();
555
556     NamedAttrMapImpl *attrMap = attributes(true);
557
558     if (attrMap) {
559         unsigned long numAttrs = attrMap->length();
560         for (unsigned long i = 0; i < numAttrs; i++) {
561             result += " ";
562
563             AttributeImpl *attribute = attrMap->attributeItem(i);
564             AttrImpl *attr = attribute->attrImpl();
565
566             if (attr) {
567                 result += attr->toString();
568             } else {
569                 result += getDocument()->attrName(attribute->id());
570                 if (!attribute->value().isNull()) {
571                     result += "=\"";
572                     // FIXME: substitute entities for any instances of " or '
573                     result += attribute->value();
574                     result += "\"";
575                 }
576             }
577         }
578     }
579
580     return result;
581 }
582
583 DOMString ElementImpl::toString() const
584 {
585     DOMString result = openTagStartToString();
586
587     if (hasChildNodes()) {
588         result += ">";
589
590         for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
591             result += child->toString();
592         }
593
594         result += "</";
595         result += tagName();
596         result += ">";
597     } else {
598         result += " />";
599     }
600
601     return result;
602 }
603
604 void ElementImpl::updateId(const AtomicString& oldId, const AtomicString& newId)
605 {
606     if (!inDocument())
607         return;
608
609     if (oldId == newId)
610         return;
611
612     DocumentImpl* doc = getDocument();
613     if (!oldId.isEmpty())
614         doc->removeElementById(oldId, this);
615     if (!newId.isEmpty())
616         doc->addElementById(newId, this);
617 }
618
619 #ifndef NDEBUG
620 void ElementImpl::dump(QTextStream *stream, QString ind) const
621 {
622     updateStyleAttributeIfNeeded();
623     if (namedAttrMap) {
624         for (uint i = 0; i < namedAttrMap->length(); i++) {
625             AttributeImpl *attr = namedAttrMap->attributeItem(i);
626             *stream << " " << DOMString(getDocument()->attrName(attr->id())).string().ascii()
627                     << "=\"" << DOMString(attr->value()).string().ascii() << "\"";
628         }
629     }
630
631     NodeBaseImpl::dump(stream,ind);
632 }
633 #endif
634
635 #ifndef NDEBUG
636 void ElementImpl::formatForDebugger(char *buffer, unsigned length) const
637 {
638     DOMString result;
639     DOMString s;
640     
641     s = nodeName();
642     if (s.length() > 0) {
643         result += s;
644     }
645           
646     s = getAttribute(ATTR_ID);
647     if (s.length() > 0) {
648         if (result.length() > 0)
649             result += "; ";
650         result += "id=";
651         result += s;
652     }
653           
654     s = getAttribute(ATTR_CLASS);
655     if (s.length() > 0) {
656         if (result.length() > 0)
657             result += "; ";
658         result += "class=";
659         result += s;
660     }
661           
662     strncpy(buffer, result.string().latin1(), length - 1);
663 }
664 #endif
665
666 // -------------------------------------------------------------------------
667
668 XMLElementImpl::XMLElementImpl(DocumentPtr *doc, DOMStringImpl *_tagName)
669     : ElementImpl(doc)
670 {
671     m_id = doc->document()->tagId(0 /* no namespace */, _tagName,  false /* allocate */);
672 }
673
674 XMLElementImpl::XMLElementImpl(DocumentPtr *doc, DOMStringImpl *_qualifiedName, DOMStringImpl *_namespaceURI)
675     : ElementImpl(doc)
676 {
677     int colonpos = -1;
678     for (uint i = 0; i < _qualifiedName->l; ++i)
679         if (_qualifiedName->s[i] == ':') {
680             colonpos = i;
681             break;
682         }
683
684     if (colonpos >= 0) {
685         // we have a prefix
686         DOMStringImpl* localName = _qualifiedName->copy();
687         localName->ref();
688         localName->remove(0,colonpos+1);
689         m_id = doc->document()->tagId(_namespaceURI, localName, false /* allocate */);
690         localName->deref();
691         m_prefix = _qualifiedName->copy();
692         m_prefix->ref();
693         m_prefix->truncate(colonpos);
694     }
695     else {
696         // no prefix
697         m_id = doc->document()->tagId(_namespaceURI, _qualifiedName, false /* allocate */);
698         m_prefix = 0;
699     }
700 }
701
702 XMLElementImpl::~XMLElementImpl()
703 {
704 }
705
706 DOMString XMLElementImpl::localName() const
707 {
708     return getDocument()->tagName(m_id);
709 }
710
711 DOMString XMLElementImpl::namespaceURI() const
712 {
713     return getDocument()->namespaceURI(m_id);
714 }
715
716 NodeImpl *XMLElementImpl::cloneNode ( bool deep )
717 {
718     // ### we loose namespace here FIXME
719     // should pass id around
720     XMLElementImpl *clone = new XMLElementImpl(docPtr(), getDocument()->tagName(m_id).implementation());
721     clone->m_id = m_id;
722
723     // clone attributes
724     if (namedAttrMap)
725         *clone->attributes() = *namedAttrMap;
726
727     if (deep)
728         cloneChildNodes(clone);
729
730     return clone;
731 }
732
733 // -------------------------------------------------------------------------
734
735 NamedAttrMapImpl::NamedAttrMapImpl(ElementImpl *e)
736     : element(e)
737     , attrs(0)
738     , len(0)
739 {
740 }
741
742 NamedAttrMapImpl::~NamedAttrMapImpl()
743 {
744     NamedAttrMapImpl::clearAttributes(); // virtual method, so qualify just to be explicit
745 }
746
747 bool NamedAttrMapImpl::isHTMLAttributeMap() const
748 {
749     return false;
750 }
751
752 AttrImpl *NamedAttrMapImpl::getNamedItem ( NodeImpl::Id id ) const
753 {
754     AttributeImpl* a = getAttributeItem(id);
755     if (!a) return 0;
756
757     if (!a->attrImpl())
758         a->allocateImpl(element);
759
760     return a->attrImpl();
761 }
762
763 Node NamedAttrMapImpl::setNamedItem ( NodeImpl* arg, int &exceptioncode )
764 {
765     if (!element) {
766         exceptioncode = DOMException::NOT_FOUND_ERR;
767         return 0;
768     }
769
770     // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly.
771     if (isReadOnly()) {
772         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
773         return 0;
774     }
775
776     // WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
777     if (arg->getDocument() != element->getDocument()) {
778         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
779         return 0;
780     }
781
782     // Not mentioned in spec: throw a HIERARCHY_REQUEST_ERROR if the user passes in a non-attribute node
783     if (!arg->isAttributeNode()) {
784         exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
785         return 0;
786     }
787     AttrImpl *attr = static_cast<AttrImpl*>(arg);
788
789     AttributeImpl* a = attr->attrImpl();
790     AttributeImpl* old = getAttributeItem(a->id());
791     if (old == a) return arg; // we know about it already
792
793     // INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
794     // The DOM user must explicitly clone Attr nodes to re-use them in other elements.
795     if (attr->ownerElement()) {
796         exceptioncode = DOMException::INUSE_ATTRIBUTE_ERR;
797         return 0;
798     }
799
800     if (a->id() == ATTR_ID) {
801         element->updateId(old ? old->value() : nullAtom, a->value());
802     }
803
804     // ### slightly inefficient - resizes attribute array twice.
805     Node r;
806     if (old) {
807         if (!old->attrImpl())
808             old->allocateImpl(element);
809         r = old->_impl;
810         removeAttribute(a->id());
811     }
812
813     addAttribute(a);
814     return r;
815 }
816
817 // The DOM2 spec doesn't say that removeAttribute[NS] throws NOT_FOUND_ERR
818 // if the attribute is not found, but at this level we have to throw NOT_FOUND_ERR
819 // because of removeNamedItem, removeNamedItemNS, and removeAttributeNode.
820 Node NamedAttrMapImpl::removeNamedItem ( NodeImpl::Id id, int &exceptioncode )
821 {
822     // ### should this really be raised when the attribute to remove isn't there at all?
823     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
824     if (isReadOnly()) {
825         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
826         return Node();
827     }
828
829     AttributeImpl* a = getAttributeItem(id);
830     if (!a) {
831         exceptioncode = DOMException::NOT_FOUND_ERR;
832         return Node();
833     }
834
835     if (!a->attrImpl())  a->allocateImpl(element);
836     Node r(a->attrImpl());
837
838     if (id == ATTR_ID) {
839         element->updateId(a->value(), nullAtom);
840     }
841
842     removeAttribute(id);
843     return r;
844 }
845
846 AttrImpl *NamedAttrMapImpl::item ( unsigned long index ) const
847 {
848     if (index >= len)
849         return 0;
850
851     if (!attrs[index]->attrImpl())
852         attrs[index]->allocateImpl(element);
853
854     return attrs[index]->attrImpl();
855 }
856
857 AttributeImpl* NamedAttrMapImpl::getAttributeItem(NodeImpl::Id id) const
858 {
859     bool matchAnyNamespace = (namespacePart(id) == anyNamespace);
860     for (unsigned long i = 0; i < len; ++i) {
861         if (attrs[i]->id() == id)
862             return attrs[i];
863         else if (matchAnyNamespace) {
864             if (localNamePart(attrs[i]->id()) == localNamePart(id))
865                 return attrs[i];
866         }
867     }
868     return 0;
869 }
870
871 NodeImpl::Id NamedAttrMapImpl::mapId(const DOMString& namespaceURI,
872                                      const DOMString& localName, bool readonly)
873 {
874     assert(element);
875     if (!element) return 0;
876     return element->getDocument()->attrId(namespaceURI.implementation(),
877                                             localName.implementation(), readonly);
878 }
879
880 void NamedAttrMapImpl::clearAttributes()
881 {
882     if (attrs) {
883         uint i;
884         for (i = 0; i < len; i++) {
885             if (attrs[i]->_impl)
886                 attrs[i]->_impl->m_element = 0;
887             attrs[i]->deref();
888         }
889         main_thread_free(attrs);
890         attrs = 0;
891     }
892     len = 0;
893 }
894
895 void NamedAttrMapImpl::detachFromElement()
896 {
897     // we allow a NamedAttrMapImpl w/o an element in case someone still has a reference
898     // to if after the element gets deleted - but the map is now invalid
899     element = 0;
900     clearAttributes();
901 }
902
903 NamedAttrMapImpl& NamedAttrMapImpl::operator=(const NamedAttrMapImpl& other)
904 {
905     // clone all attributes in the other map, but attach to our element
906     if (!element) return *this;
907
908     // If assigning the map changes the id attribute, we need to call
909     // updateId.
910
911     AttributeImpl *oldId = getAttributeItem(ATTR_ID);
912     AttributeImpl *newId = other.getAttributeItem(ATTR_ID);
913
914     if (oldId || newId) {
915         element->updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom);
916     }
917
918     clearAttributes();
919     len = other.len;
920     attrs = static_cast<AttributeImpl **>(main_thread_malloc(len * sizeof(AttributeImpl *)));
921
922     // first initialize attrs vector, then call attributeChanged on it
923     // this allows attributeChanged to use getAttribute
924     for (uint i = 0; i < len; i++) {
925         attrs[i] = other.attrs[i]->clone();
926         attrs[i]->ref();
927     }
928
929     // FIXME: This is wasteful.  The class list could be preserved on a copy, and we
930     // wouldn't have to waste time reparsing the attribute.
931     // The derived class, HTMLNamedAttrMapImpl, which manages a parsed class list for the CLASS attribute,
932     // will update its member variable when parse attribute is called.
933     for(uint i = 0; i < len; i++)
934         element->attributeChanged(attrs[i], true);
935
936     return *this;
937 }
938
939 void NamedAttrMapImpl::addAttribute(AttributeImpl *attr)
940 {
941     // Add the attribute to the list
942     AttributeImpl **newAttrs = static_cast<AttributeImpl **>(main_thread_malloc((len + 1) * sizeof(AttributeImpl *)));
943     if (attrs) {
944       for (uint i = 0; i < len; i++)
945         newAttrs[i] = attrs[i];
946       main_thread_free(attrs);
947     }
948     attrs = newAttrs;
949     attrs[len++] = attr;
950     attr->ref();
951
952     AttrImpl * const attrImpl = attr->_impl;
953     if (attrImpl)
954         attrImpl->m_element = element;
955
956     // Notify the element that the attribute has been added, and dispatch appropriate mutation events
957     // Note that element may be null here if we are called from insertAttr() during parsing
958     if (element) {
959         element->attributeChanged(attr);
960         element->dispatchAttrAdditionEvent(attr);
961         element->dispatchSubtreeModifiedEvent(false);
962     }
963 }
964
965 void NamedAttrMapImpl::removeAttribute(NodeImpl::Id id)
966 {
967     unsigned long index = len+1;
968     for (unsigned long i = 0; i < len; ++i)
969         if (attrs[i]->id() == id) {
970             index = i;
971             break;
972         }
973
974     if (index >= len) return;
975
976     // Remove the attribute from the list
977     AttributeImpl* attr = attrs[index];
978     if (attrs[index]->_impl)
979         attrs[index]->_impl->m_element = 0;
980     if (len == 1) {
981         main_thread_free(attrs);
982         attrs = 0;
983         len = 0;
984     }
985     else {
986         AttributeImpl **newAttrs = static_cast<AttributeImpl **>(main_thread_malloc((len - 1) * sizeof(AttributeImpl *)));
987         uint i;
988         for (i = 0; i < uint(index); i++)
989             newAttrs[i] = attrs[i];
990         len--;
991         for (; i < len; i++)
992             newAttrs[i] = attrs[i+1];
993         main_thread_free(attrs);
994         attrs = newAttrs;
995     }
996
997     // Notify the element that the attribute has been removed
998     // dispatch appropriate mutation events
999     if (element && !attr->_value.isNull()) {
1000         AtomicString value = attr->_value;
1001         attr->_value = nullAtom;
1002         element->attributeChanged(attr);
1003         attr->_value = value;
1004     }
1005     if (element) {
1006         element->dispatchAttrRemovalEvent(attr);
1007         element->dispatchSubtreeModifiedEvent(false);
1008     }
1009     attr->deref();
1010 }
1011