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