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