Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) 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 "CSSPropertyNames.h"
     28 #include "CSSValueKeywords.h"
     29 #include "DocumentFragment.h"
     30 #include "Event.h"
     31 #include "EventListener.h"
     32 #include "EventNames.h"
     33 #include "ExceptionCode.h"
     34 #include "Frame.h"
     35 #include "HTMLBRElement.h"
     36 #include "HTMLCollection.h"
     37 #include "HTMLDocument.h"
     38 #include "HTMLElementFactory.h"
     39 #include "HTMLFormElement.h"
     40 #include "HTMLNames.h"
     41 #include "HTMLTokenizer.h"
     42 #include "MappedAttribute.h"
     43 #include "RenderWordBreak.h"
     44 #include "ScriptEventListener.h"
     45 #include "Settings.h"
     46 #include "Text.h"
     47 #include "TextIterator.h"
     48 #include "XMLTokenizer.h"
     49 #include "markup.h"
     50 #include <wtf/StdLibExtras.h>
     51 
     52 namespace WebCore {
     53 
     54 using namespace HTMLNames;
     55 
     56 using std::min;
     57 using std::max;
     58 
     59 PassRefPtr<HTMLElement> HTMLElement::create(const QualifiedName& tagName, Document* document)
     60 {
     61     return adoptRef(new HTMLElement(tagName, document, CreateElement));
     62 }
     63 
     64 String HTMLElement::nodeName() const
     65 {
     66     // FIXME: Would be nice to have an atomicstring lookup based off uppercase chars that does not have to copy
     67     // the string on a hit in the hash.
     68     // FIXME: We should have a way to detect XHTML elements and replace the hasPrefix() check with it.
     69     if (document()->isHTMLDocument() && !tagQName().hasPrefix())
     70         return tagQName().localNameUpper();
     71     return Element::nodeName();
     72 }
     73 
     74 HTMLTagStatus HTMLElement::endTagRequirement() const
     75 {
     76     if (hasLocalName(wbrTag))
     77         return TagStatusForbidden;
     78     if (hasLocalName(dtTag) || hasLocalName(ddTag) || hasLocalName(rpTag) || hasLocalName(rtTag))
     79         return TagStatusOptional;
     80 
     81     // Same values as <span>.  This way custom tag name elements will behave like inline spans.
     82     return TagStatusRequired;
     83 }
     84 
     85 struct Empty1IntHashTraits : HashTraits<int> {
     86     static const bool emptyValueIsZero = false;
     87     static int emptyValue() { return 1; }
     88 };
     89 typedef HashMap<AtomicStringImpl*, int, DefaultHash<AtomicStringImpl*>::Hash, HashTraits<AtomicStringImpl*>, Empty1IntHashTraits> TagPriorityMap;
     90 
     91 static const TagPriorityMap* createTagPriorityMap()
     92 {
     93     TagPriorityMap* map = new TagPriorityMap;
     94 
     95     map->add(wbrTag.localName().impl(), 0);
     96 
     97     map->add(addressTag.localName().impl(), 3);
     98     map->add(ddTag.localName().impl(), 3);
     99     map->add(dtTag.localName().impl(), 3);
    100     map->add(noscriptTag.localName().impl(), 3);
    101     map->add(rpTag.localName().impl(), 3);
    102     map->add(rtTag.localName().impl(), 3);
    103 
    104     // 5 is same as <div>'s priority.
    105     map->add(articleTag.localName().impl(), 5);
    106     map->add(asideTag.localName().impl(), 5);
    107     map->add(centerTag.localName().impl(), 5);
    108     map->add(footerTag.localName().impl(), 5);
    109     map->add(headerTag.localName().impl(), 5);
    110     map->add(nobrTag.localName().impl(), 5);
    111     map->add(rubyTag.localName().impl(), 5);
    112     map->add(navTag.localName().impl(), 5);
    113     map->add(sectionTag.localName().impl(), 5);
    114 
    115     map->add(noembedTag.localName().impl(), 10);
    116     map->add(noframesTag.localName().impl(), 10);
    117 
    118     // TagPriorityMap returns 1 for unregistered tags. It's same as <span>.
    119     // This way custom tag name elements will behave like inline spans.
    120     return map;
    121 }
    122 
    123 int HTMLElement::tagPriority() const
    124 {
    125     static const TagPriorityMap* tagPriorityMap = createTagPriorityMap();
    126     return tagPriorityMap->get(localName().impl());
    127 }
    128 
    129 bool HTMLElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
    130 {
    131     if (attrName == alignAttr ||
    132         attrName == contenteditableAttr) {
    133         result = eUniversal;
    134         return false;
    135     }
    136     if (attrName == dirAttr) {
    137         result = hasLocalName(bdoTag) ? eBDO : eUniversal;
    138         return false;
    139     }
    140 
    141     return StyledElement::mapToEntry(attrName, result);
    142 }
    143 
    144 void HTMLElement::parseMappedAttribute(MappedAttribute *attr)
    145 {
    146     if (attr->name() == idAttributeName() || attr->name() == classAttr || attr->name() == styleAttr)
    147         return StyledElement::parseMappedAttribute(attr);
    148 
    149     String indexstring;
    150     if (attr->name() == alignAttr) {
    151         if (equalIgnoringCase(attr->value(), "middle"))
    152             addCSSProperty(attr, CSSPropertyTextAlign, "center");
    153         else
    154             addCSSProperty(attr, CSSPropertyTextAlign, attr->value());
    155     } else if (attr->name() == contenteditableAttr) {
    156         setContentEditable(attr);
    157     } else if (attr->name() == tabindexAttr) {
    158         indexstring = getAttribute(tabindexAttr);
    159         if (indexstring.length()) {
    160             bool parsedOK;
    161             int tabindex = indexstring.toIntStrict(&parsedOK);
    162             if (parsedOK)
    163                 // Clamp tabindex to the range of 'short' to match Firefox's behavior.
    164                 setTabIndexExplicitly(max(static_cast<int>(std::numeric_limits<short>::min()), min(tabindex, static_cast<int>(std::numeric_limits<short>::max()))));
    165         }
    166     } else if (attr->name() == langAttr) {
    167         // FIXME: Implement
    168     } else if (attr->name() == dirAttr) {
    169         addCSSProperty(attr, CSSPropertyDirection, attr->value());
    170         addCSSProperty(attr, CSSPropertyUnicodeBidi, hasLocalName(bdoTag) ? CSSValueBidiOverride : CSSValueEmbed);
    171     } else if (attr->name() == draggableAttr) {
    172         const AtomicString& value = attr->value();
    173         if (equalIgnoringCase(value, "true")) {
    174             addCSSProperty(attr, CSSPropertyWebkitUserDrag, CSSValueElement);
    175             addCSSProperty(attr, CSSPropertyWebkitUserSelect, CSSValueNone);
    176         } else if (equalIgnoringCase(value, "false"))
    177             addCSSProperty(attr, CSSPropertyWebkitUserDrag, CSSValueNone);
    178     }
    179 // standard events
    180     else if (attr->name() == onclickAttr) {
    181         setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, attr));
    182     } else if (attr->name() == oncontextmenuAttr) {
    183         setAttributeEventListener(eventNames().contextmenuEvent, createAttributeEventListener(this, attr));
    184     } else if (attr->name() == ondblclickAttr) {
    185         setAttributeEventListener(eventNames().dblclickEvent, createAttributeEventListener(this, attr));
    186     } else if (attr->name() == onmousedownAttr) {
    187         setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, attr));
    188     } else if (attr->name() == onmousemoveAttr) {
    189         setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, attr));
    190     } else if (attr->name() == onmouseoutAttr) {
    191         setAttributeEventListener(eventNames().mouseoutEvent, createAttributeEventListener(this, attr));
    192     } else if (attr->name() == onmouseoverAttr) {
    193         setAttributeEventListener(eventNames().mouseoverEvent, createAttributeEventListener(this, attr));
    194     } else if (attr->name() == onmouseupAttr) {
    195         setAttributeEventListener(eventNames().mouseupEvent, createAttributeEventListener(this, attr));
    196     } else if (attr->name() == onmousewheelAttr) {
    197         setAttributeEventListener(eventNames().mousewheelEvent, createAttributeEventListener(this, attr));
    198     } else if (attr->name() == onfocusAttr) {
    199         setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr));
    200     } else if (attr->name() == onblurAttr) {
    201         setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr));
    202     } else if (attr->name() == onkeydownAttr) {
    203         setAttributeEventListener(eventNames().keydownEvent, createAttributeEventListener(this, attr));
    204     } else if (attr->name() == onkeypressAttr) {
    205         setAttributeEventListener(eventNames().keypressEvent, createAttributeEventListener(this, attr));
    206     } else if (attr->name() == onkeyupAttr) {
    207         setAttributeEventListener(eventNames().keyupEvent, createAttributeEventListener(this, attr));
    208     } else if (attr->name() == onscrollAttr) {
    209         setAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(this, attr));
    210     } else if (attr->name() == onbeforecutAttr) {
    211         setAttributeEventListener(eventNames().beforecutEvent, createAttributeEventListener(this, attr));
    212     } else if (attr->name() == oncutAttr) {
    213         setAttributeEventListener(eventNames().cutEvent, createAttributeEventListener(this, attr));
    214     } else if (attr->name() == onbeforecopyAttr) {
    215         setAttributeEventListener(eventNames().beforecopyEvent, createAttributeEventListener(this, attr));
    216     } else if (attr->name() == oncopyAttr) {
    217         setAttributeEventListener(eventNames().copyEvent, createAttributeEventListener(this, attr));
    218     } else if (attr->name() == onbeforepasteAttr) {
    219         setAttributeEventListener(eventNames().beforepasteEvent, createAttributeEventListener(this, attr));
    220     } else if (attr->name() == onpasteAttr) {
    221         setAttributeEventListener(eventNames().pasteEvent, createAttributeEventListener(this, attr));
    222     } else if (attr->name() == ondragenterAttr) {
    223         setAttributeEventListener(eventNames().dragenterEvent, createAttributeEventListener(this, attr));
    224     } else if (attr->name() == ondragoverAttr) {
    225         setAttributeEventListener(eventNames().dragoverEvent, createAttributeEventListener(this, attr));
    226     } else if (attr->name() == ondragleaveAttr) {
    227         setAttributeEventListener(eventNames().dragleaveEvent, createAttributeEventListener(this, attr));
    228     } else if (attr->name() == ondropAttr) {
    229         setAttributeEventListener(eventNames().dropEvent, createAttributeEventListener(this, attr));
    230     } else if (attr->name() == ondragstartAttr) {
    231         setAttributeEventListener(eventNames().dragstartEvent, createAttributeEventListener(this, attr));
    232     } else if (attr->name() == ondragAttr) {
    233         setAttributeEventListener(eventNames().dragEvent, createAttributeEventListener(this, attr));
    234     } else if (attr->name() == ondragendAttr) {
    235         setAttributeEventListener(eventNames().dragendEvent, createAttributeEventListener(this, attr));
    236     } else if (attr->name() == onselectstartAttr) {
    237         setAttributeEventListener(eventNames().selectstartEvent, createAttributeEventListener(this, attr));
    238     } else if (attr->name() == onsubmitAttr) {
    239         setAttributeEventListener(eventNames().submitEvent, createAttributeEventListener(this, attr));
    240     } else if (attr->name() == onerrorAttr) {
    241         setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr));
    242     } else if (attr->name() == onwebkitanimationstartAttr) {
    243         setAttributeEventListener(eventNames().webkitAnimationStartEvent, createAttributeEventListener(this, attr));
    244     } else if (attr->name() == onwebkitanimationiterationAttr) {
    245         setAttributeEventListener(eventNames().webkitAnimationIterationEvent, createAttributeEventListener(this, attr));
    246     } else if (attr->name() == onwebkitanimationendAttr) {
    247         setAttributeEventListener(eventNames().webkitAnimationEndEvent, createAttributeEventListener(this, attr));
    248     } else if (attr->name() == onwebkittransitionendAttr) {
    249         setAttributeEventListener(eventNames().webkitTransitionEndEvent, createAttributeEventListener(this, attr));
    250     } else if (attr->name() == oninputAttr) {
    251         setAttributeEventListener(eventNames().inputEvent, createAttributeEventListener(this, attr));
    252     } else if (attr->name() == oninvalidAttr) {
    253         setAttributeEventListener(eventNames().invalidEvent, createAttributeEventListener(this, attr));
    254     } else if (attr->name() == ontouchstartAttr) {
    255         setAttributeEventListener(eventNames().touchstartEvent, createAttributeEventListener(this, attr));
    256     } else if (attr->name() == ontouchmoveAttr) {
    257         setAttributeEventListener(eventNames().touchmoveEvent, createAttributeEventListener(this, attr));
    258     } else if (attr->name() == ontouchendAttr) {
    259         setAttributeEventListener(eventNames().touchendEvent, createAttributeEventListener(this, attr));
    260     } else if (attr->name() == ontouchcancelAttr) {
    261         setAttributeEventListener(eventNames().touchcancelEvent, createAttributeEventListener(this, attr));
    262     }
    263 }
    264 
    265 String HTMLElement::innerHTML() const
    266 {
    267     return createMarkup(this, ChildrenOnly);
    268 }
    269 
    270 String HTMLElement::outerHTML() const
    271 {
    272     return createMarkup(this);
    273 }
    274 
    275 PassRefPtr<DocumentFragment> HTMLElement::createContextualFragment(const String &html, FragmentScriptingPermission scriptingPermission)
    276 {
    277     // the following is in accordance with the definition as used by IE
    278     if (endTagRequirement() == TagStatusForbidden)
    279         return 0;
    280 
    281     if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
    282         hasLocalName(headTag) || hasLocalName(styleTag) || hasLocalName(titleTag))
    283         return 0;
    284 
    285     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document());
    286 
    287     if (document()->isHTMLDocument())
    288          parseHTMLDocumentFragment(html, fragment.get(), scriptingPermission);
    289     else {
    290         if (!parseXMLDocumentFragment(html, fragment.get(), this, scriptingPermission))
    291             // FIXME: We should propagate a syntax error exception out here.
    292             return 0;
    293     }
    294 
    295     // Exceptions are ignored because none ought to happen here.
    296     int ignoredExceptionCode;
    297 
    298     // we need to pop <html> and <body> elements and remove <head> to
    299     // accommodate folks passing complete HTML documents to make the
    300     // child of an element.
    301 
    302     RefPtr<Node> nextNode;
    303     for (RefPtr<Node> node = fragment->firstChild(); node; node = nextNode) {
    304         nextNode = node->nextSibling();
    305         if (node->hasTagName(htmlTag) || node->hasTagName(bodyTag)) {
    306             Node *firstChild = node->firstChild();
    307             if (firstChild)
    308                 nextNode = firstChild;
    309             RefPtr<Node> nextChild;
    310             for (RefPtr<Node> child = firstChild; child; child = nextChild) {
    311                 nextChild = child->nextSibling();
    312                 node->removeChild(child.get(), ignoredExceptionCode);
    313                 ASSERT(!ignoredExceptionCode);
    314                 fragment->insertBefore(child, node.get(), ignoredExceptionCode);
    315                 ASSERT(!ignoredExceptionCode);
    316             }
    317             fragment->removeChild(node.get(), ignoredExceptionCode);
    318             ASSERT(!ignoredExceptionCode);
    319         } else if (node->hasTagName(headTag)) {
    320             fragment->removeChild(node.get(), ignoredExceptionCode);
    321             ASSERT(!ignoredExceptionCode);
    322         }
    323     }
    324 
    325     return fragment.release();
    326 }
    327 
    328 static inline bool hasOneChild(ContainerNode* node)
    329 {
    330     Node* firstChild = node->firstChild();
    331     return firstChild && !firstChild->nextSibling();
    332 }
    333 
    334 static inline bool hasOneTextChild(ContainerNode* node)
    335 {
    336     return hasOneChild(node) && node->firstChild()->isTextNode();
    337 }
    338 
    339 static void replaceChildrenWithFragment(HTMLElement* element, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec)
    340 {
    341     if (!fragment->firstChild()) {
    342         element->removeChildren();
    343         return;
    344     }
    345 
    346     if (hasOneTextChild(element) && hasOneTextChild(fragment.get())) {
    347         static_cast<Text*>(element->firstChild())->setData(static_cast<Text*>(fragment->firstChild())->data(), ec);
    348         return;
    349     }
    350 
    351     if (hasOneChild(element)) {
    352         element->replaceChild(fragment, element->firstChild(), ec);
    353         return;
    354     }
    355 
    356     element->removeChildren();
    357     element->appendChild(fragment, ec);
    358 }
    359 
    360 static void replaceChildrenWithText(HTMLElement* element, const String& text, ExceptionCode& ec)
    361 {
    362     if (hasOneTextChild(element)) {
    363         static_cast<Text*>(element->firstChild())->setData(text, ec);
    364         return;
    365     }
    366 
    367     RefPtr<Text> textNode = Text::create(element->document(), text);
    368 
    369     if (hasOneChild(element)) {
    370         element->replaceChild(textNode.release(), element->firstChild(), ec);
    371         return;
    372     }
    373 
    374     element->removeChildren();
    375     element->appendChild(textNode.release(), ec);
    376 }
    377 
    378 void HTMLElement::setInnerHTML(const String& html, ExceptionCode& ec)
    379 {
    380     if (hasLocalName(scriptTag) || hasLocalName(styleTag)) {
    381         // Script and CSS source shouldn't be parsed as HTML.
    382         removeChildren();
    383         appendChild(document()->createTextNode(html), ec);
    384         return;
    385     }
    386 
    387     RefPtr<DocumentFragment> fragment = createContextualFragment(html);
    388     if (!fragment) {
    389         ec = NO_MODIFICATION_ALLOWED_ERR;
    390         return;
    391     }
    392 
    393     replaceChildrenWithFragment(this, fragment.release(), ec);
    394 }
    395 
    396 void HTMLElement::setOuterHTML(const String& html, ExceptionCode& ec)
    397 {
    398     Node* p = parent();
    399     if (!p || !p->isHTMLElement()) {
    400         ec = NO_MODIFICATION_ALLOWED_ERR;
    401         return;
    402     }
    403 
    404     HTMLElement* parent = static_cast<HTMLElement*>(p);
    405     RefPtr<DocumentFragment> fragment = parent->createContextualFragment(html);
    406     if (!fragment) {
    407         ec = NO_MODIFICATION_ALLOWED_ERR;
    408         return;
    409     }
    410 
    411     // FIXME: Why doesn't this have code to merge neighboring text nodes the way setOuterText does?
    412 
    413     parent->replaceChild(fragment.release(), this, ec);
    414 }
    415 
    416 void HTMLElement::setInnerText(const String& text, ExceptionCode& ec)
    417 {
    418     // follow the IE specs about when this is allowed
    419     if (endTagRequirement() == TagStatusForbidden) {
    420         ec = NO_MODIFICATION_ALLOWED_ERR;
    421         return;
    422     }
    423     if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
    424         hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) ||
    425         hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) ||
    426         hasLocalName(trTag)) {
    427         ec = NO_MODIFICATION_ALLOWED_ERR;
    428         return;
    429     }
    430 
    431     // FIXME: This doesn't take whitespace collapsing into account at all.
    432 
    433     if (!text.contains('\n') && !text.contains('\r')) {
    434         if (text.isEmpty()) {
    435             removeChildren();
    436             return;
    437         }
    438         replaceChildrenWithText(this, text, ec);
    439         return;
    440     }
    441 
    442     // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
    443     // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded?
    444     // For example, for the contents of textarea elements that are display:none?
    445     RenderObject* r = renderer();
    446     if (r && r->style()->preserveNewline()) {
    447         if (!text.contains('\r')) {
    448             replaceChildrenWithText(this, text, ec);
    449             return;
    450         }
    451         String textWithConsistentLineBreaks = text;
    452         textWithConsistentLineBreaks.replace("\r\n", "\n");
    453         textWithConsistentLineBreaks.replace('\r', '\n');
    454         replaceChildrenWithText(this, textWithConsistentLineBreaks, ec);
    455         return;
    456     }
    457 
    458     // Add text nodes and <br> elements.
    459     ec = 0;
    460     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document());
    461     int lineStart = 0;
    462     UChar prev = 0;
    463     int length = text.length();
    464     for (int i = 0; i < length; ++i) {
    465         UChar c = text[i];
    466         if (c == '\n' || c == '\r') {
    467             if (i > lineStart) {
    468                 fragment->appendChild(Text::create(document(), text.substring(lineStart, i - lineStart)), ec);
    469                 if (ec)
    470                     return;
    471             }
    472             if (!(c == '\n' && i != 0 && prev == '\r')) {
    473                 fragment->appendChild(new HTMLBRElement(brTag, document()), ec);
    474                 if (ec)
    475                     return;
    476             }
    477             lineStart = i + 1;
    478         }
    479         prev = c;
    480     }
    481     if (length > lineStart)
    482         fragment->appendChild(Text::create(document(), text.substring(lineStart, length - lineStart)), ec);
    483     replaceChildrenWithFragment(this, fragment.release(), ec);
    484 }
    485 
    486 void HTMLElement::setOuterText(const String &text, ExceptionCode& ec)
    487 {
    488     // follow the IE specs about when this is allowed
    489     if (endTagRequirement() == TagStatusForbidden) {
    490         ec = NO_MODIFICATION_ALLOWED_ERR;
    491         return;
    492     }
    493     if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) ||
    494         hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) ||
    495         hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) ||
    496         hasLocalName(trTag)) {
    497         ec = NO_MODIFICATION_ALLOWED_ERR;
    498         return;
    499     }
    500 
    501     Node* parent = parentNode();
    502     if (!parent) {
    503         ec = NO_MODIFICATION_ALLOWED_ERR;
    504         return;
    505     }
    506 
    507     // FIXME: This creates a new text node even when the text is empty.
    508     // FIXME: This creates a single text node even when the text has CR and LF
    509     // characters in it. Instead it should create <br> elements.
    510     RefPtr<Text> t = Text::create(document(), text);
    511     ec = 0;
    512     parent->replaceChild(t, this, ec);
    513     if (ec)
    514         return;
    515 
    516     // is previous node a text node? if so, merge into it
    517     Node* prev = t->previousSibling();
    518     if (prev && prev->isTextNode()) {
    519         Text* textPrev = static_cast<Text*>(prev);
    520         textPrev->appendData(t->data(), ec);
    521         if (ec)
    522             return;
    523         t->remove(ec);
    524         if (ec)
    525             return;
    526         t = textPrev;
    527     }
    528 
    529     // is next node a text node? if so, merge it in
    530     Node* next = t->nextSibling();
    531     if (next && next->isTextNode()) {
    532         Text* textNext = static_cast<Text*>(next);
    533         t->appendData(textNext->data(), ec);
    534         if (ec)
    535             return;
    536         textNext->remove(ec);
    537         if (ec)
    538             return;
    539     }
    540 }
    541 
    542 Node* HTMLElement::insertAdjacent(const String& where, Node* newChild, ExceptionCode& ec)
    543 {
    544     // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd",
    545     // a document fragment is created and the elements appended in the correct order. This document
    546     // fragment isn't returned anywhere.
    547     //
    548     // This is impossible for us to implement as the DOM tree does not allow for such structures,
    549     // Opera also appears to disallow such usage.
    550 
    551     if (equalIgnoringCase(where, "beforeBegin")) {
    552         if (Node* p = parent())
    553             return p->insertBefore(newChild, this, ec) ? newChild : 0;
    554         return 0;
    555     }
    556 
    557     if (equalIgnoringCase(where, "afterBegin"))
    558         return insertBefore(newChild, firstChild(), ec) ? newChild : 0;
    559 
    560     if (equalIgnoringCase(where, "beforeEnd"))
    561         return appendChild(newChild, ec) ? newChild : 0;
    562 
    563     if (equalIgnoringCase(where, "afterEnd")) {
    564         if (Node* p = parent())
    565             return p->insertBefore(newChild, nextSibling(), ec) ? newChild : 0;
    566         return 0;
    567     }
    568 
    569     // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative
    570     ec = NOT_SUPPORTED_ERR;
    571     return 0;
    572 }
    573 
    574 Element* HTMLElement::insertAdjacentElement(const String& where, Element* newChild, ExceptionCode& ec)
    575 {
    576     if (!newChild) {
    577         // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative
    578         ec = TYPE_MISMATCH_ERR;
    579         return 0;
    580     }
    581 
    582     Node* returnValue = insertAdjacent(where, newChild, ec);
    583     ASSERT(!returnValue || returnValue->isElementNode());
    584     return static_cast<Element*>(returnValue);
    585 }
    586 
    587 void HTMLElement::insertAdjacentHTML(const String& where, const String& html, ExceptionCode& ec)
    588 {
    589     RefPtr<DocumentFragment> fragment = document()->createDocumentFragment();
    590     if (document()->isHTMLDocument())
    591          parseHTMLDocumentFragment(html, fragment.get());
    592     else {
    593         if (!parseXMLDocumentFragment(html, fragment.get(), this))
    594             // FIXME: We should propagate a syntax error exception out here.
    595             return;
    596     }
    597 
    598     insertAdjacent(where, fragment.get(), ec);
    599 }
    600 
    601 void HTMLElement::insertAdjacentText(const String& where, const String& text, ExceptionCode& ec)
    602 {
    603     RefPtr<Text> textNode = document()->createTextNode(text);
    604     insertAdjacent(where, textNode.get(), ec);
    605 }
    606 
    607 void HTMLElement::addHTMLAlignment(MappedAttribute* attr)
    608 {
    609     addHTMLAlignmentToStyledElement(this, attr);
    610 }
    611 
    612 void HTMLElement::addHTMLAlignmentToStyledElement(StyledElement* element, MappedAttribute* attr)
    613 {
    614     // vertical alignment with respect to the current baseline of the text
    615     // right or left means floating images
    616     int floatValue = CSSValueInvalid;
    617     int verticalAlignValue = CSSValueInvalid;
    618 
    619     const AtomicString& alignment = attr->value();
    620     if (equalIgnoringCase(alignment, "absmiddle"))
    621         verticalAlignValue = CSSValueMiddle;
    622     else if (equalIgnoringCase(alignment, "absbottom"))
    623         verticalAlignValue = CSSValueBottom;
    624     else if (equalIgnoringCase(alignment, "left")) {
    625         floatValue = CSSValueLeft;
    626         verticalAlignValue = CSSValueTop;
    627     } else if (equalIgnoringCase(alignment, "right")) {
    628         floatValue = CSSValueRight;
    629         verticalAlignValue = CSSValueTop;
    630     } else if (equalIgnoringCase(alignment, "top"))
    631         verticalAlignValue = CSSValueTop;
    632     else if (equalIgnoringCase(alignment, "middle"))
    633         verticalAlignValue = CSSValueWebkitBaselineMiddle;
    634     else if (equalIgnoringCase(alignment, "center"))
    635         verticalAlignValue = CSSValueMiddle;
    636     else if (equalIgnoringCase(alignment, "bottom"))
    637         verticalAlignValue = CSSValueBaseline;
    638     else if (equalIgnoringCase(alignment, "texttop"))
    639         verticalAlignValue = CSSValueTextTop;
    640 
    641     if (floatValue != CSSValueInvalid)
    642         element->addCSSProperty(attr, CSSPropertyFloat, floatValue);
    643 
    644     if (verticalAlignValue != CSSValueInvalid)
    645         element->addCSSProperty(attr, CSSPropertyVerticalAlign, verticalAlignValue);
    646 }
    647 
    648 bool HTMLElement::supportsFocus() const
    649 {
    650     return Element::supportsFocus() || (isContentEditable() && parent() && !parent()->isContentEditable());
    651 }
    652 
    653 bool HTMLElement::isContentEditable() const
    654 {
    655     if (document()->frame() && document()->frame()->isContentEditable())
    656         return true;
    657 
    658     // FIXME: this is a terrible thing to do here:
    659     // https://bugs.webkit.org/show_bug.cgi?id=21834
    660     document()->updateStyleIfNeeded();
    661 
    662     if (!renderer()) {
    663         if (parentNode())
    664             return parentNode()->isContentEditable();
    665         else
    666             return false;
    667     }
    668 
    669     return renderer()->style()->userModify() == READ_WRITE || renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY;
    670 }
    671 
    672 bool HTMLElement::isContentRichlyEditable() const
    673 {
    674     if (document()->frame() && document()->frame()->isContentEditable())
    675         return true;
    676 
    677     document()->updateStyleIfNeeded();
    678 
    679     if (!renderer()) {
    680         if (parentNode())
    681             return parentNode()->isContentEditable();
    682         else
    683             return false;
    684     }
    685 
    686     return renderer()->style()->userModify() == READ_WRITE;
    687 }
    688 
    689 String HTMLElement::contentEditable() const
    690 {
    691     document()->updateStyleIfNeeded();
    692 
    693     if (!renderer())
    694         return "false";
    695 
    696     switch (renderer()->style()->userModify()) {
    697         case READ_WRITE:
    698             return "true";
    699         case READ_ONLY:
    700             return "false";
    701         case READ_WRITE_PLAINTEXT_ONLY:
    702             return "plaintext-only";
    703         default:
    704             return "inherit";
    705     }
    706 }
    707 
    708 void HTMLElement::setContentEditable(MappedAttribute* attr)
    709 {
    710     const AtomicString& enabled = attr->value();
    711     if (enabled.isEmpty() || equalIgnoringCase(enabled, "true")) {
    712         addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadWrite);
    713         addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord);
    714         addCSSProperty(attr, CSSPropertyWebkitNbspMode, CSSValueSpace);
    715         addCSSProperty(attr, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
    716     } else if (equalIgnoringCase(enabled, "false")) {
    717         addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadOnly);
    718         attr->decl()->removeProperty(CSSPropertyWordWrap, false);
    719         attr->decl()->removeProperty(CSSPropertyWebkitNbspMode, false);
    720         attr->decl()->removeProperty(CSSPropertyWebkitLineBreak, false);
    721     } else if (equalIgnoringCase(enabled, "inherit")) {
    722         addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueInherit);
    723         attr->decl()->removeProperty(CSSPropertyWordWrap, false);
    724         attr->decl()->removeProperty(CSSPropertyWebkitNbspMode, false);
    725         attr->decl()->removeProperty(CSSPropertyWebkitLineBreak, false);
    726     } else if (equalIgnoringCase(enabled, "plaintext-only")) {
    727         addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadWritePlaintextOnly);
    728         addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord);
    729         addCSSProperty(attr, CSSPropertyWebkitNbspMode, CSSValueSpace);
    730         addCSSProperty(attr, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
    731     }
    732 }
    733 
    734 void HTMLElement::setContentEditable(const String &enabled)
    735 {
    736     if (enabled == "inherit") {
    737         ExceptionCode ec;
    738         removeAttribute(contenteditableAttr, ec);
    739     }
    740     else
    741         setAttribute(contenteditableAttr, enabled.isEmpty() ? "true" : enabled);
    742 }
    743 
    744 bool HTMLElement::draggable() const
    745 {
    746     return equalIgnoringCase(getAttribute(draggableAttr), "true");
    747 }
    748 
    749 void HTMLElement::setDraggable(bool value)
    750 {
    751     setAttribute(draggableAttr, value ? "true" : "false");
    752 }
    753 
    754 void HTMLElement::click()
    755 {
    756     dispatchSimulatedClick(0, false, false);
    757 }
    758 
    759 // accessKeyAction is used by the accessibility support code
    760 // to send events to elements that our JavaScript caller does
    761 // does not.  The elements JS is interested in have subclasses
    762 // that override this method to direct the click appropriately.
    763 // Here in the base class, then, we only send the click if
    764 // the caller wants it to go to any HTMLElement, and we say
    765 // to send the mouse events in addition to the click.
    766 void HTMLElement::accessKeyAction(bool sendToAnyElement)
    767 {
    768     if (sendToAnyElement)
    769         dispatchSimulatedClick(0, true);
    770 }
    771 
    772 String HTMLElement::title() const
    773 {
    774     return getAttribute(titleAttr);
    775 }
    776 
    777 short HTMLElement::tabIndex() const
    778 {
    779     if (supportsFocus())
    780         return Element::tabIndex();
    781     return -1;
    782 }
    783 
    784 void HTMLElement::setTabIndex(int value)
    785 {
    786     setAttribute(tabindexAttr, String::number(value));
    787 }
    788 
    789 PassRefPtr<HTMLCollection> HTMLElement::children()
    790 {
    791     return HTMLCollection::create(this, NodeChildren);
    792 }
    793 
    794 // DOM Section 1.1.1
    795 bool HTMLElement::childAllowed(Node *newChild)
    796 {
    797     if (!Element::childAllowed(newChild))
    798         return false;
    799 
    800     // For XML documents, we are non-validating and do not check against a DTD, even for HTML elements.
    801     if (!document()->isHTMLDocument())
    802         return true;
    803 
    804     // Future-proof for XML content inside HTML documents (we may allow this some day).
    805     if (newChild->isElementNode() && !newChild->isHTMLElement())
    806         return true;
    807 
    808     // Elements with forbidden tag status can never have children
    809     if (endTagRequirement() == TagStatusForbidden)
    810         return false;
    811 
    812     // Comment nodes are always allowed.
    813     if (newChild->isCommentNode())
    814         return true;
    815 
    816     // Now call checkDTD.
    817     return checkDTD(newChild);
    818 }
    819 
    820 // DTD Stuff
    821 // This unfortunate function is only needed when checking against the DTD.  Other languages (like SVG) won't need this.
    822 bool HTMLElement::isRecognizedTagName(const QualifiedName& tagName)
    823 {
    824     DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ());
    825     if (tagList.isEmpty()) {
    826         size_t tagCount = 0;
    827         WebCore::QualifiedName** tags = HTMLNames::getHTMLTags(&tagCount);
    828         for (size_t i = 0; i < tagCount; i++)
    829             tagList.add(tags[i]->localName().impl());
    830     }
    831     return tagList.contains(tagName.localName().impl());
    832 }
    833 
    834 // The terms inline and block are used here loosely.  Don't make the mistake of assuming all inlines or all blocks
    835 // need to be in these two lists.
    836 static HashSet<AtomicStringImpl*>* inlineTagList()
    837 {
    838     DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ());
    839     if (tagList.isEmpty()) {
    840         tagList.add(ttTag.localName().impl());
    841         tagList.add(iTag.localName().impl());
    842         tagList.add(bTag.localName().impl());
    843         tagList.add(uTag.localName().impl());
    844         tagList.add(sTag.localName().impl());
    845         tagList.add(strikeTag.localName().impl());
    846         tagList.add(bigTag.localName().impl());
    847         tagList.add(smallTag.localName().impl());
    848         tagList.add(emTag.localName().impl());
    849         tagList.add(strongTag.localName().impl());
    850         tagList.add(dfnTag.localName().impl());
    851         tagList.add(codeTag.localName().impl());
    852         tagList.add(sampTag.localName().impl());
    853         tagList.add(kbdTag.localName().impl());
    854         tagList.add(varTag.localName().impl());
    855         tagList.add(citeTag.localName().impl());
    856         tagList.add(abbrTag.localName().impl());
    857         tagList.add(acronymTag.localName().impl());
    858         tagList.add(aTag.localName().impl());
    859         tagList.add(canvasTag.localName().impl());
    860         tagList.add(imgTag.localName().impl());
    861         tagList.add(appletTag.localName().impl());
    862         tagList.add(objectTag.localName().impl());
    863         tagList.add(embedTag.localName().impl());
    864         tagList.add(fontTag.localName().impl());
    865         tagList.add(basefontTag.localName().impl());
    866         tagList.add(brTag.localName().impl());
    867         tagList.add(scriptTag.localName().impl());
    868         tagList.add(styleTag.localName().impl());
    869         tagList.add(linkTag.localName().impl());
    870         tagList.add(mapTag.localName().impl());
    871         tagList.add(qTag.localName().impl());
    872         tagList.add(subTag.localName().impl());
    873         tagList.add(supTag.localName().impl());
    874         tagList.add(spanTag.localName().impl());
    875         tagList.add(bdoTag.localName().impl());
    876         tagList.add(iframeTag.localName().impl());
    877         tagList.add(inputTag.localName().impl());
    878         tagList.add(keygenTag.localName().impl());
    879         tagList.add(selectTag.localName().impl());
    880         tagList.add(datagridTag.localName().impl());
    881         tagList.add(textareaTag.localName().impl());
    882         tagList.add(labelTag.localName().impl());
    883         tagList.add(buttonTag.localName().impl());
    884         tagList.add(datalistTag.localName().impl());
    885         tagList.add(insTag.localName().impl());
    886         tagList.add(delTag.localName().impl());
    887         tagList.add(nobrTag.localName().impl());
    888         tagList.add(wbrTag.localName().impl());
    889 #if ENABLE(VIDEO)
    890         tagList.add(audioTag.localName().impl());
    891         tagList.add(videoTag.localName().impl());
    892 #endif
    893         tagList.add(rpTag.localName().impl());
    894         tagList.add(rtTag.localName().impl());
    895         tagList.add(rubyTag.localName().impl());
    896     }
    897     return &tagList;
    898 }
    899 
    900 static HashSet<AtomicStringImpl*>* blockTagList()
    901 {
    902     DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ());
    903     if (tagList.isEmpty()) {
    904         tagList.add(addressTag.localName().impl());
    905         tagList.add(articleTag.localName().impl());
    906         tagList.add(asideTag.localName().impl());
    907         tagList.add(blockquoteTag.localName().impl());
    908         tagList.add(centerTag.localName().impl());
    909         tagList.add(ddTag.localName().impl());
    910         tagList.add(dirTag.localName().impl());
    911         tagList.add(divTag.localName().impl());
    912         tagList.add(dlTag.localName().impl());
    913         tagList.add(dtTag.localName().impl());
    914         tagList.add(fieldsetTag.localName().impl());
    915         tagList.add(footerTag.localName().impl());
    916         tagList.add(formTag.localName().impl());
    917         tagList.add(h1Tag.localName().impl());
    918         tagList.add(h2Tag.localName().impl());
    919         tagList.add(h3Tag.localName().impl());
    920         tagList.add(h4Tag.localName().impl());
    921         tagList.add(h5Tag.localName().impl());
    922         tagList.add(h6Tag.localName().impl());
    923         tagList.add(headerTag.localName().impl());
    924         tagList.add(hrTag.localName().impl());
    925         tagList.add(isindexTag.localName().impl());
    926         tagList.add(layerTag.localName().impl());
    927         tagList.add(liTag.localName().impl());
    928         tagList.add(listingTag.localName().impl());
    929         tagList.add(marqueeTag.localName().impl());
    930         tagList.add(menuTag.localName().impl());
    931         tagList.add(navTag.localName().impl());
    932         tagList.add(noembedTag.localName().impl());
    933         tagList.add(noframesTag.localName().impl());
    934         tagList.add(nolayerTag.localName().impl());
    935         tagList.add(noscriptTag.localName().impl());
    936         tagList.add(olTag.localName().impl());
    937         tagList.add(pTag.localName().impl());
    938         tagList.add(plaintextTag.localName().impl());
    939         tagList.add(preTag.localName().impl());
    940         tagList.add(sectionTag.localName().impl());
    941         tagList.add(tableTag.localName().impl());
    942         tagList.add(ulTag.localName().impl());
    943         tagList.add(xmpTag.localName().impl());
    944     }
    945     return &tagList;
    946 }
    947 
    948 bool HTMLElement::inEitherTagList(const Node* newChild)
    949 {
    950     if (newChild->isTextNode())
    951         return true;
    952 
    953     if (newChild->isHTMLElement()) {
    954         const HTMLElement* child = static_cast<const HTMLElement*>(newChild);
    955         if (inlineTagList()->contains(child->tagQName().localName().impl())) {
    956 #if PLATFORM(MAC)
    957             if (child->tagQName().localName() == styleTag) {
    958                 // Leopard Mail doesn't expect <style> to be in the body of the document, so don't allow it in that case.
    959                 // See <rdar://problem/6621310>
    960                 Settings* settings = newChild->document() ? newChild->document()->settings() : 0;
    961                 if (settings && settings->needsLeopardMailQuirks())
    962                     return false;
    963             }
    964 #endif
    965             return true;
    966         }
    967         if (blockTagList()->contains(child->tagQName().localName().impl()))
    968             return true;
    969         return !isRecognizedTagName(child->tagQName()); // Accept custom html tags
    970     }
    971 
    972     return false;
    973 }
    974 
    975 bool HTMLElement::inInlineTagList(const Node* newChild)
    976 {
    977     if (newChild->isTextNode())
    978         return true;
    979 
    980     if (newChild->isHTMLElement()) {
    981         const HTMLElement* child = static_cast<const HTMLElement*>(newChild);
    982         if (inlineTagList()->contains(child->tagQName().localName().impl()))
    983             return true;
    984         return !isRecognizedTagName(child->tagQName()); // Accept custom html tags
    985     }
    986 
    987     return false;
    988 }
    989 
    990 bool HTMLElement::inBlockTagList(const Node* newChild)
    991 {
    992     if (newChild->isTextNode())
    993         return true;
    994 
    995     if (newChild->isHTMLElement()) {
    996         const HTMLElement* child = static_cast<const HTMLElement*>(newChild);
    997         return (blockTagList()->contains(child->tagQName().localName().impl()));
    998     }
    999 
   1000     return false;
   1001 }
   1002 
   1003 bool HTMLElement::checkDTD(const Node* newChild)
   1004 {
   1005     if (hasLocalName(addressTag) && newChild->hasTagName(pTag))
   1006         return true;
   1007     return inEitherTagList(newChild);
   1008 }
   1009 
   1010 bool HTMLElement::rendererIsNeeded(RenderStyle *style)
   1011 {
   1012 #if !ENABLE(XHTMLMP)
   1013     if (hasLocalName(noscriptTag)) {
   1014         Settings* settings = document()->settings();
   1015         if (settings && settings->isJavaScriptEnabled())
   1016             return false;
   1017     }
   1018 #endif
   1019     return StyledElement::rendererIsNeeded(style);
   1020 }
   1021 
   1022 RenderObject* HTMLElement::createRenderer(RenderArena* arena, RenderStyle* style)
   1023 {
   1024     if (hasLocalName(wbrTag))
   1025         return new (arena) RenderWordBreak(this);
   1026     return RenderObject::createObject(this, style);
   1027 }
   1028 
   1029 HTMLFormElement* HTMLElement::findFormAncestor() const
   1030 {
   1031     for (Node* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode())
   1032         if (ancestor->hasTagName(formTag))
   1033             return static_cast<HTMLFormElement*>(ancestor);
   1034     return 0;
   1035 }
   1036 
   1037 HTMLFormElement* HTMLElement::virtualForm() const
   1038 {
   1039     return findFormAncestor();
   1040 }
   1041 
   1042 } // namespace WebCore
   1043 
   1044 #ifndef NDEBUG
   1045 
   1046 // For use in the debugger
   1047 void dumpInnerHTML(WebCore::HTMLElement*);
   1048 
   1049 void dumpInnerHTML(WebCore::HTMLElement* element)
   1050 {
   1051     printf("%s\n", element->innerHTML().ascii().data());
   1052 }
   1053 #endif
   1054