initial import
[vuplus_webkit] / Source / WebCore / html / HTMLElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  */
23
24 #include "config.h"
25 #include "HTMLElement.h"
26
27 #include "Attribute.h"
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 "HTMLCollection.h"
38 #include "HTMLDocument.h"
39 #include "HTMLElementFactory.h"
40 #include "HTMLFormElement.h"
41 #include "HTMLNames.h"
42 #include "HTMLParserIdioms.h"
43 #include "HTMLTextFormControlElement.h"
44 #include "RenderWordBreak.h"
45 #include "ScriptEventListener.h"
46 #include "Settings.h"
47 #include "Text.h"
48 #include "TextIterator.h"
49 #include "markup.h"
50 #include <wtf/StdLibExtras.h>
51 #include <wtf/text/CString.h>
52
53 namespace WebCore {
54
55 using namespace HTMLNames;
56 using namespace WTF;
57
58 using std::min;
59 using std::max;
60
61 PassRefPtr<HTMLElement> HTMLElement::create(const QualifiedName& tagName, Document* document)
62 {
63     return adoptRef(new HTMLElement(tagName, document));
64 }
65
66 String HTMLElement::nodeName() const
67 {
68     // FIXME: Would be nice to have an atomicstring lookup based off uppercase
69     // chars that does not have to copy the string on a hit in the hash.
70     // FIXME: We should have a way to detect XHTML elements and replace the hasPrefix() check with it.
71     if (document()->isHTMLDocument() && !tagQName().hasPrefix())
72         return tagQName().localNameUpper();
73     return Element::nodeName();
74 }
75
76 bool HTMLElement::ieForbidsInsertHTML() const
77 {
78     // FIXME: Supposedly IE disallows settting innerHTML, outerHTML
79     // and createContextualFragment on these tags.  We have no tests to
80     // verify this however, so this list could be totally wrong.
81     // This list was moved from the previous endTagRequirement() implementation.
82     // This is also called from editing and assumed to be the list of tags
83     // for which no end tag should be serialized. It's unclear if the list for
84     // IE compat and the list for serialization sanity are the same.
85     if (hasLocalName(areaTag)
86         || hasLocalName(baseTag)
87         || hasLocalName(basefontTag)
88         || hasLocalName(brTag)
89         || hasLocalName(colTag)
90         || hasLocalName(embedTag)
91         || hasLocalName(frameTag)
92         || hasLocalName(hrTag)
93         || hasLocalName(imageTag)
94         || hasLocalName(imgTag)
95         || hasLocalName(inputTag)
96         || hasLocalName(isindexTag)
97         || hasLocalName(linkTag)
98         || hasLocalName(metaTag)
99         || hasLocalName(paramTag)
100         || hasLocalName(sourceTag)
101         || hasLocalName(wbrTag))
102         return true;
103     // FIXME: I'm not sure why dashboard mode would want to change the
104     // serialization of <canvas>, that seems like a bad idea.
105 #if ENABLE(DASHBOARD_SUPPORT)
106     if (hasLocalName(canvasTag)) {
107         Settings* settings = document()->settings();
108         if (settings && settings->usesDashboardBackwardCompatibilityMode())
109             return true;
110     }
111 #endif
112     return false;
113 }
114
115 bool HTMLElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
116 {
117     if (attrName == alignAttr
118         || attrName == contenteditableAttr
119         || attrName == hiddenAttr) {
120         result = eUniversal;
121         return false;
122     }
123     if (attrName == dirAttr) {
124         result = hasLocalName(bdoTag) ? eBDO : eUniversal;
125         return true;
126     }
127
128     return StyledElement::mapToEntry(attrName, result);
129 }
130
131 static inline int unicodeBidiAttributeForDirAuto(HTMLElement* element)
132 {
133     if (element->hasLocalName(bdoTag))
134         return CSSValueBidiOverride;
135     if (element->hasLocalName(preTag) || element->hasLocalName(textareaTag))
136         return CSSValueWebkitPlaintext;
137     return CSSValueEmbed;
138 }
139
140 static unsigned parseBorderWidthAttribute(Attribute* attr)
141 {
142     ASSERT(attr && attr->name() == borderAttr);
143
144     unsigned borderWidth = 0;
145     if (!attr->value().isEmpty())
146         parseHTMLNonNegativeInteger(attr->value(), borderWidth);
147
148     return borderWidth;
149 }
150
151 void HTMLElement::applyBorderAttribute(Attribute* attr)
152 {
153     addCSSLength(attr, CSSPropertyBorderWidth, String::number(parseBorderWidthAttribute(attr)));
154     addCSSProperty(attr, CSSPropertyBorderTopStyle, CSSValueSolid);
155     addCSSProperty(attr, CSSPropertyBorderRightStyle, CSSValueSolid);
156     addCSSProperty(attr, CSSPropertyBorderBottomStyle, CSSValueSolid);
157     addCSSProperty(attr, CSSPropertyBorderLeftStyle, CSSValueSolid);
158 }
159
160 void HTMLElement::parseMappedAttribute(Attribute* attr)
161 {
162     if (isIdAttributeName(attr->name()) || attr->name() == classAttr || attr->name() == styleAttr)
163         return StyledElement::parseMappedAttribute(attr);
164
165     String indexstring;
166     if (attr->name() == alignAttr) {
167         if (equalIgnoringCase(attr->value(), "middle"))
168             addCSSProperty(attr, CSSPropertyTextAlign, "center");
169         else
170             addCSSProperty(attr, CSSPropertyTextAlign, attr->value());
171     } else if (attr->name() == contenteditableAttr) {
172         setContentEditable(attr);
173     } else if (attr->name() == hiddenAttr) {
174         addCSSProperty(attr, CSSPropertyDisplay, CSSValueNone);
175     } else if (attr->name() == tabindexAttr) {
176         indexstring = getAttribute(tabindexAttr);
177         int tabindex = 0;
178         if (!indexstring.length()) {
179             clearTabIndexExplicitly();
180         } else if (parseHTMLInteger(indexstring, tabindex)) {
181             // Clamp tabindex to the range of 'short' to match Firefox's behavior.
182             setTabIndexExplicitly(max(static_cast<int>(std::numeric_limits<short>::min()), min(tabindex, static_cast<int>(std::numeric_limits<short>::max()))));
183         }
184     } else if (attr->name() == langAttr) {
185         // FIXME: Implement
186     } else if (attr->name() == dirAttr) {
187         if (!equalIgnoringCase(attr->value(), "auto"))
188             addCSSProperty(attr, CSSPropertyDirection, attr->value());
189         dirAttributeChanged(attr);
190         addCSSProperty(attr, CSSPropertyUnicodeBidi, unicodeBidiAttributeForDirAuto(this));
191     } else if (attr->name() == draggableAttr) {
192         const AtomicString& value = attr->value();
193         if (equalIgnoringCase(value, "true")) {
194             addCSSProperty(attr, CSSPropertyWebkitUserDrag, CSSValueElement);
195             addCSSProperty(attr, CSSPropertyWebkitUserSelect, CSSValueNone);
196         } else if (equalIgnoringCase(value, "false"))
197             addCSSProperty(attr, CSSPropertyWebkitUserDrag, CSSValueNone);
198     }
199 // standard events
200     else if (attr->name() == onclickAttr) {
201         setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, attr));
202     } else if (attr->name() == oncontextmenuAttr) {
203         setAttributeEventListener(eventNames().contextmenuEvent, createAttributeEventListener(this, attr));
204     } else if (attr->name() == ondblclickAttr) {
205         setAttributeEventListener(eventNames().dblclickEvent, createAttributeEventListener(this, attr));
206     } else if (attr->name() == onmousedownAttr) {
207         setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, attr));
208     } else if (attr->name() == onmousemoveAttr) {
209         setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, attr));
210     } else if (attr->name() == onmouseoutAttr) {
211         setAttributeEventListener(eventNames().mouseoutEvent, createAttributeEventListener(this, attr));
212     } else if (attr->name() == onmouseoverAttr) {
213         setAttributeEventListener(eventNames().mouseoverEvent, createAttributeEventListener(this, attr));
214     } else if (attr->name() == onmouseupAttr) {
215         setAttributeEventListener(eventNames().mouseupEvent, createAttributeEventListener(this, attr));
216     } else if (attr->name() == onmousewheelAttr) {
217         setAttributeEventListener(eventNames().mousewheelEvent, createAttributeEventListener(this, attr));
218     } else if (attr->name() == onfocusAttr) {
219         setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr));
220     } else if (attr->name() == onfocusinAttr) {
221         setAttributeEventListener(eventNames().focusinEvent, createAttributeEventListener(this, attr));
222     } else if (attr->name() == onfocusoutAttr) {
223         setAttributeEventListener(eventNames().focusoutEvent, createAttributeEventListener(this, attr));
224     } else if (attr->name() == onblurAttr) {
225         setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr));
226     } else if (attr->name() == onkeydownAttr) {
227         setAttributeEventListener(eventNames().keydownEvent, createAttributeEventListener(this, attr));
228     } else if (attr->name() == onkeypressAttr) {
229         setAttributeEventListener(eventNames().keypressEvent, createAttributeEventListener(this, attr));
230     } else if (attr->name() == onkeyupAttr) {
231         setAttributeEventListener(eventNames().keyupEvent, createAttributeEventListener(this, attr));
232     } else if (attr->name() == onscrollAttr) {
233         setAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(this, attr));
234     } else if (attr->name() == onbeforecutAttr) {
235         setAttributeEventListener(eventNames().beforecutEvent, createAttributeEventListener(this, attr));
236     } else if (attr->name() == oncutAttr) {
237         setAttributeEventListener(eventNames().cutEvent, createAttributeEventListener(this, attr));
238     } else if (attr->name() == onbeforecopyAttr) {
239         setAttributeEventListener(eventNames().beforecopyEvent, createAttributeEventListener(this, attr));
240     } else if (attr->name() == oncopyAttr) {
241         setAttributeEventListener(eventNames().copyEvent, createAttributeEventListener(this, attr));
242     } else if (attr->name() == onbeforepasteAttr) {
243         setAttributeEventListener(eventNames().beforepasteEvent, createAttributeEventListener(this, attr));
244     } else if (attr->name() == onpasteAttr) {
245         setAttributeEventListener(eventNames().pasteEvent, createAttributeEventListener(this, attr));
246     } else if (attr->name() == ondragenterAttr) {
247         setAttributeEventListener(eventNames().dragenterEvent, createAttributeEventListener(this, attr));
248     } else if (attr->name() == ondragoverAttr) {
249         setAttributeEventListener(eventNames().dragoverEvent, createAttributeEventListener(this, attr));
250     } else if (attr->name() == ondragleaveAttr) {
251         setAttributeEventListener(eventNames().dragleaveEvent, createAttributeEventListener(this, attr));
252     } else if (attr->name() == ondropAttr) {
253         setAttributeEventListener(eventNames().dropEvent, createAttributeEventListener(this, attr));
254     } else if (attr->name() == ondragstartAttr) {
255         setAttributeEventListener(eventNames().dragstartEvent, createAttributeEventListener(this, attr));
256     } else if (attr->name() == ondragAttr) {
257         setAttributeEventListener(eventNames().dragEvent, createAttributeEventListener(this, attr));
258     } else if (attr->name() == ondragendAttr) {
259         setAttributeEventListener(eventNames().dragendEvent, createAttributeEventListener(this, attr));
260     } else if (attr->name() == onselectstartAttr) {
261         setAttributeEventListener(eventNames().selectstartEvent, createAttributeEventListener(this, attr));
262     } else if (attr->name() == onsubmitAttr) {
263         setAttributeEventListener(eventNames().submitEvent, createAttributeEventListener(this, attr));
264     } else if (attr->name() == onerrorAttr) {
265         setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
266     } else if (attr->name() == onwebkitanimationstartAttr) {
267         setAttributeEventListener(eventNames().webkitAnimationStartEvent, createAttributeEventListener(this, attr));
268     } else if (attr->name() == onwebkitanimationiterationAttr) {
269         setAttributeEventListener(eventNames().webkitAnimationIterationEvent, createAttributeEventListener(this, attr));
270     } else if (attr->name() == onwebkitanimationendAttr) {
271         setAttributeEventListener(eventNames().webkitAnimationEndEvent, createAttributeEventListener(this, attr));
272     } else if (attr->name() == onwebkittransitionendAttr) {
273         setAttributeEventListener(eventNames().webkitTransitionEndEvent, createAttributeEventListener(this, attr));
274     } else if (attr->name() == oninputAttr) {
275         setAttributeEventListener(eventNames().inputEvent, createAttributeEventListener(this, attr));
276     } else if (attr->name() == oninvalidAttr) {
277         setAttributeEventListener(eventNames().invalidEvent, createAttributeEventListener(this, attr));
278     } else if (attr->name() == ontouchstartAttr) {
279         setAttributeEventListener(eventNames().touchstartEvent, createAttributeEventListener(this, attr));
280     } else if (attr->name() == ontouchmoveAttr) {
281         setAttributeEventListener(eventNames().touchmoveEvent, createAttributeEventListener(this, attr));
282     } else if (attr->name() == ontouchendAttr) {
283         setAttributeEventListener(eventNames().touchendEvent, createAttributeEventListener(this, attr));
284     } else if (attr->name() == ontouchcancelAttr) {
285         setAttributeEventListener(eventNames().touchcancelEvent, createAttributeEventListener(this, attr));
286 #if ENABLE(FULLSCREEN_API)
287     } else if (attr->name() == onwebkitfullscreenchangeAttr) {
288         setAttributeEventListener(eventNames().webkitfullscreenchangeEvent, createAttributeEventListener(this, attr));
289 #endif
290     }
291 }
292
293 String HTMLElement::innerHTML() const
294 {
295     return createMarkup(this, ChildrenOnly);
296 }
297
298 String HTMLElement::outerHTML() const
299 {
300     return createMarkup(this);
301 }
302
303 static inline bool hasOneChild(ContainerNode* node)
304 {
305     Node* firstChild = node->firstChild();
306     return firstChild && !firstChild->nextSibling();
307 }
308
309 static inline bool hasOneTextChild(ContainerNode* node)
310 {
311     return hasOneChild(node) && node->firstChild()->isTextNode();
312 }
313
314 static void replaceChildrenWithFragment(HTMLElement* element, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec)
315 {
316     if (!fragment->firstChild()) {
317         element->removeChildren();
318         return;
319     }
320
321     if (hasOneTextChild(element) && hasOneTextChild(fragment.get())) {
322         static_cast<Text*>(element->firstChild())->setData(static_cast<Text*>(fragment->firstChild())->data(), ec);
323         return;
324     }
325
326     if (hasOneChild(element)) {
327         element->replaceChild(fragment, element->firstChild(), ec);
328         return;
329     }
330
331     element->removeChildren();
332     element->appendChild(fragment, ec);
333 }
334
335 static void replaceChildrenWithText(HTMLElement* element, const String& text, ExceptionCode& ec)
336 {
337     if (hasOneTextChild(element)) {
338         static_cast<Text*>(element->firstChild())->setData(text, ec);
339         return;
340     }
341
342     RefPtr<Text> textNode = Text::create(element->document(), text);
343
344     if (hasOneChild(element)) {
345         element->replaceChild(textNode.release(), element->firstChild(), ec);
346         return;
347     }
348
349     element->removeChildren();
350     element->appendChild(textNode.release(), ec);
351 }
352
353 // We may want to move a version of this function into DocumentFragment.h/cpp
354 static PassRefPtr<DocumentFragment> createFragmentFromSource(const String& markup, Element* contextElement, ExceptionCode& ec)
355 {
356     Document* document = contextElement->document();
357     RefPtr<DocumentFragment> fragment;
358
359     fragment = DocumentFragment::create(document);
360     if (document->isHTMLDocument()) {
361         fragment->parseHTML(markup, contextElement);
362         return fragment;
363     }
364
365     bool wasValid = fragment->parseXML(markup, contextElement);
366     if (!wasValid) {
367         ec = INVALID_STATE_ERR;
368         return 0;
369     }
370     return fragment;
371 }
372
373 void HTMLElement::setInnerHTML(const String& html, ExceptionCode& ec)
374 {
375     RefPtr<DocumentFragment> fragment = createFragmentFromSource(html, this, ec);
376     if (fragment)
377         replaceChildrenWithFragment(this, fragment.release(), ec);
378 }
379
380 static void mergeWithNextTextNode(PassRefPtr<Node> node, ExceptionCode& ec)
381 {
382     ASSERT(node && node->isTextNode());
383     Node* next = node->nextSibling();
384     if (!next || !next->isTextNode())
385         return;
386     
387     RefPtr<Text> textNode = static_cast<Text*>(node.get());
388     RefPtr<Text> textNext = static_cast<Text*>(next);
389     textNode->appendData(textNext->data(), ec);
390     if (ec)
391         return;
392     if (textNext->parentNode()) // Might have been removed by mutation event.
393         textNext->remove(ec);
394 }
395
396 void HTMLElement::setOuterHTML(const String& html, ExceptionCode& ec)
397 {
398     Node* p = parentNode();
399     if (!p || !p->isHTMLElement()) {
400         ec = NO_MODIFICATION_ALLOWED_ERR;
401         return;
402     }
403     RefPtr<HTMLElement> parent = static_cast<HTMLElement*>(p);
404     RefPtr<Node> prev = previousSibling();
405     RefPtr<Node> next = nextSibling();
406
407     RefPtr<DocumentFragment> fragment = createFragmentFromSource(html, parent.get(), ec);
408     if (ec)
409         return;
410       
411     parent->replaceChild(fragment.release(), this, ec);
412     RefPtr<Node> node = next ? next->previousSibling() : 0;
413     if (!ec && node && node->isTextNode())
414         mergeWithNextTextNode(node.release(), ec);
415
416     if (!ec && prev && prev->isTextNode())
417         mergeWithNextTextNode(prev.release(), ec);
418 }
419
420 PassRefPtr<DocumentFragment> HTMLElement::textToFragment(const String& text, ExceptionCode& ec)
421 {
422     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document());
423     unsigned int i, length = text.length();
424     UChar c = 0;
425     for (unsigned int start = 0; start < length; ) {
426
427         // Find next line break.
428         for (i = start; i < length; i++) {
429           c = text[i];
430           if (c == '\r' || c == '\n')
431               break;
432         }
433
434         fragment->appendChild(Text::create(document(), text.substring(start, i - start)), ec);
435         if (ec)
436             return 0;
437
438         if (c == '\r' || c == '\n') {
439             fragment->appendChild(HTMLBRElement::create(document()), ec);
440             if (ec)
441                 return 0;
442             // Make sure \r\n doesn't result in two line breaks.
443             if (c == '\r' && i + 1 < length && text[i + 1] == '\n')
444                 i++;
445         }
446
447         start = i + 1; // Character after line break.
448     }
449
450     return fragment;
451 }
452
453 void HTMLElement::setInnerText(const String& text, ExceptionCode& ec)
454 {
455     if (ieForbidsInsertHTML()) {
456         ec = NO_MODIFICATION_ALLOWED_ERR;
457         return;
458     }
459     if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
460         hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || 
461         hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) ||
462         hasLocalName(trTag)) {
463         ec = NO_MODIFICATION_ALLOWED_ERR;
464         return;
465     }
466
467     // FIXME: This doesn't take whitespace collapsing into account at all.
468
469     if (!text.contains('\n') && !text.contains('\r')) {
470         if (text.isEmpty()) {
471             removeChildren();
472             return;
473         }
474         replaceChildrenWithText(this, text, ec);
475         return;
476     }
477
478     // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
479     // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded?
480     // For example, for the contents of textarea elements that are display:none?
481     RenderObject* r = renderer();
482     if (r && r->style()->preserveNewline()) {
483         if (!text.contains('\r')) {
484             replaceChildrenWithText(this, text, ec);
485             return;
486         }
487         String textWithConsistentLineBreaks = text;
488         textWithConsistentLineBreaks.replace("\r\n", "\n");
489         textWithConsistentLineBreaks.replace('\r', '\n');
490         replaceChildrenWithText(this, textWithConsistentLineBreaks, ec);
491         return;
492     }
493
494     // Add text nodes and <br> elements.
495     ec = 0;
496     RefPtr<DocumentFragment> fragment = textToFragment(text, ec);
497     if (!ec)
498         replaceChildrenWithFragment(this, fragment.release(), ec);
499 }
500
501 void HTMLElement::setOuterText(const String &text, ExceptionCode& ec)
502 {
503     if (ieForbidsInsertHTML()) {
504         ec = NO_MODIFICATION_ALLOWED_ERR;
505         return;
506     }
507     if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
508         hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || 
509         hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) ||
510         hasLocalName(trTag)) {
511         ec = NO_MODIFICATION_ALLOWED_ERR;
512         return;
513     }
514
515     ContainerNode* parent = parentNode();
516     if (!parent) {
517         ec = NO_MODIFICATION_ALLOWED_ERR;
518         return;
519     }
520
521     RefPtr<Node> prev = previousSibling();
522     RefPtr<Node> next = nextSibling();
523     RefPtr<Node> newChild;
524     ec = 0;
525     
526     // Convert text to fragment with <br> tags instead of linebreaks if needed.
527     if (text.contains('\r') || text.contains('\n'))
528         newChild = textToFragment(text, ec);
529     else
530         newChild = Text::create(document(), text);
531
532     if (!this || !parentNode())
533         ec = HIERARCHY_REQUEST_ERR;
534     if (ec)
535         return;
536     parent->replaceChild(newChild.release(), this, ec);
537
538     RefPtr<Node> node = next ? next->previousSibling() : 0;
539     if (!ec && node && node->isTextNode())
540         mergeWithNextTextNode(node.release(), ec);
541
542     if (!ec && prev && prev->isTextNode())
543         mergeWithNextTextNode(prev.release(), ec);
544 }
545
546 Node* HTMLElement::insertAdjacent(const String& where, Node* newChild, ExceptionCode& ec)
547 {
548     // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd",
549     // a document fragment is created and the elements appended in the correct order. This document
550     // fragment isn't returned anywhere.
551     //
552     // This is impossible for us to implement as the DOM tree does not allow for such structures,
553     // Opera also appears to disallow such usage.
554
555     if (equalIgnoringCase(where, "beforeBegin")) {
556         ContainerNode* parent = this->parentNode();
557         return (parent && parent->insertBefore(newChild, this, ec)) ? newChild : 0;
558     }
559
560     if (equalIgnoringCase(where, "afterBegin"))
561         return insertBefore(newChild, firstChild(), ec) ? newChild : 0;
562
563     if (equalIgnoringCase(where, "beforeEnd"))
564         return appendChild(newChild, ec) ? newChild : 0;
565
566     if (equalIgnoringCase(where, "afterEnd")) {
567         ContainerNode* parent = this->parentNode();
568         return (parent && parent->insertBefore(newChild, nextSibling(), ec)) ? newChild : 0;
569     }
570     
571     // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative.
572     ec = NOT_SUPPORTED_ERR;
573     return 0;
574 }
575
576 Element* HTMLElement::insertAdjacentElement(const String& where, Element* newChild, ExceptionCode& ec)
577 {
578     if (!newChild) {
579         // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative.
580         ec = TYPE_MISMATCH_ERR;
581         return 0;
582     }
583
584     Node* returnValue = insertAdjacent(where, newChild, ec);
585     ASSERT(!returnValue || returnValue->isElementNode());
586     return static_cast<Element*>(returnValue); 
587 }
588
589 // Step 3 of http://www.whatwg.org/specs/web-apps/current-work/multipage/apis-in-html-documents.html#insertadjacenthtml()
590 static Element* contextElementForInsertion(const String& where, Element* element, ExceptionCode& ec)
591 {
592     if (equalIgnoringCase(where, "beforeBegin") || equalIgnoringCase(where, "afterEnd")) {
593         ContainerNode* parent = element->parentNode();
594         if (parent && parent->isDocumentNode()) {
595             ec = NO_MODIFICATION_ALLOWED_ERR;
596             return 0;
597         }
598         ASSERT(!parent || parent->isElementNode());
599         return static_cast<Element*>(parent);
600     }
601     if (equalIgnoringCase(where, "afterBegin") || equalIgnoringCase(where, "beforeEnd"))
602         return element;
603     ec =  SYNTAX_ERR;
604     return 0;
605 }
606
607 void HTMLElement::insertAdjacentHTML(const String& where, const String& markup, ExceptionCode& ec)
608 {
609     RefPtr<DocumentFragment> fragment = document()->createDocumentFragment();
610     Element* contextElement = contextElementForInsertion(where, this, ec);
611     if (!contextElement)
612         return;
613
614     if (document()->isHTMLDocument())
615          fragment->parseHTML(markup, contextElement);
616     else {
617         if (!fragment->parseXML(markup, contextElement))
618             // FIXME: We should propagate a syntax error exception out here.
619             return;
620     }
621
622     insertAdjacent(where, fragment.get(), ec);
623 }
624
625 void HTMLElement::insertAdjacentText(const String& where, const String& text, ExceptionCode& ec)
626 {
627     RefPtr<Text> textNode = document()->createTextNode(text);
628     insertAdjacent(where, textNode.get(), ec);
629 }
630
631 void HTMLElement::addHTMLAlignment(Attribute* attr)
632 {
633     addHTMLAlignmentToStyledElement(this, attr);
634 }
635
636 void HTMLElement::addHTMLAlignmentToStyledElement(StyledElement* element, Attribute* attr)
637 {
638     // Vertical alignment with respect to the current baseline of the text
639     // right or left means floating images.
640     int floatValue = CSSValueInvalid;
641     int verticalAlignValue = CSSValueInvalid;
642
643     const AtomicString& alignment = attr->value();
644     if (equalIgnoringCase(alignment, "absmiddle"))
645         verticalAlignValue = CSSValueMiddle;
646     else if (equalIgnoringCase(alignment, "absbottom"))
647         verticalAlignValue = CSSValueBottom;
648     else if (equalIgnoringCase(alignment, "left")) {
649         floatValue = CSSValueLeft;
650         verticalAlignValue = CSSValueTop;
651     } else if (equalIgnoringCase(alignment, "right")) {
652         floatValue = CSSValueRight;
653         verticalAlignValue = CSSValueTop;
654     } else if (equalIgnoringCase(alignment, "top"))
655         verticalAlignValue = CSSValueTop;
656     else if (equalIgnoringCase(alignment, "middle"))
657         verticalAlignValue = CSSValueWebkitBaselineMiddle;
658     else if (equalIgnoringCase(alignment, "center"))
659         verticalAlignValue = CSSValueMiddle;
660     else if (equalIgnoringCase(alignment, "bottom"))
661         verticalAlignValue = CSSValueBaseline;
662     else if (equalIgnoringCase(alignment, "texttop"))
663         verticalAlignValue = CSSValueTextTop;
664
665     if (floatValue != CSSValueInvalid)
666         element->addCSSProperty(attr, CSSPropertyFloat, floatValue);
667
668     if (verticalAlignValue != CSSValueInvalid)
669         element->addCSSProperty(attr, CSSPropertyVerticalAlign, verticalAlignValue);
670 }
671
672 bool HTMLElement::supportsFocus() const
673 {
674     return Element::supportsFocus() || (rendererIsEditable() && parentNode() && !parentNode()->rendererIsEditable());
675 }
676
677 String HTMLElement::contentEditable() const
678 {
679     const AtomicString& value = fastGetAttribute(contenteditableAttr);
680
681     if (value.isNull())
682         return "inherit";
683     if (value.isEmpty() || equalIgnoringCase(value, "true"))
684         return "true";
685     if (equalIgnoringCase(value, "false"))
686          return "false";
687     if (equalIgnoringCase(value, "plaintext-only"))
688         return "plaintext-only";
689
690     return "inherit";
691 }
692
693 void HTMLElement::setContentEditable(Attribute* attr) 
694 {
695     const AtomicString& enabled = attr->value();
696     if (enabled.isEmpty() || equalIgnoringCase(enabled, "true")) {
697         addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadWrite);
698         addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord);
699         addCSSProperty(attr, CSSPropertyWebkitNbspMode, CSSValueSpace);
700         addCSSProperty(attr, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
701     } else if (equalIgnoringCase(enabled, "false")) {
702         addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadOnly);
703         attr->decl()->removeProperty(CSSPropertyWordWrap, false);
704         attr->decl()->removeProperty(CSSPropertyWebkitNbspMode, false);
705         attr->decl()->removeProperty(CSSPropertyWebkitLineBreak, false);
706     } else if (equalIgnoringCase(enabled, "plaintext-only")) {
707         addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadWritePlaintextOnly);
708         addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord);
709         addCSSProperty(attr, CSSPropertyWebkitNbspMode, CSSValueSpace);
710         addCSSProperty(attr, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
711     }
712 }
713
714 void HTMLElement::setContentEditable(const String& enabled, ExceptionCode& ec)
715 {
716     if (equalIgnoringCase(enabled, "true"))
717         setAttribute(contenteditableAttr, "true", ec);
718     else if (equalIgnoringCase(enabled, "false"))
719         setAttribute(contenteditableAttr, "false", ec);
720     else if (equalIgnoringCase(enabled, "plaintext-only"))
721         setAttribute(contenteditableAttr, "plaintext-only");
722     else if (equalIgnoringCase(enabled, "inherit"))
723         removeAttribute(contenteditableAttr, ec);
724     else
725         ec = SYNTAX_ERR;
726 }
727
728 bool HTMLElement::draggable() const
729 {
730     return equalIgnoringCase(getAttribute(draggableAttr), "true");
731 }
732
733 void HTMLElement::setDraggable(bool value)
734 {
735     setAttribute(draggableAttr, value ? "true" : "false");
736 }
737
738 bool HTMLElement::spellcheck() const
739 {
740     return isSpellCheckingEnabled();
741 }
742
743 void HTMLElement::setSpellcheck(bool enable)
744 {
745     setAttribute(spellcheckAttr, enable ? "true" : "false");
746 }
747
748
749 void HTMLElement::click()
750 {
751     dispatchSimulatedClick(0, false, false);
752 }
753
754 // accessKeyAction is used by the accessibility support code
755 // to send events to elements that our JavaScript caller does
756 // does not.  The elements JS is interested in have subclasses
757 // that override this method to direct the click appropriately.
758 // Here in the base class, then, we only send the click if
759 // the caller wants it to go to any HTMLElement, and we say
760 // to send the mouse events in addition to the click.
761 void HTMLElement::accessKeyAction(bool sendToAnyElement)
762 {
763     if (sendToAnyElement)
764         dispatchSimulatedClick(0, true);
765 }
766
767 String HTMLElement::title() const
768 {
769     return getAttribute(titleAttr);
770 }
771
772 short HTMLElement::tabIndex() const
773 {
774     if (supportsFocus())
775         return Element::tabIndex();
776     return -1;
777 }
778
779 void HTMLElement::setTabIndex(int value)
780 {
781     setAttribute(tabindexAttr, String::number(value));
782 }
783
784 PassRefPtr<HTMLCollection> HTMLElement::children()
785 {
786     return HTMLCollection::create(this, NodeChildren);
787 }
788
789 bool HTMLElement::rendererIsNeeded(const NodeRenderingContext& context)
790 {
791     if (hasLocalName(noscriptTag)) {
792         Frame* frame = document()->frame();
793 #if ENABLE(XHTMLMP)
794         if (!document()->shouldProcessNoscriptElement())
795             return false;
796 #else
797         if (frame && frame->script()->canExecuteScripts(NotAboutToExecuteScript))
798             return false;
799 #endif        
800     } else if (hasLocalName(noembedTag)) {
801         Frame* frame = document()->frame();
802         if (frame && frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin))
803             return false;
804     }
805     return StyledElement::rendererIsNeeded(context);
806 }
807
808 RenderObject* HTMLElement::createRenderer(RenderArena* arena, RenderStyle* style)
809 {
810     if (hasLocalName(wbrTag))
811         return new (arena) RenderWordBreak(this);
812     return RenderObject::createObject(this, style);
813 }
814
815 HTMLFormElement* HTMLElement::findFormAncestor() const
816 {
817     for (ContainerNode* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
818         if (ancestor->hasTagName(formTag))
819             return static_cast<HTMLFormElement*>(ancestor);
820     }
821     return 0;
822 }
823
824 HTMLFormElement* HTMLElement::virtualForm() const
825 {
826     return findFormAncestor();
827 }
828
829 static void setHasDirAutoFlagRecursively(Node* firstNode, bool flag, Node* lastNode = 0)
830 {
831     firstNode->setSelfOrAncestorHasDirAutoAttribute(flag);
832
833     Node* node = firstNode->firstChild();
834
835     while (node) {
836         if (node->selfOrAncestorHasDirAutoAttribute() == flag)
837             return;
838
839         if (node->isHTMLElement() && toElement(node)->hasAttribute(dirAttr)) {
840             if (node == lastNode)
841                 return;
842             node = node->traverseNextSibling(firstNode);
843             continue;
844         }
845         node->setSelfOrAncestorHasDirAutoAttribute(flag);
846         if (node == lastNode)
847             return;
848         node = node->traverseNextNode(firstNode);
849     }
850 }
851
852 void HTMLElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
853 {
854     StyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
855     adjustDirectionalityIfNeededAfterChildrenChanged(beforeChange, childCountDelta);
856 }
857
858 TextDirection HTMLElement::directionalityIfhasDirAutoAttribute(bool& isAuto) const
859 {
860     if (!(selfOrAncestorHasDirAutoAttribute() && equalIgnoringCase(getAttribute(dirAttr), "auto"))) {
861         isAuto = false;
862         return LTR;
863     }
864
865     isAuto = true;
866     return directionality();
867 }
868
869 TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) const
870 {
871     if (HTMLTextFormControlElement* textElement = toTextFormControl(const_cast<HTMLElement*>(this))) {
872         bool hasStrongDirectionality;
873         Unicode::Direction textDirection = textElement->value().defaultWritingDirection(&hasStrongDirectionality);
874         if (hasStrongDirectionality && strongDirectionalityTextNode)
875             *strongDirectionalityTextNode = textElement;
876         return (textDirection == Unicode::LeftToRight) ? LTR : RTL;
877     }
878
879     Node* node = firstChild();
880     while (node) {
881         // Skip bdi, script, style and text form controls.
882         if (equalIgnoringCase(node->nodeName(), "bdi") || node->hasTagName(scriptTag) || node->hasTagName(styleTag) 
883             || (node->isElementNode() && toElement(node)->isTextFormControl())) {
884             node = node->traverseNextSibling(this);
885             continue;
886         }
887
888         // Skip elements with valid dir attribute
889         if (node->isElementNode()) {
890             AtomicString dirAttributeValue = toElement(node)->fastGetAttribute(dirAttr);
891             if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr") || equalIgnoringCase(dirAttributeValue, "auto")) {
892                 node = node->traverseNextSibling(this);
893                 continue;
894             }
895         }
896
897         if (node->isTextNode()) {
898             bool hasStrongDirectionality;
899             WTF::Unicode::Direction textDirection = node->textContent(true).defaultWritingDirection(&hasStrongDirectionality);
900             if (hasStrongDirectionality) {
901                 if (strongDirectionalityTextNode)
902                     *strongDirectionalityTextNode = node;
903                 return (textDirection == WTF::Unicode::LeftToRight) ? LTR : RTL;
904             }
905         }
906         node = node->traverseNextNode(this);
907     }
908     if (strongDirectionalityTextNode)
909         *strongDirectionalityTextNode = 0;
910     return LTR;
911 }
912
913 void HTMLElement::dirAttributeChanged(Attribute* attribute)
914 {
915     Element* parent = parentElement();
916
917     if (parent && parent->isHTMLElement() && parent->selfOrAncestorHasDirAutoAttribute())
918         toHTMLElement(parent)->adjustDirectionalityIfNeededAfterChildAttributeChanged(this);
919
920     if (equalIgnoringCase(attribute->value(), "auto"))
921         calculateAndAdjustDirectionality();
922 }
923
924 void HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child)
925 {
926     ASSERT(selfOrAncestorHasDirAutoAttribute());
927     Node* strongDirectionalityTextNode;
928     TextDirection textDirection = directionality(&strongDirectionalityTextNode);
929     setHasDirAutoFlagRecursively(child, false);
930     if (renderer() && renderer()->style() && renderer()->style()->direction() != textDirection) {
931         Element* elementToAdjust = this;
932         for (; elementToAdjust; elementToAdjust = elementToAdjust->parentElement()) {
933             if (elementToAdjust->hasAttribute(dirAttr)) {
934                 elementToAdjust->setNeedsStyleRecalc();
935                 return;
936             }
937         }
938     }
939 }
940
941 void HTMLElement::calculateAndAdjustDirectionality()
942 {
943     Node* strongDirectionalityTextNode;
944     TextDirection textDirection = directionality(&strongDirectionalityTextNode);
945     setHasDirAutoFlagRecursively(this, true, strongDirectionalityTextNode);
946     if (renderer() && renderer()->style() && renderer()->style()->direction() != textDirection)
947         setNeedsStyleRecalc();
948 }
949
950 void HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged(Node* beforeChange, int childCountDelta)
951 {
952     if ((!document() || document()->renderer()) && childCountDelta < 0) {
953         Node* node = beforeChange ? beforeChange->traverseNextSibling() : 0;
954         for (int counter = 0; node && counter < childCountDelta; counter++, node = node->traverseNextSibling()) {
955             if (node->isElementNode() && toElement(node)->hasAttribute(dirAttr))
956                 continue;
957
958             setHasDirAutoFlagRecursively(node, false);
959         }
960     }
961
962     if (!selfOrAncestorHasDirAutoAttribute())
963         return;
964
965     Node* oldMarkedNode = beforeChange ? beforeChange->traverseNextSibling() : 0;
966     while (oldMarkedNode && oldMarkedNode->isHTMLElement() && toHTMLElement(oldMarkedNode)->hasAttribute(dirAttr))
967         oldMarkedNode = oldMarkedNode->traverseNextSibling(this);
968     if (oldMarkedNode)
969         setHasDirAutoFlagRecursively(oldMarkedNode, false);
970
971     for (Element* elementToAdjust = this; elementToAdjust; elementToAdjust = elementToAdjust->parentElement()) {
972         if (elementToAdjust->isHTMLElement() && elementToAdjust->hasAttribute(dirAttr)) {
973             toHTMLElement(elementToAdjust)->calculateAndAdjustDirectionality();
974             return;
975         }
976     }
977 }
978
979 } // namespace WebCore
980
981 #ifndef NDEBUG
982
983 // For use in the debugger
984 void dumpInnerHTML(WebCore::HTMLElement*);
985
986 void dumpInnerHTML(WebCore::HTMLElement* element)
987 {
988     printf("%s\n", element->innerHTML().ascii().data());
989 }
990 #endif