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