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/)
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.
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.
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.
25 #include "HTMLElement.h"
27 #include "CSSPropertyNames.h"
28 #include "CSSValueKeywords.h"
29 #include "DocumentFragment.h"
31 #include "EventListener.h"
32 #include "EventNames.h"
33 #include "ExceptionCode.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"
47 #include "TextIterator.h"
48 #include "XMLTokenizer.h"
50 #include <wtf/StdLibExtras.h>
54 using namespace HTMLNames
;
59 PassRefPtr
<HTMLElement
> HTMLElement::create(const QualifiedName
& tagName
, Document
* document
)
61 return adoptRef(new HTMLElement(tagName
, document
, CreateElement
));
64 String
HTMLElement::nodeName() const
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();
74 HTMLTagStatus
HTMLElement::endTagRequirement() const
76 if (hasLocalName(wbrTag
))
77 return TagStatusForbidden
;
78 if (hasLocalName(dtTag
) || hasLocalName(ddTag
) || hasLocalName(rpTag
) || hasLocalName(rtTag
))
79 return TagStatusOptional
;
81 // Same values as <span>. This way custom tag name elements will behave like inline spans.
82 return TagStatusRequired
;
85 int HTMLElement::tagPriority() const
87 if (hasLocalName(wbrTag
))
89 if (hasLocalName(addressTag
) || hasLocalName(ddTag
) || hasLocalName(dtTag
) || hasLocalName(noscriptTag
) || hasLocalName(rpTag
) || hasLocalName(rtTag
))
91 if (hasLocalName(centerTag
) || hasLocalName(nobrTag
) || hasLocalName(rubyTag
) || hasLocalName(navTag
))
93 if (hasLocalName(noembedTag
) || hasLocalName(noframesTag
))
96 // Same values as <span>. This way custom tag name elements will behave like inline spans.
100 bool HTMLElement::mapToEntry(const QualifiedName
& attrName
, MappedAttributeEntry
& result
) const
102 if (attrName
== alignAttr
||
103 attrName
== contenteditableAttr
) {
107 if (attrName
== dirAttr
) {
108 result
= hasLocalName(bdoTag
) ? eBDO
: eUniversal
;
112 return StyledElement::mapToEntry(attrName
, result
);
115 void HTMLElement::parseMappedAttribute(MappedAttribute
*attr
)
117 if (attr
->name() == idAttr
|| attr
->name() == classAttr
|| attr
->name() == styleAttr
)
118 return StyledElement::parseMappedAttribute(attr
);
121 if (attr
->name() == alignAttr
) {
122 if (equalIgnoringCase(attr
->value(), "middle"))
123 addCSSProperty(attr
, CSSPropertyTextAlign
, "center");
125 addCSSProperty(attr
, CSSPropertyTextAlign
, attr
->value());
126 } else if (attr
->name() == contenteditableAttr
) {
127 setContentEditable(attr
);
128 } else if (attr
->name() == tabindexAttr
) {
129 indexstring
= getAttribute(tabindexAttr
);
130 if (indexstring
.length()) {
132 int tabindex
= indexstring
.toIntStrict(&parsedOK
);
134 // Clamp tabindex to the range of 'short' to match Firefox's behavior.
135 setTabIndexExplicitly(max(static_cast<int>(std::numeric_limits
<short>::min()), min(tabindex
, static_cast<int>(std::numeric_limits
<short>::max()))));
137 } else if (attr
->name() == langAttr
) {
139 } else if (attr
->name() == dirAttr
) {
140 addCSSProperty(attr
, CSSPropertyDirection
, attr
->value());
141 addCSSProperty(attr
, CSSPropertyUnicodeBidi
, hasLocalName(bdoTag
) ? CSSValueBidiOverride
: CSSValueEmbed
);
142 } else if (attr
->name() == draggableAttr
) {
143 const AtomicString
& value
= attr
->value();
144 if (equalIgnoringCase(value
, "true")) {
145 addCSSProperty(attr
, CSSPropertyWebkitUserDrag
, CSSValueElement
);
146 addCSSProperty(attr
, CSSPropertyWebkitUserSelect
, CSSValueNone
);
147 } else if (equalIgnoringCase(value
, "false"))
148 addCSSProperty(attr
, CSSPropertyWebkitUserDrag
, CSSValueNone
);
151 else if (attr
->name() == onclickAttr
) {
152 setAttributeEventListener(eventNames().clickEvent
, createAttributeEventListener(this, attr
));
153 } else if (attr
->name() == oncontextmenuAttr
) {
154 setAttributeEventListener(eventNames().contextmenuEvent
, createAttributeEventListener(this, attr
));
155 } else if (attr
->name() == ondblclickAttr
) {
156 setAttributeEventListener(eventNames().dblclickEvent
, createAttributeEventListener(this, attr
));
157 } else if (attr
->name() == onmousedownAttr
) {
158 setAttributeEventListener(eventNames().mousedownEvent
, createAttributeEventListener(this, attr
));
159 } else if (attr
->name() == onmousemoveAttr
) {
160 setAttributeEventListener(eventNames().mousemoveEvent
, createAttributeEventListener(this, attr
));
161 } else if (attr
->name() == onmouseoutAttr
) {
162 setAttributeEventListener(eventNames().mouseoutEvent
, createAttributeEventListener(this, attr
));
163 } else if (attr
->name() == onmouseoverAttr
) {
164 setAttributeEventListener(eventNames().mouseoverEvent
, createAttributeEventListener(this, attr
));
165 } else if (attr
->name() == onmouseupAttr
) {
166 setAttributeEventListener(eventNames().mouseupEvent
, createAttributeEventListener(this, attr
));
167 } else if (attr
->name() == onmousewheelAttr
) {
168 setAttributeEventListener(eventNames().mousewheelEvent
, createAttributeEventListener(this, attr
));
169 } else if (attr
->name() == onfocusAttr
) {
170 setAttributeEventListener(eventNames().focusEvent
, createAttributeEventListener(this, attr
));
171 } else if (attr
->name() == onblurAttr
) {
172 setAttributeEventListener(eventNames().blurEvent
, createAttributeEventListener(this, attr
));
173 } else if (attr
->name() == onkeydownAttr
) {
174 setAttributeEventListener(eventNames().keydownEvent
, createAttributeEventListener(this, attr
));
175 } else if (attr
->name() == onkeypressAttr
) {
176 setAttributeEventListener(eventNames().keypressEvent
, createAttributeEventListener(this, attr
));
177 } else if (attr
->name() == onkeyupAttr
) {
178 setAttributeEventListener(eventNames().keyupEvent
, createAttributeEventListener(this, attr
));
179 } else if (attr
->name() == onscrollAttr
) {
180 setAttributeEventListener(eventNames().scrollEvent
, createAttributeEventListener(this, attr
));
181 } else if (attr
->name() == onbeforecutAttr
) {
182 setAttributeEventListener(eventNames().beforecutEvent
, createAttributeEventListener(this, attr
));
183 } else if (attr
->name() == oncutAttr
) {
184 setAttributeEventListener(eventNames().cutEvent
, createAttributeEventListener(this, attr
));
185 } else if (attr
->name() == onbeforecopyAttr
) {
186 setAttributeEventListener(eventNames().beforecopyEvent
, createAttributeEventListener(this, attr
));
187 } else if (attr
->name() == oncopyAttr
) {
188 setAttributeEventListener(eventNames().copyEvent
, createAttributeEventListener(this, attr
));
189 } else if (attr
->name() == onbeforepasteAttr
) {
190 setAttributeEventListener(eventNames().beforepasteEvent
, createAttributeEventListener(this, attr
));
191 } else if (attr
->name() == onpasteAttr
) {
192 setAttributeEventListener(eventNames().pasteEvent
, createAttributeEventListener(this, attr
));
193 } else if (attr
->name() == ondragenterAttr
) {
194 setAttributeEventListener(eventNames().dragenterEvent
, createAttributeEventListener(this, attr
));
195 } else if (attr
->name() == ondragoverAttr
) {
196 setAttributeEventListener(eventNames().dragoverEvent
, createAttributeEventListener(this, attr
));
197 } else if (attr
->name() == ondragleaveAttr
) {
198 setAttributeEventListener(eventNames().dragleaveEvent
, createAttributeEventListener(this, attr
));
199 } else if (attr
->name() == ondropAttr
) {
200 setAttributeEventListener(eventNames().dropEvent
, createAttributeEventListener(this, attr
));
201 } else if (attr
->name() == ondragstartAttr
) {
202 setAttributeEventListener(eventNames().dragstartEvent
, createAttributeEventListener(this, attr
));
203 } else if (attr
->name() == ondragAttr
) {
204 setAttributeEventListener(eventNames().dragEvent
, createAttributeEventListener(this, attr
));
205 } else if (attr
->name() == ondragendAttr
) {
206 setAttributeEventListener(eventNames().dragendEvent
, createAttributeEventListener(this, attr
));
207 } else if (attr
->name() == onselectstartAttr
) {
208 setAttributeEventListener(eventNames().selectstartEvent
, createAttributeEventListener(this, attr
));
209 } else if (attr
->name() == onsubmitAttr
) {
210 setAttributeEventListener(eventNames().submitEvent
, createAttributeEventListener(this, attr
));
211 } else if (attr
->name() == onerrorAttr
) {
212 setAttributeEventListener(eventNames().errorEvent
, createAttributeEventListener(this, attr
));
213 } else if (attr
->name() == onwebkitanimationstartAttr
) {
214 setAttributeEventListener(eventNames().webkitAnimationStartEvent
, createAttributeEventListener(this, attr
));
215 } else if (attr
->name() == onwebkitanimationiterationAttr
) {
216 setAttributeEventListener(eventNames().webkitAnimationIterationEvent
, createAttributeEventListener(this, attr
));
217 } else if (attr
->name() == onwebkitanimationendAttr
) {
218 setAttributeEventListener(eventNames().webkitAnimationEndEvent
, createAttributeEventListener(this, attr
));
219 } else if (attr
->name() == onwebkittransitionendAttr
) {
220 setAttributeEventListener(eventNames().webkitTransitionEndEvent
, createAttributeEventListener(this, attr
));
221 } else if (attr
->name() == oninputAttr
) {
222 setAttributeEventListener(eventNames().inputEvent
, createAttributeEventListener(this, attr
));
223 } else if (attr
->name() == oninvalidAttr
) {
224 setAttributeEventListener(eventNames().invalidEvent
, createAttributeEventListener(this, attr
));
225 } else if (attr
->name() == ontouchstartAttr
) {
226 setAttributeEventListener(eventNames().touchstartEvent
, createAttributeEventListener(this, attr
));
227 } else if (attr
->name() == ontouchmoveAttr
) {
228 setAttributeEventListener(eventNames().touchmoveEvent
, createAttributeEventListener(this, attr
));
229 } else if (attr
->name() == ontouchendAttr
) {
230 setAttributeEventListener(eventNames().touchendEvent
, createAttributeEventListener(this, attr
));
234 String
HTMLElement::innerHTML() const
236 return createMarkup(this, ChildrenOnly
);
239 String
HTMLElement::outerHTML() const
241 return createMarkup(this);
244 PassRefPtr
<DocumentFragment
> HTMLElement::createContextualFragment(const String
&html
)
246 // the following is in accordance with the definition as used by IE
247 if (endTagRequirement() == TagStatusForbidden
)
250 if (hasLocalName(colTag
) || hasLocalName(colgroupTag
) || hasLocalName(framesetTag
) ||
251 hasLocalName(headTag
) || hasLocalName(styleTag
) || hasLocalName(titleTag
))
254 RefPtr
<DocumentFragment
> fragment
= DocumentFragment::create(document());
256 if (document()->isHTMLDocument())
257 parseHTMLDocumentFragment(html
, fragment
.get());
259 if (!parseXMLDocumentFragment(html
, fragment
.get(), this))
260 // FIXME: We should propagate a syntax error exception out here.
264 // Exceptions are ignored because none ought to happen here.
265 int ignoredExceptionCode
;
267 // we need to pop <html> and <body> elements and remove <head> to
268 // accommodate folks passing complete HTML documents to make the
269 // child of an element.
271 RefPtr
<Node
> nextNode
;
272 for (RefPtr
<Node
> node
= fragment
->firstChild(); node
; node
= nextNode
) {
273 nextNode
= node
->nextSibling();
274 if (node
->hasTagName(htmlTag
) || node
->hasTagName(bodyTag
)) {
275 Node
*firstChild
= node
->firstChild();
277 nextNode
= firstChild
;
278 RefPtr
<Node
> nextChild
;
279 for (RefPtr
<Node
> child
= firstChild
; child
; child
= nextChild
) {
280 nextChild
= child
->nextSibling();
281 node
->removeChild(child
.get(), ignoredExceptionCode
);
282 ASSERT(!ignoredExceptionCode
);
283 fragment
->insertBefore(child
, node
.get(), ignoredExceptionCode
);
284 ASSERT(!ignoredExceptionCode
);
286 fragment
->removeChild(node
.get(), ignoredExceptionCode
);
287 ASSERT(!ignoredExceptionCode
);
288 } else if (node
->hasTagName(headTag
)) {
289 fragment
->removeChild(node
.get(), ignoredExceptionCode
);
290 ASSERT(!ignoredExceptionCode
);
294 return fragment
.release();
297 static inline bool hasOneChild(ContainerNode
* node
)
299 Node
* firstChild
= node
->firstChild();
300 return firstChild
&& !firstChild
->nextSibling();
303 static inline bool hasOneTextChild(ContainerNode
* node
)
305 return hasOneChild(node
) && node
->firstChild()->isTextNode();
308 static void replaceChildrenWithFragment(HTMLElement
* element
, PassRefPtr
<DocumentFragment
> fragment
, ExceptionCode
& ec
)
310 if (!fragment
->firstChild()) {
311 element
->removeChildren();
315 if (hasOneTextChild(element
) && hasOneTextChild(fragment
.get())) {
316 static_cast<Text
*>(element
->firstChild())->setData(static_cast<Text
*>(fragment
->firstChild())->data(), ec
);
320 if (hasOneChild(element
)) {
321 element
->replaceChild(fragment
, element
->firstChild(), ec
);
325 element
->removeChildren();
326 element
->appendChild(fragment
, ec
);
329 static void replaceChildrenWithText(HTMLElement
* element
, const String
& text
, ExceptionCode
& ec
)
331 if (hasOneTextChild(element
)) {
332 static_cast<Text
*>(element
->firstChild())->setData(text
, ec
);
336 RefPtr
<Text
> textNode
= Text::create(element
->document(), text
);
338 if (hasOneChild(element
)) {
339 element
->replaceChild(textNode
.release(), element
->firstChild(), ec
);
343 element
->removeChildren();
344 element
->appendChild(textNode
.release(), ec
);
347 void HTMLElement::setInnerHTML(const String
& html
, ExceptionCode
& ec
)
349 RefPtr
<DocumentFragment
> fragment
= createContextualFragment(html
);
351 ec
= NO_MODIFICATION_ALLOWED_ERR
;
355 replaceChildrenWithFragment(this, fragment
.release(), ec
);
358 void HTMLElement::setOuterHTML(const String
& html
, ExceptionCode
& ec
)
361 if (!p
|| !p
->isHTMLElement()) {
362 ec
= NO_MODIFICATION_ALLOWED_ERR
;
366 HTMLElement
* parent
= static_cast<HTMLElement
*>(p
);
367 RefPtr
<DocumentFragment
> fragment
= parent
->createContextualFragment(html
);
369 ec
= NO_MODIFICATION_ALLOWED_ERR
;
373 // FIXME: Why doesn't this have code to merge neighboring text nodes the way setOuterText does?
375 parent
->replaceChild(fragment
.release(), this, ec
);
378 void HTMLElement::setInnerText(const String
& text
, ExceptionCode
& ec
)
380 // follow the IE specs about when this is allowed
381 if (endTagRequirement() == TagStatusForbidden
) {
382 ec
= NO_MODIFICATION_ALLOWED_ERR
;
385 if (hasLocalName(colTag
) || hasLocalName(colgroupTag
) || hasLocalName(framesetTag
) ||
386 hasLocalName(headTag
) || hasLocalName(htmlTag
) || hasLocalName(tableTag
) ||
387 hasLocalName(tbodyTag
) || hasLocalName(tfootTag
) || hasLocalName(theadTag
) ||
388 hasLocalName(trTag
)) {
389 ec
= NO_MODIFICATION_ALLOWED_ERR
;
393 // FIXME: This doesn't take whitespace collapsing into account at all.
395 if (!text
.contains('\n') && !text
.contains('\r')) {
396 if (text
.isEmpty()) {
400 replaceChildrenWithText(this, text
, ec
);
404 // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
405 // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded?
406 // For example, for the contents of textarea elements that are display:none?
407 RenderObject
* r
= renderer();
408 if (r
&& r
->style()->preserveNewline()) {
409 if (!text
.contains('\r')) {
410 replaceChildrenWithText(this, text
, ec
);
413 String textWithConsistentLineBreaks
= text
;
414 textWithConsistentLineBreaks
.replace("\r\n", "\n");
415 textWithConsistentLineBreaks
.replace('\r', '\n');
416 replaceChildrenWithText(this, textWithConsistentLineBreaks
, ec
);
420 // Add text nodes and <br> elements.
422 RefPtr
<DocumentFragment
> fragment
= DocumentFragment::create(document());
425 int length
= text
.length();
426 for (int i
= 0; i
< length
; ++i
) {
428 if (c
== '\n' || c
== '\r') {
430 fragment
->appendChild(Text::create(document(), text
.substring(lineStart
, i
- lineStart
)), ec
);
434 if (!(c
== '\n' && i
!= 0 && prev
== '\r')) {
435 fragment
->appendChild(new HTMLBRElement(brTag
, document()), ec
);
443 if (length
> lineStart
)
444 fragment
->appendChild(Text::create(document(), text
.substring(lineStart
, length
- lineStart
)), ec
);
445 replaceChildrenWithFragment(this, fragment
.release(), ec
);
448 void HTMLElement::setOuterText(const String
&text
, ExceptionCode
& ec
)
450 // follow the IE specs about when this is allowed
451 if (endTagRequirement() == TagStatusForbidden
) {
452 ec
= NO_MODIFICATION_ALLOWED_ERR
;
455 if (hasLocalName(colTag
) || hasLocalName(colgroupTag
) || hasLocalName(framesetTag
) ||
456 hasLocalName(headTag
) || hasLocalName(htmlTag
) || hasLocalName(tableTag
) ||
457 hasLocalName(tbodyTag
) || hasLocalName(tfootTag
) || hasLocalName(theadTag
) ||
458 hasLocalName(trTag
)) {
459 ec
= NO_MODIFICATION_ALLOWED_ERR
;
463 Node
* parent
= parentNode();
465 ec
= NO_MODIFICATION_ALLOWED_ERR
;
469 // FIXME: This creates a new text node even when the text is empty.
470 // FIXME: This creates a single text node even when the text has CR and LF
471 // characters in it. Instead it should create <br> elements.
472 RefPtr
<Text
> t
= Text::create(document(), text
);
474 parent
->replaceChild(t
, this, ec
);
478 // is previous node a text node? if so, merge into it
479 Node
* prev
= t
->previousSibling();
480 if (prev
&& prev
->isTextNode()) {
481 Text
* textPrev
= static_cast<Text
*>(prev
);
482 textPrev
->appendData(t
->data(), ec
);
491 // is next node a text node? if so, merge it in
492 Node
* next
= t
->nextSibling();
493 if (next
&& next
->isTextNode()) {
494 Text
* textNext
= static_cast<Text
*>(next
);
495 t
->appendData(textNext
->data(), ec
);
498 textNext
->remove(ec
);
504 Node
* HTMLElement::insertAdjacent(const String
& where
, Node
* newChild
, ExceptionCode
& ec
)
506 // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd",
507 // a document fragment is created and the elements appended in the correct order. This document
508 // fragment isn't returned anywhere.
510 // This is impossible for us to implement as the DOM tree does not allow for such structures,
511 // Opera also appears to disallow such usage.
513 if (equalIgnoringCase(where
, "beforeBegin")) {
514 if (Node
* p
= parent())
515 return p
->insertBefore(newChild
, this, ec
) ? newChild
: 0;
519 if (equalIgnoringCase(where
, "afterBegin"))
520 return insertBefore(newChild
, firstChild(), ec
) ? newChild
: 0;
522 if (equalIgnoringCase(where
, "beforeEnd"))
523 return appendChild(newChild
, ec
) ? newChild
: 0;
525 if (equalIgnoringCase(where
, "afterEnd")) {
526 if (Node
* p
= parent())
527 return p
->insertBefore(newChild
, nextSibling(), ec
) ? newChild
: 0;
531 // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative
532 ec
= NOT_SUPPORTED_ERR
;
536 Element
* HTMLElement::insertAdjacentElement(const String
& where
, Element
* newChild
, ExceptionCode
& ec
)
539 // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative
540 ec
= TYPE_MISMATCH_ERR
;
544 Node
* returnValue
= insertAdjacent(where
, newChild
, ec
);
545 ASSERT(!returnValue
|| returnValue
->isElementNode());
546 return static_cast<Element
*>(returnValue
);
549 void HTMLElement::insertAdjacentHTML(const String
& where
, const String
& html
, ExceptionCode
& ec
)
551 RefPtr
<DocumentFragment
> fragment
= document()->createDocumentFragment();
552 if (document()->isHTMLDocument())
553 parseHTMLDocumentFragment(html
, fragment
.get());
555 if (!parseXMLDocumentFragment(html
, fragment
.get(), this))
556 // FIXME: We should propagate a syntax error exception out here.
560 insertAdjacent(where
, fragment
.get(), ec
);
563 void HTMLElement::insertAdjacentText(const String
& where
, const String
& text
, ExceptionCode
& ec
)
565 RefPtr
<Text
> textNode
= document()->createTextNode(text
);
566 insertAdjacent(where
, textNode
.get(), ec
);
569 void HTMLElement::addHTMLAlignment(MappedAttribute
* attr
)
571 addHTMLAlignmentToStyledElement(this, attr
);
574 void HTMLElement::addHTMLAlignmentToStyledElement(StyledElement
* element
, MappedAttribute
* attr
)
576 // vertical alignment with respect to the current baseline of the text
577 // right or left means floating images
578 int floatValue
= CSSValueInvalid
;
579 int verticalAlignValue
= CSSValueInvalid
;
581 const AtomicString
& alignment
= attr
->value();
582 if (equalIgnoringCase(alignment
, "absmiddle"))
583 verticalAlignValue
= CSSValueMiddle
;
584 else if (equalIgnoringCase(alignment
, "absbottom"))
585 verticalAlignValue
= CSSValueBottom
;
586 else if (equalIgnoringCase(alignment
, "left")) {
587 floatValue
= CSSValueLeft
;
588 verticalAlignValue
= CSSValueTop
;
589 } else if (equalIgnoringCase(alignment
, "right")) {
590 floatValue
= CSSValueRight
;
591 verticalAlignValue
= CSSValueTop
;
592 } else if (equalIgnoringCase(alignment
, "top"))
593 verticalAlignValue
= CSSValueTop
;
594 else if (equalIgnoringCase(alignment
, "middle"))
595 verticalAlignValue
= CSSValueWebkitBaselineMiddle
;
596 else if (equalIgnoringCase(alignment
, "center"))
597 verticalAlignValue
= CSSValueMiddle
;
598 else if (equalIgnoringCase(alignment
, "bottom"))
599 verticalAlignValue
= CSSValueBaseline
;
600 else if (equalIgnoringCase(alignment
, "texttop"))
601 verticalAlignValue
= CSSValueTextTop
;
603 if (floatValue
!= CSSValueInvalid
)
604 element
->addCSSProperty(attr
, CSSPropertyFloat
, floatValue
);
606 if (verticalAlignValue
!= CSSValueInvalid
)
607 element
->addCSSProperty(attr
, CSSPropertyVerticalAlign
, verticalAlignValue
);
610 bool HTMLElement::supportsFocus() const
612 return Element::supportsFocus() || (isContentEditable() && parent() && !parent()->isContentEditable());
615 bool HTMLElement::isContentEditable() const
617 if (document()->frame() && document()->frame()->isContentEditable())
620 // FIXME: this is a terrible thing to do here:
621 // https://bugs.webkit.org/show_bug.cgi?id=21834
622 document()->updateStyleIfNeeded();
626 return parentNode()->isContentEditable();
631 return renderer()->style()->userModify() == READ_WRITE
|| renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY
;
634 bool HTMLElement::isContentRichlyEditable() const
636 if (document()->frame() && document()->frame()->isContentEditable())
639 document()->updateStyleIfNeeded();
643 return parentNode()->isContentEditable();
648 return renderer()->style()->userModify() == READ_WRITE
;
651 String
HTMLElement::contentEditable() const
653 document()->updateStyleIfNeeded();
658 switch (renderer()->style()->userModify()) {
663 case READ_WRITE_PLAINTEXT_ONLY
:
664 return "plaintext-only";
670 void HTMLElement::setContentEditable(MappedAttribute
* attr
)
672 const AtomicString
& enabled
= attr
->value();
673 if (enabled
.isEmpty() || equalIgnoringCase(enabled
, "true")) {
674 addCSSProperty(attr
, CSSPropertyWebkitUserModify
, CSSValueReadWrite
);
675 addCSSProperty(attr
, CSSPropertyWordWrap
, CSSValueBreakWord
);
676 addCSSProperty(attr
, CSSPropertyWebkitNbspMode
, CSSValueSpace
);
677 addCSSProperty(attr
, CSSPropertyWebkitLineBreak
, CSSValueAfterWhiteSpace
);
678 } else if (equalIgnoringCase(enabled
, "false")) {
679 addCSSProperty(attr
, CSSPropertyWebkitUserModify
, CSSValueReadOnly
);
680 attr
->decl()->removeProperty(CSSPropertyWordWrap
, false);
681 attr
->decl()->removeProperty(CSSPropertyWebkitNbspMode
, false);
682 attr
->decl()->removeProperty(CSSPropertyWebkitLineBreak
, false);
683 } else if (equalIgnoringCase(enabled
, "inherit")) {
684 addCSSProperty(attr
, CSSPropertyWebkitUserModify
, CSSValueInherit
);
685 attr
->decl()->removeProperty(CSSPropertyWordWrap
, false);
686 attr
->decl()->removeProperty(CSSPropertyWebkitNbspMode
, false);
687 attr
->decl()->removeProperty(CSSPropertyWebkitLineBreak
, false);
688 } else if (equalIgnoringCase(enabled
, "plaintext-only")) {
689 addCSSProperty(attr
, CSSPropertyWebkitUserModify
, CSSValueReadWritePlaintextOnly
);
690 addCSSProperty(attr
, CSSPropertyWordWrap
, CSSValueBreakWord
);
691 addCSSProperty(attr
, CSSPropertyWebkitNbspMode
, CSSValueSpace
);
692 addCSSProperty(attr
, CSSPropertyWebkitLineBreak
, CSSValueAfterWhiteSpace
);
696 void HTMLElement::setContentEditable(const String
&enabled
)
698 if (enabled
== "inherit") {
700 removeAttribute(contenteditableAttr
, ec
);
703 setAttribute(contenteditableAttr
, enabled
.isEmpty() ? "true" : enabled
);
706 bool HTMLElement::draggable() const
708 return equalIgnoringCase(getAttribute(draggableAttr
), "true");
711 void HTMLElement::setDraggable(bool value
)
713 setAttribute(draggableAttr
, value
? "true" : "false");
716 void HTMLElement::click()
718 dispatchSimulatedClick(0, false, false);
721 // accessKeyAction is used by the accessibility support code
722 // to send events to elements that our JavaScript caller does
723 // does not. The elements JS is interested in have subclasses
724 // that override this method to direct the click appropriately.
725 // Here in the base class, then, we only send the click if
726 // the caller wants it to go to any HTMLElement, and we say
727 // to send the mouse events in addition to the click.
728 void HTMLElement::accessKeyAction(bool sendToAnyElement
)
730 if (sendToAnyElement
)
731 dispatchSimulatedClick(0, true);
734 String
HTMLElement::title() const
736 return getAttribute(titleAttr
);
739 short HTMLElement::tabIndex() const
742 return Element::tabIndex();
746 void HTMLElement::setTabIndex(int value
)
748 setAttribute(tabindexAttr
, String::number(value
));
751 PassRefPtr
<HTMLCollection
> HTMLElement::children()
753 return HTMLCollection::create(this, NodeChildren
);
757 bool HTMLElement::childAllowed(Node
*newChild
)
759 if (!Element::childAllowed(newChild
))
762 // For XML documents, we are non-validating and do not check against a DTD, even for HTML elements.
763 if (!document()->isHTMLDocument())
766 // Future-proof for XML content inside HTML documents (we may allow this some day).
767 if (newChild
->isElementNode() && !newChild
->isHTMLElement())
770 // Elements with forbidden tag status can never have children
771 if (endTagRequirement() == TagStatusForbidden
)
774 // Comment nodes are always allowed.
775 if (newChild
->isCommentNode())
778 // Now call checkDTD.
779 return checkDTD(newChild
);
783 // This unfortunate function is only needed when checking against the DTD. Other languages (like SVG) won't need this.
784 bool HTMLElement::isRecognizedTagName(const QualifiedName
& tagName
)
786 DEFINE_STATIC_LOCAL(HashSet
<AtomicStringImpl
*>, tagList
, ());
787 if (tagList
.isEmpty()) {
789 WebCore::QualifiedName
** tags
= HTMLNames::getHTMLTags(&tagCount
);
790 for (size_t i
= 0; i
< tagCount
; i
++)
791 tagList
.add(tags
[i
]->localName().impl());
793 return tagList
.contains(tagName
.localName().impl());
796 // The terms inline and block are used here loosely. Don't make the mistake of assuming all inlines or all blocks
797 // need to be in these two lists.
798 static HashSet
<AtomicStringImpl
*>* inlineTagList()
800 DEFINE_STATIC_LOCAL(HashSet
<AtomicStringImpl
*>, tagList
, ());
801 if (tagList
.isEmpty()) {
802 tagList
.add(ttTag
.localName().impl());
803 tagList
.add(iTag
.localName().impl());
804 tagList
.add(bTag
.localName().impl());
805 tagList
.add(uTag
.localName().impl());
806 tagList
.add(sTag
.localName().impl());
807 tagList
.add(strikeTag
.localName().impl());
808 tagList
.add(bigTag
.localName().impl());
809 tagList
.add(smallTag
.localName().impl());
810 tagList
.add(emTag
.localName().impl());
811 tagList
.add(strongTag
.localName().impl());
812 tagList
.add(dfnTag
.localName().impl());
813 tagList
.add(codeTag
.localName().impl());
814 tagList
.add(sampTag
.localName().impl());
815 tagList
.add(kbdTag
.localName().impl());
816 tagList
.add(varTag
.localName().impl());
817 tagList
.add(citeTag
.localName().impl());
818 tagList
.add(abbrTag
.localName().impl());
819 tagList
.add(acronymTag
.localName().impl());
820 tagList
.add(aTag
.localName().impl());
821 tagList
.add(canvasTag
.localName().impl());
822 tagList
.add(imgTag
.localName().impl());
823 tagList
.add(appletTag
.localName().impl());
824 tagList
.add(objectTag
.localName().impl());
825 tagList
.add(embedTag
.localName().impl());
826 tagList
.add(fontTag
.localName().impl());
827 tagList
.add(basefontTag
.localName().impl());
828 tagList
.add(brTag
.localName().impl());
829 tagList
.add(scriptTag
.localName().impl());
830 tagList
.add(styleTag
.localName().impl());
831 tagList
.add(linkTag
.localName().impl());
832 tagList
.add(mapTag
.localName().impl());
833 tagList
.add(qTag
.localName().impl());
834 tagList
.add(subTag
.localName().impl());
835 tagList
.add(supTag
.localName().impl());
836 tagList
.add(spanTag
.localName().impl());
837 tagList
.add(bdoTag
.localName().impl());
838 tagList
.add(iframeTag
.localName().impl());
839 tagList
.add(inputTag
.localName().impl());
840 tagList
.add(keygenTag
.localName().impl());
841 tagList
.add(selectTag
.localName().impl());
842 tagList
.add(datagridTag
.localName().impl());
843 tagList
.add(textareaTag
.localName().impl());
844 tagList
.add(labelTag
.localName().impl());
845 tagList
.add(buttonTag
.localName().impl());
846 tagList
.add(datalistTag
.localName().impl());
847 tagList
.add(insTag
.localName().impl());
848 tagList
.add(delTag
.localName().impl());
849 tagList
.add(nobrTag
.localName().impl());
850 tagList
.add(wbrTag
.localName().impl());
852 tagList
.add(audioTag
.localName().impl());
853 tagList
.add(videoTag
.localName().impl());
855 tagList
.add(rpTag
.localName().impl());
856 tagList
.add(rtTag
.localName().impl());
857 tagList
.add(rubyTag
.localName().impl());
862 static HashSet
<AtomicStringImpl
*>* blockTagList()
864 DEFINE_STATIC_LOCAL(HashSet
<AtomicStringImpl
*>, tagList
, ());
865 if (tagList
.isEmpty()) {
866 tagList
.add(addressTag
.localName().impl());
867 tagList
.add(blockquoteTag
.localName().impl());
868 tagList
.add(centerTag
.localName().impl());
869 tagList
.add(ddTag
.localName().impl());
870 tagList
.add(dirTag
.localName().impl());
871 tagList
.add(divTag
.localName().impl());
872 tagList
.add(dlTag
.localName().impl());
873 tagList
.add(dtTag
.localName().impl());
874 tagList
.add(fieldsetTag
.localName().impl());
875 tagList
.add(formTag
.localName().impl());
876 tagList
.add(h1Tag
.localName().impl());
877 tagList
.add(h2Tag
.localName().impl());
878 tagList
.add(h3Tag
.localName().impl());
879 tagList
.add(h4Tag
.localName().impl());
880 tagList
.add(h5Tag
.localName().impl());
881 tagList
.add(h6Tag
.localName().impl());
882 tagList
.add(hrTag
.localName().impl());
883 tagList
.add(isindexTag
.localName().impl());
884 tagList
.add(layerTag
.localName().impl());
885 tagList
.add(liTag
.localName().impl());
886 tagList
.add(listingTag
.localName().impl());
887 tagList
.add(marqueeTag
.localName().impl());
888 tagList
.add(menuTag
.localName().impl());
889 tagList
.add(navTag
.localName().impl());
890 tagList
.add(noembedTag
.localName().impl());
891 tagList
.add(noframesTag
.localName().impl());
892 tagList
.add(nolayerTag
.localName().impl());
893 tagList
.add(noscriptTag
.localName().impl());
894 tagList
.add(olTag
.localName().impl());
895 tagList
.add(pTag
.localName().impl());
896 tagList
.add(plaintextTag
.localName().impl());
897 tagList
.add(preTag
.localName().impl());
898 tagList
.add(tableTag
.localName().impl());
899 tagList
.add(ulTag
.localName().impl());
900 tagList
.add(xmpTag
.localName().impl());
905 bool HTMLElement::inEitherTagList(const Node
* newChild
)
907 if (newChild
->isTextNode())
910 if (newChild
->isHTMLElement()) {
911 const HTMLElement
* child
= static_cast<const HTMLElement
*>(newChild
);
912 if (inlineTagList()->contains(child
->tagQName().localName().impl())) {
914 if (child
->tagQName().localName() == styleTag
) {
915 // Leopard Mail doesn't expect <style> to be in the body of the document, so don't allow it in that case.
916 // See <rdar://problem/6621310>
917 Settings
* settings
= newChild
->document() ? newChild
->document()->settings() : 0;
918 if (settings
&& settings
->needsLeopardMailQuirks())
924 if (blockTagList()->contains(child
->tagQName().localName().impl()))
926 return !isRecognizedTagName(child
->tagQName()); // Accept custom html tags
932 bool HTMLElement::inInlineTagList(const Node
* newChild
)
934 if (newChild
->isTextNode())
937 if (newChild
->isHTMLElement()) {
938 const HTMLElement
* child
= static_cast<const HTMLElement
*>(newChild
);
939 if (inlineTagList()->contains(child
->tagQName().localName().impl()))
941 return !isRecognizedTagName(child
->tagQName()); // Accept custom html tags
947 bool HTMLElement::inBlockTagList(const Node
* newChild
)
949 if (newChild
->isTextNode())
952 if (newChild
->isHTMLElement()) {
953 const HTMLElement
* child
= static_cast<const HTMLElement
*>(newChild
);
954 return (blockTagList()->contains(child
->tagQName().localName().impl()));
960 bool HTMLElement::checkDTD(const Node
* newChild
)
962 if (hasLocalName(addressTag
) && newChild
->hasTagName(pTag
))
964 return inEitherTagList(newChild
);
967 bool HTMLElement::rendererIsNeeded(RenderStyle
*style
)
970 if (hasLocalName(noscriptTag
)) {
971 Settings
* settings
= document()->settings();
972 if (settings
&& settings
->isJavaScriptEnabled())
976 return StyledElement::rendererIsNeeded(style
);
979 RenderObject
* HTMLElement::createRenderer(RenderArena
* arena
, RenderStyle
* style
)
981 if (hasLocalName(wbrTag
))
982 return new (arena
) RenderWordBreak(this);
983 return RenderObject::createObject(this, style
);
986 HTMLFormElement
* HTMLElement::findFormAncestor() const
988 for (Node
* ancestor
= parentNode(); ancestor
; ancestor
= ancestor
->parentNode())
989 if (ancestor
->hasTagName(formTag
))
990 return static_cast<HTMLFormElement
*>(ancestor
);
994 HTMLFormElement
* HTMLElement::virtualForm() const
996 return findFormAncestor();
999 } // namespace WebCore
1003 // For use in the debugger
1004 void dumpInnerHTML(WebCore::HTMLElement
*);
1006 void dumpInnerHTML(WebCore::HTMLElement
* element
)
1008 printf("%s\n", element
->innerHTML().ascii().data());