33b988a3694eba2d10b68f2cad5c837b7cf0a64a
[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 NodeImpl *ElementImpl::cloneNode(bool deep)
340 {
341     // ### we lose the namespace here ... FIXME
342     int exceptioncode;
343     ElementImpl *clone = getDocument()->createElement(tagName(), exceptioncode);
344     if (!clone) return 0;
345
346     // clone attributes
347     if (namedAttrMap)
348         *clone->attributes() = *namedAttrMap;
349
350     if (deep)
351         cloneChildNodes(clone);
352     return clone;
353 }
354
355 DOMString ElementImpl::nodeName() const
356 {
357     return tagName();
358 }
359
360 DOMString ElementImpl::tagName() const
361 {
362     DOMString tn = getDocument()->tagName(id());
363
364     if (m_prefix)
365         return DOMString(m_prefix) + ":" + tn;
366
367     return tn;
368 }
369
370 void ElementImpl::setPrefix( const DOMString &_prefix, int &exceptioncode )
371 {
372     checkSetPrefix(_prefix, exceptioncode);
373     if (exceptioncode)
374         return;
375
376     if (m_prefix)
377         m_prefix->deref();
378     m_prefix = _prefix.implementation();
379     if (m_prefix)
380         m_prefix->ref();
381 }
382
383 void ElementImpl::createAttributeMap() const
384 {
385     namedAttrMap = new NamedAttrMapImpl(const_cast<ElementImpl*>(this));
386     namedAttrMap->ref();
387 }
388
389 bool ElementImpl::isURLAttribute(AttributeImpl *attr) const
390 {
391     return false;
392     
393 }
394
395 RenderStyle *ElementImpl::styleForRenderer(RenderObject *parentRenderer)
396 {
397     return getDocument()->styleSelector()->styleForElement(this);
398 }
399
400 RenderObject *ElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
401 {
402     if (getDocument()->documentElement() == this && style->display() == NONE) {
403         // Ignore display: none on root elements.  Force a display of block in that case.
404         RenderBlock* result = new (arena) RenderBlock(this);
405         if (result) result->setStyle(style);
406         return result;
407     }
408     return RenderObject::createObject(this, style);
409 }
410
411 void ElementImpl::attach()
412 {
413 #if SPEED_DEBUG < 1
414     createRendererIfNeeded();
415 #endif
416     NodeBaseImpl::attach();
417
418     if (hasID()) {
419         NamedAttrMapImpl *attrs = attributes(true);
420         if (attrs) {
421             AttributeImpl *idAttr = attrs->getAttributeItem(ATTR_ID);
422             if (idAttr && !idAttr->isNull()) {
423                 updateId(nullAtom, idAttr->value());
424             }
425         }
426     }
427 }
428
429 void ElementImpl::detach()
430 {
431     if (hasID()) {
432         NamedAttrMapImpl *attrs = attributes(true);
433         if (attrs) {
434             AttributeImpl *idAttr = attrs->getAttributeItem(ATTR_ID);
435             if (idAttr && !idAttr->isNull()) {
436                 updateId(idAttr->value(), nullAtom);
437             }
438         }
439     }
440
441     NodeBaseImpl::detach();
442 }
443
444 void ElementImpl::recalcStyle( StyleChange change )
445 {
446     // ### should go away and be done in renderobject
447     RenderStyle* _style = m_render ? m_render->style() : 0;
448     bool hasParentRenderer = parent() ? parent()->renderer() : false;
449     
450 #if 0
451     const char* debug;
452     switch(change) {
453     case NoChange: debug = "NoChange";
454         break;
455     case NoInherit: debug= "NoInherit";
456         break;
457     case Inherit: debug = "Inherit";
458         break;
459     case Force: debug = "Force";
460         break;
461     }
462     qDebug("recalcStyle(%d: %s)[%p: %s]", change, debug, this, tagName().string().latin1());
463 #endif
464     if ( hasParentRenderer && (change >= Inherit || changed()) ) {
465         RenderStyle *newStyle = getDocument()->styleSelector()->styleForElement(this);
466         newStyle->ref();
467         StyleChange ch = diff( _style, newStyle );
468         if (ch == Detach) {
469             if (attached()) detach();
470             // ### Suboptimal. Style gets calculated again.
471             attach();
472             // attach recalulates the style for all children. No need to do it twice.
473             setChanged( false );
474             setHasChangedChild( false );
475             newStyle->deref(getDocument()->renderArena());
476             return;
477         }
478         else if (ch != NoChange) {
479             if( m_render && newStyle ) {
480                 //qDebug("--> setting style on render element bgcolor=%s", newStyle->backgroundColor().name().latin1());
481                 m_render->setStyle(newStyle);
482             }
483         }
484         else if (changed() && m_render && newStyle && (getDocument()->usesSiblingRules() || getDocument()->usesDescendantRules())) {
485             // Although no change occurred, we use the new style so that the cousin style sharing code won't get
486             // fooled into believing this style is the same.  This is only necessary if the document actually uses
487             // sibling/descendant rules, since otherwise it isn't possible for ancestor styles to affect sharing of
488             // descendants.
489             m_render->setStyleInternal(newStyle);
490         }
491
492         newStyle->deref(getDocument()->renderArena());
493
494         if ( change != Force) {
495             if (getDocument()->usesDescendantRules())
496                 change = Force;
497             else
498                 change = ch;
499         }
500     }
501
502     NodeImpl *n;
503     for (n = _first; n; n = n->nextSibling()) {
504         //qDebug("    (%p) calling recalcStyle on child %s/%p, change=%d", this, n, n->isElementNode() ? ((ElementImpl *)n)->tagName().string().latin1() : n->isTextNode() ? "text" : "unknown", change );
505         if ( change >= Inherit || n->isTextNode() ||
506              n->hasChangedChild() || n->changed() )
507             n->recalcStyle( change );
508     }
509
510     setChanged( false );
511     setHasChangedChild( false );
512 }
513
514 // DOM Section 1.1.1
515 bool ElementImpl::childAllowed( NodeImpl *newChild )
516 {
517     if (!childTypeAllowed(newChild->nodeType()))
518         return false;
519
520     // For XML documents, we are non-validating and do not check against a DTD, even for HTML elements.
521     if (getDocument()->isHTMLDocument())
522         return checkChild(id(), newChild->id());
523     return true;
524 }
525
526 bool ElementImpl::childTypeAllowed( unsigned short type )
527 {
528     switch (type) {
529         case Node::ELEMENT_NODE:
530         case Node::TEXT_NODE:
531         case Node::COMMENT_NODE:
532         case Node::PROCESSING_INSTRUCTION_NODE:
533         case Node::CDATA_SECTION_NODE:
534         case Node::ENTITY_REFERENCE_NODE:
535             return true;
536             break;
537         default:
538             return false;
539     }
540 }
541
542 void ElementImpl::dispatchAttrRemovalEvent(AttributeImpl *attr)
543 {
544     if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
545         return;
546     //int exceptioncode = 0;
547 //     dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(),
548 //                attr->value(), getDocument()->attrName(attr->id()),MutationEvent::REMOVAL),exceptioncode);
549 }
550
551 void ElementImpl::dispatchAttrAdditionEvent(AttributeImpl *attr)
552 {
553     if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
554         return;
555    // int exceptioncode = 0;
556 //     dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(),
557 //                                         attr->value(),getDocument()->attrName(attr->id()),MutationEvent::ADDITION),exceptioncode);
558 }
559
560 DOMString ElementImpl::openTagStartToString() const
561 {
562     DOMString result = DOMString("<") + tagName();
563
564     NamedAttrMapImpl *attrMap = attributes(true);
565
566     if (attrMap) {
567         unsigned long numAttrs = attrMap->length();
568         for (unsigned long i = 0; i < numAttrs; i++) {
569             result += " ";
570
571             AttributeImpl *attribute = attrMap->attributeItem(i);
572             AttrImpl *attr = attribute->attrImpl();
573
574             if (attr) {
575                 result += attr->toString();
576             } else {
577                 result += getDocument()->attrName(attribute->id());
578                 if (!attribute->value().isNull()) {
579                     result += "=\"";
580                     // FIXME: substitute entities for any instances of " or '
581                     result += attribute->value();
582                     result += "\"";
583                 }
584             }
585         }
586     }
587
588     return result;
589 }
590
591 DOMString ElementImpl::toString() const
592 {
593     DOMString result = openTagStartToString();
594
595     if (hasChildNodes()) {
596         result += ">";
597
598         for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
599             result += child->toString();
600         }
601
602         result += "</";
603         result += tagName();
604         result += ">";
605     } else {
606         result += " />";
607     }
608
609     return result;
610 }
611
612 void ElementImpl::updateId(const AtomicString& oldId, const AtomicString& newId)
613 {
614     if (!attached())
615         return;
616
617     if (oldId == newId)
618         return;
619
620     DocumentImpl* doc = getDocument();
621     if (!oldId.isEmpty())
622         doc->removeElementById(oldId, this);
623     if (!newId.isEmpty())
624         doc->addElementById(newId, this);
625 }
626
627 #ifndef NDEBUG
628 void ElementImpl::dump(QTextStream *stream, QString ind) const
629 {
630     updateStyleAttributeIfNeeded();
631     if (namedAttrMap) {
632         for (uint i = 0; i < namedAttrMap->length(); i++) {
633             AttributeImpl *attr = namedAttrMap->attributeItem(i);
634             *stream << " " << DOMString(getDocument()->attrName(attr->id())).string().ascii()
635                     << "=\"" << DOMString(attr->value()).string().ascii() << "\"";
636         }
637     }
638
639     NodeBaseImpl::dump(stream,ind);
640 }
641 #endif
642
643 #ifndef NDEBUG
644 void ElementImpl::formatForDebugger(char *buffer, unsigned length) const
645 {
646     DOMString result;
647     DOMString s;
648     
649     s = nodeName();
650     if (s.length() > 0) {
651         result += s;
652     }
653           
654     s = getAttribute(ATTR_ID);
655     if (s.length() > 0) {
656         if (result.length() > 0)
657             result += "; ";
658         result += "id=";
659         result += s;
660     }
661           
662     s = getAttribute(ATTR_CLASS);
663     if (s.length() > 0) {
664         if (result.length() > 0)
665             result += "; ";
666         result += "class=";
667         result += s;
668     }
669           
670     strncpy(buffer, result.string().latin1(), length - 1);
671 }
672 #endif
673
674 // -------------------------------------------------------------------------
675
676 XMLElementImpl::XMLElementImpl(DocumentPtr *doc, DOMStringImpl *_tagName)
677     : ElementImpl(doc)
678 {
679     m_id = doc->document()->tagId(0 /* no namespace */, _tagName,  false /* allocate */);
680 }
681
682 XMLElementImpl::XMLElementImpl(DocumentPtr *doc, DOMStringImpl *_qualifiedName, DOMStringImpl *_namespaceURI)
683     : ElementImpl(doc)
684 {
685     int colonpos = -1;
686     for (uint i = 0; i < _qualifiedName->l; ++i)
687         if (_qualifiedName->s[i] == ':') {
688             colonpos = i;
689             break;
690         }
691
692     if (colonpos >= 0) {
693         // we have a prefix
694         DOMStringImpl* localName = _qualifiedName->copy();
695         localName->ref();
696         localName->remove(0,colonpos+1);
697         m_id = doc->document()->tagId(_namespaceURI, localName, false /* allocate */);
698         localName->deref();
699         m_prefix = _qualifiedName->copy();
700         m_prefix->ref();
701         m_prefix->truncate(colonpos);
702     }
703     else {
704         // no prefix
705         m_id = doc->document()->tagId(_namespaceURI, _qualifiedName, false /* allocate */);
706         m_prefix = 0;
707     }
708 }
709
710 XMLElementImpl::~XMLElementImpl()
711 {
712 }
713
714 DOMString XMLElementImpl::localName() const
715 {
716     return getDocument()->tagName(m_id);
717 }
718
719
720 NodeImpl *XMLElementImpl::cloneNode ( bool deep )
721 {
722     // ### we loose namespace here FIXME
723     // should pass id around
724     XMLElementImpl *clone = new XMLElementImpl(docPtr(), getDocument()->tagName(m_id).implementation());
725     clone->m_id = m_id;
726
727     // clone attributes
728     if (namedAttrMap)
729         *clone->attributes() = *namedAttrMap;
730
731     if (deep)
732         cloneChildNodes(clone);
733
734     return clone;
735 }
736
737 // -------------------------------------------------------------------------
738
739 NamedAttrMapImpl::NamedAttrMapImpl(ElementImpl *e)
740     : element(e)
741 {
742     attrs = 0;
743     len = 0;
744 }
745
746 NamedAttrMapImpl::~NamedAttrMapImpl()
747 {
748     NamedAttrMapImpl::clearAttributes(); // virtual method, so qualify just to be explicit
749 }
750
751 bool NamedAttrMapImpl::isHTMLAttributeMap() const
752 {
753     return false;
754 }
755
756 AttrImpl *NamedAttrMapImpl::getNamedItem ( NodeImpl::Id id ) const
757 {
758     AttributeImpl* a = getAttributeItem(id);
759     if (!a) return 0;
760
761     if (!a->attrImpl())
762         a->allocateImpl(element);
763
764     return a->attrImpl();
765 }
766
767 Node NamedAttrMapImpl::setNamedItem ( NodeImpl* arg, int &exceptioncode )
768 {
769     if (!element) {
770         exceptioncode = DOMException::NOT_FOUND_ERR;
771         return 0;
772     }
773
774     // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly.
775     if (isReadOnly()) {
776         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
777         return 0;
778     }
779
780     // WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
781     if (arg->getDocument() != element->getDocument()) {
782         exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
783         return 0;
784     }
785
786     // Not mentioned in spec: throw a HIERARCHY_REQUEST_ERROR if the user passes in a non-attribute node
787     if (!arg->isAttributeNode()) {
788         exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
789         return 0;
790     }
791     AttrImpl *attr = static_cast<AttrImpl*>(arg);
792
793     AttributeImpl* a = attr->attrImpl();
794     AttributeImpl* old = getAttributeItem(a->id());
795     if (old == a) return arg; // we know about it already
796
797     // INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
798     // The DOM user must explicitly clone Attr nodes to re-use them in other elements.
799     if (attr->ownerElement()) {
800         exceptioncode = DOMException::INUSE_ATTRIBUTE_ERR;
801         return 0;
802     }
803
804     if (a->id() == ATTR_ID) {
805         element->updateId(old ? old->value() : nullAtom, a->value());
806     }
807
808     // ### slightly inefficient - resizes attribute array twice.
809     Node r;
810     if (old) {
811         if (!old->attrImpl())
812             old->allocateImpl(element);
813         r = old->_impl;
814         removeAttribute(a->id());
815     }
816
817     addAttribute(a);
818     return r;
819 }
820
821 // The DOM2 spec doesn't say that removeAttribute[NS] throws NOT_FOUND_ERR
822 // if the attribute is not found, but at this level we have to throw NOT_FOUND_ERR
823 // because of removeNamedItem, removeNamedItemNS, and removeAttributeNode.
824 Node NamedAttrMapImpl::removeNamedItem ( NodeImpl::Id id, int &exceptioncode )
825 {
826     // ### should this really be raised when the attribute to remove isn't there at all?
827     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
828     if (isReadOnly()) {
829         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
830         return Node();
831     }
832
833     AttributeImpl* a = getAttributeItem(id);
834     if (!a) {
835         exceptioncode = DOMException::NOT_FOUND_ERR;
836         return Node();
837     }
838
839     if (!a->attrImpl())  a->allocateImpl(element);
840     Node r(a->attrImpl());
841
842     if (id == ATTR_ID) {
843         element->updateId(a->value(), nullAtom);
844     }
845
846     removeAttribute(id);
847     return r;
848 }
849
850 AttrImpl *NamedAttrMapImpl::item ( unsigned long index ) const
851 {
852     if (index >= len)
853         return 0;
854
855     if (!attrs[index]->attrImpl())
856         attrs[index]->allocateImpl(element);
857
858     return attrs[index]->attrImpl();
859 }
860
861 AttributeImpl* NamedAttrMapImpl::getAttributeItem(NodeImpl::Id id) const
862 {
863     bool matchAnyNamespace = (namespacePart(id) == anyNamespace);
864     for (unsigned long i = 0; i < len; ++i) {
865         if (attrs[i]->id() == id)
866             return attrs[i];
867         else if (matchAnyNamespace) {
868             if (localNamePart(attrs[i]->id()) == localNamePart(id))
869                 return attrs[i];
870         }
871     }
872     return 0;
873 }
874
875 NodeImpl::Id NamedAttrMapImpl::mapId(const DOMString& namespaceURI,
876                                      const DOMString& localName, bool readonly)
877 {
878     assert(element);
879     if (!element) return 0;
880     return element->getDocument()->attrId(namespaceURI.implementation(),
881                                             localName.implementation(), readonly);
882 }
883
884 void NamedAttrMapImpl::clearAttributes()
885 {
886     if (attrs) {
887         uint i;
888         for (i = 0; i < len; i++) {
889             if (attrs[i]->_impl)
890                 attrs[i]->_impl->m_element = 0;
891             attrs[i]->deref();
892         }
893         delete [] attrs;
894         attrs = 0;
895     }
896     len = 0;
897 }
898
899 void NamedAttrMapImpl::detachFromElement()
900 {
901     // we allow a NamedAttrMapImpl w/o an element in case someone still has a reference
902     // to if after the element gets deleted - but the map is now invalid
903     element = 0;
904     clearAttributes();
905 }
906
907 NamedAttrMapImpl& NamedAttrMapImpl::operator=(const NamedAttrMapImpl& other)
908 {
909     // clone all attributes in the other map, but attach to our element
910     if (!element) return *this;
911
912     // If assigning the map changes the id attribute, we need to call
913     // updateId.
914
915     AttributeImpl *oldId = getAttributeItem(ATTR_ID);
916     AttributeImpl *newId = other.getAttributeItem(ATTR_ID);
917
918     if (oldId || newId) {
919         element->updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom);
920     }
921
922     clearAttributes();
923     len = other.len;
924     attrs = new AttributeImpl* [len];
925
926     // first initialize attrs vector, then call attributeChanged on it
927     // this allows attributeChanged to use getAttribute
928     for (uint i = 0; i < len; i++) {
929         attrs[i] = other.attrs[i]->clone();
930         attrs[i]->ref();
931     }
932
933     // FIXME: This is wasteful.  The class list could be preserved on a copy, and we
934     // wouldn't have to waste time reparsing the attribute.
935     // The derived class, HTMLNamedAttrMapImpl, which manages a parsed class list for the CLASS attribute,
936     // will update its member variable when parse attribute is called.
937     for(uint i = 0; i < len; i++)
938         element->attributeChanged(attrs[i], true);
939
940     return *this;
941 }
942
943 void NamedAttrMapImpl::addAttribute(AttributeImpl *attr)
944 {
945     // Add the attribute to the list
946     AttributeImpl **newAttrs = new AttributeImpl* [len+1];
947     if (attrs) {
948       for (uint i = 0; i < len; i++)
949         newAttrs[i] = attrs[i];
950       delete [] attrs;
951     }
952     attrs = newAttrs;
953     attrs[len++] = attr;
954     attr->ref();
955
956     AttrImpl * const attrImpl = attr->_impl;
957     if (attrImpl)
958         attrImpl->m_element = element;
959
960     // Notify the element that the attribute has been added, and dispatch appropriate mutation events
961     // Note that element may be null here if we are called from insertAttr() during parsing
962     if (element) {
963         element->attributeChanged(attr);
964         element->dispatchAttrAdditionEvent(attr);
965         element->dispatchSubtreeModifiedEvent(false);
966     }
967 }
968
969 void NamedAttrMapImpl::removeAttribute(NodeImpl::Id id)
970 {
971     unsigned long index = len+1;
972     for (unsigned long i = 0; i < len; ++i)
973         if (attrs[i]->id() == id) {
974             index = i;
975             break;
976         }
977
978     if (index >= len) return;
979
980     // Remove the attribute from the list
981     AttributeImpl* attr = attrs[index];
982     if (attrs[index]->_impl)
983         attrs[index]->_impl->m_element = 0;
984     if (len == 1) {
985         delete [] attrs;
986         attrs = 0;
987         len = 0;
988     }
989     else {
990         AttributeImpl **newAttrs = new AttributeImpl* [len-1];
991         uint i;
992         for (i = 0; i < uint(index); i++)
993             newAttrs[i] = attrs[i];
994         len--;
995         for (; i < len; i++)
996             newAttrs[i] = attrs[i+1];
997         delete [] attrs;
998         attrs = newAttrs;
999     }
1000
1001     // Notify the element that the attribute has been removed
1002     // dispatch appropriate mutation events
1003     if (element && !attr->_value.isNull()) {
1004         AtomicString value = attr->_value;
1005         attr->_value = nullAtom;
1006         element->attributeChanged(attr);
1007         attr->_value = value;
1008     }
1009     if (element) {
1010         element->dispatchAttrRemovalEvent(attr);
1011         element->dispatchSubtreeModifiedEvent(false);
1012     }
1013     attr->deref();
1014 }
1015