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