WebCore:
[WebKit-https.git] / WebCore / html / HTMLElement.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  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "HTMLElement.h"
27
28 #include "CSSPropertyNames.h"
29 #include "CSSValueKeywords.h"
30 #include "DocumentFragment.h"
31 #include "Event.h"
32 #include "EventListener.h"
33 #include "EventNames.h"
34 #include "ExceptionCode.h"
35 #include "Frame.h"
36 #include "HTMLBRElement.h"
37 #include "HTMLDocument.h"
38 #include "HTMLElementFactory.h"
39 #include "HTMLNames.h"
40 #include "HTMLTokenizer.h"
41 #include "TextIterator.h"
42 #include "markup.h"
43
44 namespace WebCore {
45
46 using namespace EventNames;
47 using namespace HTMLNames;
48
49 HTMLElement::HTMLElement(const QualifiedName& tagName, Document *doc)
50     : StyledElement(tagName, doc)
51 {
52 }
53
54 HTMLElement::~HTMLElement()
55 {
56 }
57
58 String HTMLElement::nodeName() const
59 {
60     // FIXME: Would be nice to have an atomicstring lookup based off uppercase chars that does not have to copy
61     // the string on a hit in the hash.
62     if (document()->isHTMLDocument())
63         return m_tagName.localName().impl()->upper();
64     return Element::nodeName();
65 }
66     
67 HTMLTagStatus HTMLElement::endTagRequirement() const
68 {
69     if (hasLocalName(dtTag) || hasLocalName(ddTag))
70         return TagStatusOptional;
71
72     // Same values as <span>.  This way custom tag name elements will behave like inline spans.
73     return TagStatusRequired;
74 }
75
76 int HTMLElement::tagPriority() const
77 {
78     if (hasLocalName(addressTag) || hasLocalName(ddTag) || hasLocalName(dtTag) || hasLocalName(noscriptTag))
79         return 3;
80     if (hasLocalName(centerTag) || hasLocalName(nobrTag))
81         return 5;
82     if (hasLocalName(noembedTag) || hasLocalName(noframesTag))
83         return 10;
84
85     // Same values as <span>.  This way custom tag name elements will behave like inline spans.
86     return 1;
87 }
88
89 PassRefPtr<Node> HTMLElement::cloneNode(bool deep)
90 {
91     RefPtr<HTMLElement> clone = HTMLElementFactory::createHTMLElement(m_tagName.localName(), document(), 0, false);
92     if (!clone)
93         return 0;
94
95     if (namedAttrMap)
96         *clone->attributes() = *namedAttrMap;
97
98     if (m_inlineStyleDecl)
99         *clone->getInlineStyleDecl() = *m_inlineStyleDecl;
100
101     clone->copyNonAttributeProperties(this);
102
103     if (deep)
104         cloneChildNodes(clone.get());
105
106     return clone.release();
107 }
108
109 bool HTMLElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
110 {
111     if (attrName == alignAttr ||
112         attrName == contenteditableAttr) {
113         result = eUniversal;
114         return false;
115     }
116     if (attrName == dirAttr) {
117         result = hasTagName(bdoTag) ? eBDO : eUniversal;
118         return false;
119     }
120
121     return StyledElement::mapToEntry(attrName, result);
122 }
123     
124 void HTMLElement::parseMappedAttribute(MappedAttribute *attr)
125 {
126     if (attr->name() == idAttr || attr->name() == classAttr || attr->name() == styleAttr)
127         return StyledElement::parseMappedAttribute(attr);
128
129     String indexstring;
130     if (attr->name() == alignAttr) {
131         if (equalIgnoringCase(attr->value(), "middle"))
132             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, "center");
133         else
134             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, attr->value());
135     } else if (attr->name() == contenteditableAttr) {
136         setContentEditable(attr);
137     } else if (attr->name() == tabindexAttr) {
138         indexstring = getAttribute(tabindexAttr);
139         if (indexstring.length())
140             setTabIndex(indexstring.toInt());
141     } else if (attr->name() == langAttr) {
142         // FIXME: Implement
143     } else if (attr->name() == dirAttr) {
144         addCSSProperty(attr, CSS_PROP_DIRECTION, attr->value());
145         addCSSProperty(attr, CSS_PROP_UNICODE_BIDI, hasTagName(bdoTag) ? CSS_VAL_BIDI_OVERRIDE : CSS_VAL_EMBED);
146     }
147 // standard events
148     else if (attr->name() == onclickAttr) {
149         setHTMLEventListener(clickEvent, attr);
150     } else if (attr->name() == oncontextmenuAttr) {
151         setHTMLEventListener(contextmenuEvent, attr);
152     } else if (attr->name() == ondblclickAttr) {
153         setHTMLEventListener(dblclickEvent, attr);
154     } else if (attr->name() == onmousedownAttr) {
155         setHTMLEventListener(mousedownEvent, attr);
156     } else if (attr->name() == onmousemoveAttr) {
157         setHTMLEventListener(mousemoveEvent, attr);
158     } else if (attr->name() == onmouseoutAttr) {
159         setHTMLEventListener(mouseoutEvent, attr);
160     } else if (attr->name() == onmouseoverAttr) {
161         setHTMLEventListener(mouseoverEvent, attr);
162     } else if (attr->name() == onmouseupAttr) {
163         setHTMLEventListener(mouseupEvent, attr);
164     } else if (attr->name() == onmousewheelAttr) {
165         setHTMLEventListener(mousewheelEvent, attr);
166     } else if (attr->name() == onfocusAttr) {
167         setHTMLEventListener(focusEvent, attr);
168     } else if (attr->name() == onblurAttr) {
169         setHTMLEventListener(blurEvent, attr);
170     } else if (attr->name() == onkeydownAttr) {
171         setHTMLEventListener(keydownEvent, attr);
172     } else if (attr->name() == onkeypressAttr) {
173         setHTMLEventListener(keypressEvent, attr);
174     } else if (attr->name() == onkeyupAttr) {
175         setHTMLEventListener(keyupEvent, attr);
176     } else if (attr->name() == onscrollAttr) {
177         setHTMLEventListener(scrollEvent, attr);
178     } else if (attr->name() == onbeforecutAttr) {
179         setHTMLEventListener(beforecutEvent, attr);
180     } else if (attr->name() == oncutAttr) {
181         setHTMLEventListener(cutEvent, attr);
182     } else if (attr->name() == onbeforecopyAttr) {
183         setHTMLEventListener(beforecopyEvent, attr);
184     } else if (attr->name() == oncopyAttr) {
185         setHTMLEventListener(copyEvent, attr);
186     } else if (attr->name() == onbeforepasteAttr) {
187         setHTMLEventListener(beforepasteEvent, attr);
188     } else if (attr->name() == onpasteAttr) {
189         setHTMLEventListener(pasteEvent, attr);
190     } else if (attr->name() == ondragenterAttr) {
191         setHTMLEventListener(dragenterEvent, attr);
192     } else if (attr->name() == ondragoverAttr) {
193         setHTMLEventListener(dragoverEvent, attr);
194     } else if (attr->name() == ondragleaveAttr) {
195         setHTMLEventListener(dragleaveEvent, attr);
196     } else if (attr->name() == ondropAttr) {
197         setHTMLEventListener(dropEvent, attr);
198     } else if (attr->name() == ondragstartAttr) {
199         setHTMLEventListener(dragstartEvent, attr);
200     } else if (attr->name() == ondragAttr) {
201         setHTMLEventListener(dragEvent, attr);
202     } else if (attr->name() == ondragendAttr) {
203         setHTMLEventListener(dragendEvent, attr);
204     } else if (attr->name() == onselectstartAttr) {
205         setHTMLEventListener(selectstartEvent, attr);
206     } else if (attr->name() == onsubmitAttr) {
207         setHTMLEventListener(submitEvent, attr);
208     } else if (attr->name() == onerrorAttr) {
209         setHTMLEventListener(errorEvent, attr);
210     }
211 }
212
213 String HTMLElement::innerHTML() const
214 {
215     return createMarkup(this, ChildrenOnly);
216 }
217
218 String HTMLElement::outerHTML() const
219 {
220     return createMarkup(this);
221 }
222
223 String HTMLElement::innerText() const
224 {
225     if (!renderer())
226         return textContent(true);
227     
228     // We need to update layout, since plainText uses line boxes in the render tree.
229     document()->updateLayoutIgnorePendingStylesheets();
230     return plainText(rangeOfContents(const_cast<HTMLElement *>(this)).get());
231 }
232
233 String HTMLElement::outerText() const
234 {
235     // Getting outerText is the same as getting innerText, only
236     // setting is different. You would think this should get the plain
237     // text for the outer range, but this is wrong, <br> for instance
238     // would return different values for inner and outer text by such
239     // a rule, but it doesn't in WinIE, and we want to match that.
240     return innerText();
241 }
242
243 PassRefPtr<DocumentFragment> HTMLElement::createContextualFragment(const String &html)
244 {
245     // the following is in accordance with the definition as used by IE
246     if (endTagRequirement() == TagStatusForbidden)
247         return 0;
248
249     if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
250         hasLocalName(headTag) || hasLocalName(styleTag) || hasLocalName(titleTag))
251         return 0;
252
253     RefPtr<DocumentFragment> fragment = new DocumentFragment(document());
254     
255     if (document()->isHTMLDocument())
256          parseHTMLDocumentFragment(html, fragment.get());
257     else {
258         if (!parseXMLDocumentFragment(html, fragment.get(), this))
259             // FIXME: We should propagate a syntax error exception out here.
260             return 0;
261     }
262
263     // Exceptions are ignored because none ought to happen here.
264     int ignoredExceptionCode;
265
266     // we need to pop <html> and <body> elements and remove <head> to
267     // accommodate folks passing complete HTML documents to make the
268     // child of an element.
269
270     RefPtr<Node> nextNode;
271     for (RefPtr<Node> node = fragment->firstChild(); node; node = nextNode) {
272         nextNode = node->nextSibling();
273         if (node->hasTagName(htmlTag) || node->hasTagName(bodyTag)) {
274             Node *firstChild = node->firstChild();
275             if (firstChild)
276                 nextNode = firstChild;
277             RefPtr<Node> nextChild;
278             for (RefPtr<Node> child = firstChild; child; child = nextChild) {
279                 nextChild = child->nextSibling();
280                 node->removeChild(child.get(), ignoredExceptionCode);
281                 assert(!ignoredExceptionCode);
282                 fragment->insertBefore(child, node.get(), ignoredExceptionCode);
283                 assert(!ignoredExceptionCode);
284             }
285             fragment->removeChild(node.get(), ignoredExceptionCode);
286             assert(!ignoredExceptionCode);
287         } else if (node->hasTagName(headTag)) {
288             fragment->removeChild(node.get(), ignoredExceptionCode);
289             assert(!ignoredExceptionCode);
290         }
291     }
292
293     return fragment.release();
294 }
295
296 void HTMLElement::setInnerHTML(const String &html, ExceptionCode& ec)
297 {
298     RefPtr<DocumentFragment> fragment = createContextualFragment(html);
299     if (!fragment) {
300         ec = NO_MODIFICATION_ALLOWED_ERR;
301         return;
302     }
303
304     // FIXME: Add special case for when we had one text child and we just want to set its text?
305     // FIXME: Add special case for cases where we can use replaceChild?
306
307     removeChildren();
308     appendChild(fragment.release(), ec);
309 }
310
311 void HTMLElement::setOuterHTML(const String &html, ExceptionCode& ec)
312 {
313     Node* p = parent();
314     if (!p || !p->isHTMLElement()) {
315         ec = NO_MODIFICATION_ALLOWED_ERR;
316         return;
317     }
318
319     HTMLElement* parent = static_cast<HTMLElement*>(p);
320     RefPtr<DocumentFragment> fragment = parent->createContextualFragment(html);
321     if (!fragment) {
322         ec = NO_MODIFICATION_ALLOWED_ERR;
323         return;
324     }
325
326     // FIXME: Add special case for when we had one text child and we just want to set its text?
327     // FIXME: Why doesn't this have code to merge neighboring text nodes the way setOuterText does?
328     parent->replaceChild(fragment.release(), this, ec);
329 }
330
331 void HTMLElement::setInnerText(const String& text, ExceptionCode& ec)
332 {
333     // follow the IE specs about when this is allowed
334     if (endTagRequirement() == TagStatusForbidden) {
335         ec = NO_MODIFICATION_ALLOWED_ERR;
336         return;
337     }
338     if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
339         hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || 
340         hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) ||
341         hasLocalName(trTag)) {
342         ec = NO_MODIFICATION_ALLOWED_ERR;
343         return;
344     }
345
346     // FIXME: This doesn't take whitespace collapsing into account at all.
347     // FIXME: Add special case for when we had one text child and we just want to set its text?
348     // FIXME: Add special case for cases where we can use replaceChild?
349
350     removeChildren();
351
352     if (!text.contains('\n') && !text.contains('\r')) {
353         if (text.isEmpty())
354             return;
355         appendChild(new Text(document(), text), ec);
356         return;
357     }
358
359     // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
360     // FIXME: Can the renderer be out of date here? Do we need to call updateRendering?
361     // For example, for the contents of textarea elements that are display:none?
362     RenderObject* r = renderer();
363     if (r && r->style()->preserveNewline()) {
364         if (!text.contains('\r')) {
365             appendChild(new Text(document(), text), ec);
366             return;
367         }
368         // FIXME: Stick with String once it has a replace that can do this.
369         DeprecatedString textWithConsistentLineBreaks = text.deprecatedString();
370         textWithConsistentLineBreaks.replace("\r\n", "\n");
371         textWithConsistentLineBreaks.replace('\r', '\n');
372         appendChild(new Text(document(), textWithConsistentLineBreaks), ec);
373         return;
374     }
375
376     // Add text nodes and <br> elements.
377     ec = 0;
378     int lineStart = 0;
379     UChar prev = 0;
380     int length = text.length();
381     for (int i = 0; i < length; ++i) {
382         UChar c = text[i];
383         if (c == '\n' || c == '\r') {
384             if (i > lineStart) {
385                 appendChild(new Text(document(), text.substring(lineStart, i - lineStart)), ec);
386                 if (ec)
387                     return;
388             }
389             if (!(c == '\n' && i != 0 && prev == '\r')) {
390                 appendChild(new HTMLBRElement(document()), ec);
391                 if (ec)
392                     return;
393             }
394             lineStart = i + 1;
395         }
396         prev = c;
397     }
398     if (length > lineStart)
399         appendChild(new Text(document(), text.substring(lineStart, length - lineStart)), ec);
400 }
401
402 void HTMLElement::setOuterText(const String &text, ExceptionCode& ec)
403 {
404     // follow the IE specs about when this is allowed
405     if (endTagRequirement() == TagStatusForbidden) {
406         ec = NO_MODIFICATION_ALLOWED_ERR;
407         return;
408     }
409     if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
410         hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || 
411         hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) ||
412         hasLocalName(trTag)) {
413         ec = NO_MODIFICATION_ALLOWED_ERR;
414         return;
415     }
416
417     Node* parent = parentNode();
418     if (!parent) {
419         ec = NO_MODIFICATION_ALLOWED_ERR;
420         return;
421     }
422
423     // FIXME: This creates a new text node even when the text is empty.
424     // FIXME: This creates a single text node even when the text has CR and LF
425     // characters in it. Instead it should create <br> elements.
426     RefPtr<Text> t = new Text(document(), text);
427     ec = 0;
428     parent->replaceChild(t, this, ec);
429     if (ec)
430         return;
431
432     // is previous node a text node? if so, merge into it
433     Node* prev = t->previousSibling();
434     if (prev && prev->isTextNode()) {
435         Text* textPrev = static_cast<Text*>(prev);
436         textPrev->appendData(t->data(), ec);
437         if (ec)
438             return;
439         t->remove(ec);
440         if (ec)
441             return;
442         t = textPrev;
443     }
444
445     // is next node a text node? if so, merge it in
446     Node* next = t->nextSibling();
447     if (next && next->isTextNode()) {
448         Text* textNext = static_cast<Text*>(next);
449         t->appendData(textNext->data(), ec);
450         if (ec)
451             return;
452         textNext->remove(ec);
453         if (ec)
454             return;
455     }
456 }
457
458 void HTMLElement::addHTMLAlignment(MappedAttribute* attr)
459 {
460     // vertical alignment with respect to the current baseline of the text
461     // right or left means floating images
462     int propfloat = -1;
463     int propvalign = -1;
464     const AtomicString& alignment = attr->value();
465     if (equalIgnoringCase(alignment, "absmiddle")) {
466         propvalign = CSS_VAL_MIDDLE;
467     } else if (equalIgnoringCase(alignment, "absbottom")) {
468         propvalign = CSS_VAL_BOTTOM;
469     } else if (equalIgnoringCase(alignment, "left")) {
470         propfloat = CSS_VAL_LEFT;
471         propvalign = CSS_VAL_TOP;
472     } else if (equalIgnoringCase(alignment, "right")) {
473         propfloat = CSS_VAL_RIGHT;
474         propvalign = CSS_VAL_TOP;
475     } else if (equalIgnoringCase(alignment, "top")) {
476         propvalign = CSS_VAL_TOP;
477     } else if (equalIgnoringCase(alignment, "middle")) {
478         propvalign = CSS_VAL__WEBKIT_BASELINE_MIDDLE;
479     } else if (equalIgnoringCase(alignment, "center")) {
480         propvalign = CSS_VAL_MIDDLE;
481     } else if (equalIgnoringCase(alignment, "bottom")) {
482         propvalign = CSS_VAL_BASELINE;
483     } else if (equalIgnoringCase(alignment, "texttop")) {
484         propvalign = CSS_VAL_TEXT_TOP;
485     }
486     
487     if ( propfloat != -1 )
488         addCSSProperty( attr, CSS_PROP_FLOAT, propfloat );
489     if ( propvalign != -1 )
490         addCSSProperty( attr, CSS_PROP_VERTICAL_ALIGN, propvalign );
491 }
492
493 bool HTMLElement::isFocusable() const
494 {
495     return isContentEditable() && parent() && !parent()->isContentEditable();
496 }
497
498 bool HTMLElement::isContentEditable() const 
499 {
500     if (document()->frame() && document()->frame()->isContentEditable())
501         return true;
502
503     document()->updateRendering();
504
505     if (!renderer()) {
506         if (parentNode())
507             return parentNode()->isContentEditable();
508         else
509             return false;
510     }
511     
512     return renderer()->style()->userModify() == READ_WRITE || renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY;
513 }
514
515 bool HTMLElement::isContentRichlyEditable() const
516 {
517     if (document()->frame() && document()->frame()->isContentEditable())
518         return true;
519
520     document()->updateRendering();
521
522     if (!renderer()) {
523         if (parentNode())
524             return parentNode()->isContentEditable();
525         else
526             return false;
527     }
528     
529     return renderer()->style()->userModify() == READ_WRITE;
530 }
531
532 String HTMLElement::contentEditable() const 
533 {
534     document()->updateRendering();
535
536     if (!renderer())
537         return "false";
538     
539     switch (renderer()->style()->userModify()) {
540         case READ_WRITE:
541             return "true";
542         case READ_ONLY:
543             return "false";
544         case READ_WRITE_PLAINTEXT_ONLY:
545             return "plaintext-only";
546         default:
547             return "inherit";
548     }
549     return "inherit";
550 }
551
552 void HTMLElement::setContentEditable(MappedAttribute* attr) 
553 {
554     const AtomicString& enabled = attr->value();
555     if (enabled.isEmpty() || equalIgnoringCase(enabled, "true")) {
556         addCSSProperty(attr, CSS_PROP__WEBKIT_USER_MODIFY, CSS_VAL_READ_WRITE);
557         addCSSProperty(attr, CSS_PROP_WORD_WRAP, CSS_VAL_BREAK_WORD);
558         addCSSProperty(attr, CSS_PROP__WEBKIT_NBSP_MODE, CSS_VAL_SPACE);
559         addCSSProperty(attr, CSS_PROP__WEBKIT_LINE_BREAK, CSS_VAL_AFTER_WHITE_SPACE);
560     } else if (equalIgnoringCase(enabled, "false")) {
561         addCSSProperty(attr, CSS_PROP__WEBKIT_USER_MODIFY, CSS_VAL_READ_ONLY);
562         attr->decl()->removeProperty(CSS_PROP_WORD_WRAP, false);
563         attr->decl()->removeProperty(CSS_PROP__WEBKIT_NBSP_MODE, false);
564         attr->decl()->removeProperty(CSS_PROP__WEBKIT_LINE_BREAK, false);
565     } else if (equalIgnoringCase(enabled, "inherit")) {
566         addCSSProperty(attr, CSS_PROP__WEBKIT_USER_MODIFY, CSS_VAL_INHERIT);
567         attr->decl()->removeProperty(CSS_PROP_WORD_WRAP, false);
568         attr->decl()->removeProperty(CSS_PROP__WEBKIT_NBSP_MODE, false);
569         attr->decl()->removeProperty(CSS_PROP__WEBKIT_LINE_BREAK, false);
570     } else if (equalIgnoringCase(enabled, "plaintext-only")) {
571         addCSSProperty(attr, CSS_PROP__WEBKIT_USER_MODIFY, CSS_VAL_READ_WRITE_PLAINTEXT_ONLY);
572         addCSSProperty(attr, CSS_PROP_WORD_WRAP, CSS_VAL_BREAK_WORD);
573         addCSSProperty(attr, CSS_PROP__WEBKIT_NBSP_MODE, CSS_VAL_SPACE);
574         addCSSProperty(attr, CSS_PROP__WEBKIT_LINE_BREAK, CSS_VAL_AFTER_WHITE_SPACE);
575     }
576 }
577
578 void HTMLElement::setContentEditable(const String &enabled)
579 {
580     if (enabled == "inherit") {
581         ExceptionCode ec;
582         removeAttribute(contenteditableAttr, ec);
583     }
584     else
585         setAttribute(contenteditableAttr, enabled.isEmpty() ? "true" : enabled);
586 }
587
588 void HTMLElement::click()
589 {
590     dispatchSimulatedClick(0);
591 }
592
593 // accessKeyAction is used by the accessibility support code
594 // to send events to elements that our JavaScript caller does
595 // does not.  The elements JS is interested in have subclasses
596 // that override this method to direct the click appropriately.
597 // Here in the base class, then, we only send the click if
598 // the caller wants it to go to any HTMLElement, and we say
599 // to send the mouse events in addition to the click.
600 void HTMLElement::accessKeyAction(bool sendToAnyElement)
601 {
602     if (sendToAnyElement)
603         dispatchSimulatedClick(0, true);
604 }
605
606 String HTMLElement::toString() const
607 {
608     if (!hasChildNodes() && document()->isHTMLDocument()) {
609         String result = openTagStartToString();
610         result += ">";
611
612         if (endTagRequirement() == TagStatusRequired) {
613             result += "</";
614             result += nodeName();
615             result += ">";
616         }
617
618         return result;
619     }
620
621     return Element::toString();
622 }
623
624 String HTMLElement::id() const
625 {
626     return getAttribute(idAttr);
627 }
628
629 void HTMLElement::setId(const String &value)
630 {
631     setAttribute(idAttr, value);
632 }
633
634 String HTMLElement::title() const
635 {
636     return getAttribute(titleAttr);
637 }
638
639 void HTMLElement::setTitle(const String &value)
640 {
641     setAttribute(titleAttr, value);
642 }
643
644 String HTMLElement::lang() const
645 {
646     return getAttribute(langAttr);
647 }
648
649 void HTMLElement::setLang(const String &value)
650 {
651     setAttribute(langAttr, value);
652 }
653
654 String HTMLElement::dir() const
655 {
656     return getAttribute(dirAttr);
657 }
658
659 void HTMLElement::setDir(const String &value)
660 {
661     setAttribute(dirAttr, value);
662 }
663
664 String HTMLElement::className() const
665 {
666     return getAttribute(classAttr);
667 }
668
669 void HTMLElement::setClassName(const String &value)
670 {
671     setAttribute(classAttr, value);
672 }
673
674 PassRefPtr<HTMLCollection> HTMLElement::children()
675 {
676     return new HTMLCollection(this, HTMLCollection::NodeChildren);
677 }
678
679 // DOM Section 1.1.1
680 bool HTMLElement::childAllowed(Node *newChild)
681 {
682     if (!Element::childAllowed(newChild))
683         return false;
684
685     // For XML documents, we are non-validating and do not check against a DTD, even for HTML elements.
686     if (!document()->isHTMLDocument())
687         return true;
688
689     // Future-proof for XML content inside HTML documents (we may allow this some day).
690     if (newChild->isElementNode() && !newChild->isHTMLElement())
691         return true;
692
693     // Elements with forbidden tag status can never have children
694     if (endTagRequirement() == TagStatusForbidden)
695         return false;
696
697     // Comment nodes are always allowed.
698     if (newChild->isCommentNode())
699         return true;
700
701     // Now call checkDTD.
702     return checkDTD(newChild);
703 }
704
705 // DTD Stuff
706 // This unfortunate function is only needed when checking against the DTD.  Other languages (like SVG) won't need this.
707 bool HTMLElement::isRecognizedTagName(const QualifiedName& tagName)
708 {
709     static HashSet<AtomicStringImpl*> tagList;
710     if (tagList.isEmpty()) {
711         size_t tagCount = 0;
712         WebCore::QualifiedName** tags = HTMLNames::getHTMLTags(&tagCount);
713         for (size_t i = 0; i < tagCount; i++)
714             tagList.add(tags[i]->localName().impl());
715     }
716     return tagList.contains(tagName.localName().impl());
717 }
718
719 // The terms inline and block are used here loosely.  Don't make the mistake of assuming all inlines or all blocks
720 // need to be in these two lists.
721 HashSet<AtomicStringImpl*>* inlineTagList()
722 {
723     static HashSet<AtomicStringImpl*> tagList;
724     if (tagList.isEmpty()) {
725         tagList.add(ttTag.localName().impl());
726         tagList.add(iTag.localName().impl());
727         tagList.add(bTag.localName().impl());
728         tagList.add(uTag.localName().impl());
729         tagList.add(sTag.localName().impl());
730         tagList.add(strikeTag.localName().impl());
731         tagList.add(bigTag.localName().impl());
732         tagList.add(smallTag.localName().impl());
733         tagList.add(emTag.localName().impl());
734         tagList.add(strongTag.localName().impl());
735         tagList.add(dfnTag.localName().impl());
736         tagList.add(codeTag.localName().impl());
737         tagList.add(sampTag.localName().impl());
738         tagList.add(kbdTag.localName().impl());
739         tagList.add(varTag.localName().impl());
740         tagList.add(citeTag.localName().impl());
741         tagList.add(abbrTag.localName().impl());
742         tagList.add(acronymTag.localName().impl());
743         tagList.add(aTag.localName().impl());
744         tagList.add(canvasTag.localName().impl());
745         tagList.add(imgTag.localName().impl());
746         tagList.add(appletTag.localName().impl());
747         tagList.add(objectTag.localName().impl());
748         tagList.add(embedTag.localName().impl());
749         tagList.add(fontTag.localName().impl());
750         tagList.add(basefontTag.localName().impl());
751         tagList.add(brTag.localName().impl());
752         tagList.add(scriptTag.localName().impl());
753         tagList.add(mapTag.localName().impl());
754         tagList.add(qTag.localName().impl());
755         tagList.add(subTag.localName().impl());
756         tagList.add(supTag.localName().impl());
757         tagList.add(spanTag.localName().impl());
758         tagList.add(bdoTag.localName().impl());
759         tagList.add(iframeTag.localName().impl());
760         tagList.add(inputTag.localName().impl());
761         tagList.add(keygenTag.localName().impl());
762         tagList.add(selectTag.localName().impl());
763         tagList.add(textareaTag.localName().impl());
764         tagList.add(labelTag.localName().impl());
765         tagList.add(buttonTag.localName().impl());
766         tagList.add(insTag.localName().impl());
767         tagList.add(delTag.localName().impl());
768         tagList.add(nobrTag.localName().impl());
769         tagList.add(wbrTag.localName().impl());
770     }
771     return &tagList;
772 }
773
774 HashSet<AtomicStringImpl*>* blockTagList()
775 {
776     static HashSet<AtomicStringImpl*> tagList;
777     if (tagList.isEmpty()) {
778         tagList.add(addressTag.localName().impl());
779         tagList.add(blockquoteTag.localName().impl());
780         tagList.add(centerTag.localName().impl());
781         tagList.add(ddTag.localName().impl());
782         tagList.add(dirTag.localName().impl());
783         tagList.add(divTag.localName().impl());
784         tagList.add(dlTag.localName().impl());
785         tagList.add(dtTag.localName().impl());
786         tagList.add(fieldsetTag.localName().impl());
787         tagList.add(formTag.localName().impl());
788         tagList.add(h1Tag.localName().impl());
789         tagList.add(h2Tag.localName().impl());
790         tagList.add(h3Tag.localName().impl());
791         tagList.add(h4Tag.localName().impl());
792         tagList.add(h5Tag.localName().impl());
793         tagList.add(h6Tag.localName().impl());
794         tagList.add(hrTag.localName().impl());
795         tagList.add(isindexTag.localName().impl());
796         tagList.add(layerTag.localName().impl());
797         tagList.add(liTag.localName().impl());
798         tagList.add(listingTag.localName().impl());
799         tagList.add(marqueeTag.localName().impl());
800         tagList.add(menuTag.localName().impl());
801         tagList.add(noembedTag.localName().impl());
802         tagList.add(noframesTag.localName().impl());
803         tagList.add(nolayerTag.localName().impl());
804         tagList.add(noscriptTag.localName().impl());
805         tagList.add(olTag.localName().impl());
806         tagList.add(pTag.localName().impl());
807         tagList.add(plaintextTag.localName().impl());
808         tagList.add(preTag.localName().impl());
809         tagList.add(tableTag.localName().impl());
810         tagList.add(ulTag.localName().impl());
811         tagList.add(xmpTag.localName().impl());
812     }
813     return &tagList;
814 }
815
816 bool HTMLElement::inEitherTagList(const Node* newChild)
817 {
818     if (newChild->isTextNode())
819         return true;
820         
821     if (newChild->isHTMLElement()) {
822         const HTMLElement* child = static_cast<const HTMLElement*>(newChild);
823         if (inlineTagList()->contains(child->tagQName().localName().impl()))
824             return true;
825         if (blockTagList()->contains(child->tagQName().localName().impl()))
826             return true;
827         return !isRecognizedTagName(child->tagQName()); // Accept custom html tags
828     }
829
830     return false;
831 }
832
833 bool HTMLElement::inInlineTagList(const Node* newChild)
834 {
835     if (newChild->isTextNode())
836         return true;
837
838     if (newChild->isHTMLElement()) {
839         const HTMLElement* child = static_cast<const HTMLElement*>(newChild);
840         if (inlineTagList()->contains(child->tagQName().localName().impl()))
841             return true;
842         return !isRecognizedTagName(child->tagQName()); // Accept custom html tags
843     }
844
845     return false;
846 }
847
848 bool HTMLElement::inBlockTagList(const Node* newChild)
849 {
850     if (newChild->isTextNode())
851         return true;
852             
853     if (newChild->isHTMLElement()) {
854         const HTMLElement* child = static_cast<const HTMLElement*>(newChild);
855         return (blockTagList()->contains(child->tagQName().localName().impl()));
856     }
857
858     return false;
859 }
860
861 bool HTMLElement::checkDTD(const Node* newChild)
862 {
863     if (hasTagName(addressTag) && newChild->hasTagName(pTag))
864         return true;
865     return inEitherTagList(newChild);
866 }
867
868 void HTMLElement::setHTMLEventListener(const AtomicString& eventType, Attribute* attr)
869 {
870     Element::setHTMLEventListener(eventType,
871         document()->createHTMLEventListener(attr->localName().domString(), attr->value(), this));
872 }
873
874 }