Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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-2008, 2013, 2014 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 "core/html/HTMLElement.h"
27
28 #include "bindings/core/v8/ExceptionState.h"
29 #include "bindings/core/v8/ScriptEventListener.h"
30 #include "core/CSSPropertyNames.h"
31 #include "core/CSSValueKeywords.h"
32 #include "core/HTMLNames.h"
33 #include "core/XMLNames.h"
34 #include "core/css/CSSMarkup.h"
35 #include "core/css/CSSValuePool.h"
36 #include "core/css/StylePropertySet.h"
37 #include "core/dom/DocumentFragment.h"
38 #include "core/dom/ElementTraversal.h"
39 #include "core/dom/ExceptionCode.h"
40 #include "core/dom/NodeTraversal.h"
41 #include "core/dom/Text.h"
42 #include "core/dom/shadow/ElementShadow.h"
43 #include "core/dom/shadow/ShadowRoot.h"
44 #include "core/editing/markup.h"
45 #include "core/events/EventListener.h"
46 #include "core/events/KeyboardEvent.h"
47 #include "core/frame/Settings.h"
48 #include "core/frame/UseCounter.h"
49 #include "core/html/HTMLBRElement.h"
50 #include "core/html/HTMLFormElement.h"
51 #include "core/html/HTMLInputElement.h"
52 #include "core/html/HTMLMenuElement.h"
53 #include "core/html/HTMLTemplateElement.h"
54 #include "core/html/HTMLTextFormControlElement.h"
55 #include "core/html/parser/HTMLParserIdioms.h"
56 #include "core/rendering/RenderObject.h"
57 #include "platform/Language.h"
58 #include "platform/text/BidiResolver.h"
59 #include "platform/text/BidiTextRun.h"
60 #include "platform/text/TextRunIterator.h"
61 #include "wtf/StdLibExtras.h"
62 #include "wtf/text/CString.h"
63
64 namespace blink {
65
66 using namespace HTMLNames;
67 using namespace WTF;
68
69 using std::min;
70 using std::max;
71
72 DEFINE_ELEMENT_FACTORY_WITH_TAGNAME(HTMLElement);
73
74 String HTMLElement::nodeName() const
75 {
76     // FIXME: Would be nice to have an atomicstring lookup based off uppercase
77     // chars that does not have to copy the string on a hit in the hash.
78     // FIXME: We should have a way to detect XHTML elements and replace the hasPrefix() check with it.
79     if (document().isHTMLDocument()) {
80         if (!tagQName().hasPrefix())
81             return tagQName().localNameUpper();
82         return Element::nodeName().upper();
83     }
84     return Element::nodeName();
85 }
86
87 bool HTMLElement::ieForbidsInsertHTML() const
88 {
89     // FIXME: Supposedly IE disallows settting innerHTML, outerHTML
90     // and createContextualFragment on these tags.  We have no tests to
91     // verify this however, so this list could be totally wrong.
92     // This list was moved from the previous endTagRequirement() implementation.
93     // This is also called from editing and assumed to be the list of tags
94     // for which no end tag should be serialized. It's unclear if the list for
95     // IE compat and the list for serialization sanity are the same.
96     if (hasTagName(areaTag)
97         || hasTagName(baseTag)
98         || hasTagName(basefontTag)
99         || hasTagName(brTag)
100         || hasTagName(colTag)
101         || hasTagName(embedTag)
102         || hasTagName(frameTag)
103         || hasTagName(hrTag)
104         || hasTagName(imageTag)
105         || hasTagName(imgTag)
106         || hasTagName(inputTag)
107         || hasTagName(linkTag)
108         || (RuntimeEnabledFeatures::contextMenuEnabled() && hasTagName(menuitemTag))
109         || hasTagName(metaTag)
110         || hasTagName(paramTag)
111         || hasTagName(sourceTag)
112         || hasTagName(wbrTag))
113         return true;
114     return false;
115 }
116
117 static inline CSSValueID unicodeBidiAttributeForDirAuto(HTMLElement* element)
118 {
119     if (element->hasTagName(preTag) || element->hasTagName(textareaTag))
120         return CSSValueWebkitPlaintext;
121     // FIXME: For bdo element, dir="auto" should result in "bidi-override isolate" but we don't support having multiple values in unicode-bidi yet.
122     // See https://bugs.webkit.org/show_bug.cgi?id=73164.
123     return CSSValueWebkitIsolate;
124 }
125
126 unsigned HTMLElement::parseBorderWidthAttribute(const AtomicString& value) const
127 {
128     unsigned borderWidth = 0;
129     if (value.isEmpty() || !parseHTMLNonNegativeInteger(value, borderWidth))
130         return hasTagName(tableTag) ? 1 : borderWidth;
131     return borderWidth;
132 }
133
134 void HTMLElement::applyBorderAttributeToStyle(const AtomicString& value, MutableStylePropertySet* style)
135 {
136     addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderWidth, parseBorderWidthAttribute(value), CSSPrimitiveValue::CSS_PX);
137     addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderStyle, CSSValueSolid);
138 }
139
140 void HTMLElement::mapLanguageAttributeToLocale(const AtomicString& value, MutableStylePropertySet* style)
141 {
142     if (!value.isEmpty()) {
143         // Have to quote so the locale id is treated as a string instead of as a CSS keyword.
144         addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, quoteCSSString(value));
145
146         // FIXME: Remove the following UseCounter code when we collect enough
147         // data.
148         UseCounter::count(document(), UseCounter::LangAttribute);
149         if (isHTMLHtmlElement(*this))
150             UseCounter::count(document(), UseCounter::LangAttributeOnHTML);
151         else if (isHTMLBodyElement(*this))
152             UseCounter::count(document(), UseCounter::LangAttributeOnBody);
153         String htmlLanguage = value.string();
154         size_t firstSeparator = htmlLanguage.find('-');
155         if (firstSeparator != kNotFound)
156             htmlLanguage = htmlLanguage.left(firstSeparator);
157         String uiLanguage = defaultLanguage();
158         firstSeparator = uiLanguage.find('-');
159         if (firstSeparator != kNotFound)
160             uiLanguage = uiLanguage.left(firstSeparator);
161         firstSeparator = uiLanguage.find('_');
162         if (firstSeparator != kNotFound)
163             uiLanguage = uiLanguage.left(firstSeparator);
164         if (!equalIgnoringCase(htmlLanguage, uiLanguage))
165             UseCounter::count(document(), UseCounter::LangAttributeDoesNotMatchToUILocale);
166     } else {
167         // The empty string means the language is explicitly unknown.
168         addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, CSSValueAuto);
169     }
170 }
171
172 bool HTMLElement::isPresentationAttribute(const QualifiedName& name) const
173 {
174     if (name == alignAttr || name == contenteditableAttr || name == hiddenAttr || name == langAttr || name.matches(XMLNames::langAttr) || name == draggableAttr || name == dirAttr)
175         return true;
176     return Element::isPresentationAttribute(name);
177 }
178
179 static inline bool isValidDirAttribute(const AtomicString& value)
180 {
181     return equalIgnoringCase(value, "auto") || equalIgnoringCase(value, "ltr") || equalIgnoringCase(value, "rtl");
182 }
183
184 void HTMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
185 {
186     if (name == alignAttr) {
187         if (equalIgnoringCase(value, "middle"))
188             addPropertyToPresentationAttributeStyle(style, CSSPropertyTextAlign, CSSValueCenter);
189         else
190             addPropertyToPresentationAttributeStyle(style, CSSPropertyTextAlign, value);
191     } else if (name == contenteditableAttr) {
192         if (value.isEmpty() || equalIgnoringCase(value, "true")) {
193             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadWrite);
194             addPropertyToPresentationAttributeStyle(style, CSSPropertyWordWrap, CSSValueBreakWord);
195             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
196         } else if (equalIgnoringCase(value, "plaintext-only")) {
197             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadWritePlaintextOnly);
198             addPropertyToPresentationAttributeStyle(style, CSSPropertyWordWrap, CSSValueBreakWord);
199             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
200         } else if (equalIgnoringCase(value, "false"))
201             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadOnly);
202     } else if (name == hiddenAttr) {
203         addPropertyToPresentationAttributeStyle(style, CSSPropertyDisplay, CSSValueNone);
204     } else if (name == draggableAttr) {
205         if (equalIgnoringCase(value, "true")) {
206             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserDrag, CSSValueElement);
207             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserSelect, CSSValueNone);
208         } else if (equalIgnoringCase(value, "false"))
209             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserDrag, CSSValueNone);
210     } else if (name == dirAttr) {
211         if (equalIgnoringCase(value, "auto"))
212             addPropertyToPresentationAttributeStyle(style, CSSPropertyUnicodeBidi, unicodeBidiAttributeForDirAuto(this));
213         else {
214             if (isValidDirAttribute(value))
215                 addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, value);
216             else
217                 addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, "ltr");
218             if (!hasTagName(bdiTag) && !hasTagName(bdoTag) && !hasTagName(outputTag))
219                 addPropertyToPresentationAttributeStyle(style, CSSPropertyUnicodeBidi, CSSValueEmbed);
220         }
221     } else if (name.matches(XMLNames::langAttr))
222         mapLanguageAttributeToLocale(value, style);
223     else if (name == langAttr) {
224         // xml:lang has a higher priority than lang.
225         if (!fastHasAttribute(XMLNames::langAttr))
226             mapLanguageAttributeToLocale(value, style);
227     } else
228         Element::collectStyleForPresentationAttribute(name, value, style);
229 }
230
231 const AtomicString& HTMLElement::eventNameForAttributeName(const QualifiedName& attrName)
232 {
233     if (!attrName.namespaceURI().isNull())
234         return nullAtom;
235
236     if (!attrName.localName().startsWith("on", false))
237         return nullAtom;
238
239     typedef HashMap<AtomicString, AtomicString> StringToStringMap;
240     DEFINE_STATIC_LOCAL(StringToStringMap, attributeNameToEventNameMap, ());
241     if (!attributeNameToEventNameMap.size()) {
242         struct AttrToEventName {
243             const QualifiedName& attr;
244             const AtomicString& event;
245         };
246         AttrToEventName attrToEventNames[] = {
247             { onabortAttr, EventTypeNames::abort },
248             { onanimationendAttr, EventTypeNames::animationend },
249             { onanimationiterationAttr, EventTypeNames::animationiteration },
250             { onanimationstartAttr, EventTypeNames::animationstart },
251             { onautocompleteAttr, EventTypeNames::autocomplete },
252             { onautocompleteerrorAttr, EventTypeNames::autocompleteerror },
253             { onbeforecopyAttr, EventTypeNames::beforecopy },
254             { onbeforecutAttr, EventTypeNames::beforecut },
255             { onbeforepasteAttr, EventTypeNames::beforepaste },
256             { onblurAttr, EventTypeNames::blur },
257             { oncancelAttr, EventTypeNames::cancel },
258             { oncanplayAttr, EventTypeNames::canplay },
259             { oncanplaythroughAttr, EventTypeNames::canplaythrough },
260             { onchangeAttr, EventTypeNames::change },
261             { onclickAttr, EventTypeNames::click },
262             { oncloseAttr, EventTypeNames::close },
263             { oncontextmenuAttr, EventTypeNames::contextmenu },
264             { oncopyAttr, EventTypeNames::copy },
265             { oncuechangeAttr, EventTypeNames::cuechange },
266             { oncutAttr, EventTypeNames::cut },
267             { ondblclickAttr, EventTypeNames::dblclick },
268             { ondragAttr, EventTypeNames::drag },
269             { ondragendAttr, EventTypeNames::dragend },
270             { ondragenterAttr, EventTypeNames::dragenter },
271             { ondragleaveAttr, EventTypeNames::dragleave },
272             { ondragoverAttr, EventTypeNames::dragover },
273             { ondragstartAttr, EventTypeNames::dragstart },
274             { ondropAttr, EventTypeNames::drop },
275             { ondurationchangeAttr, EventTypeNames::durationchange },
276             { onemptiedAttr, EventTypeNames::emptied },
277             { onendedAttr, EventTypeNames::ended },
278             { onerrorAttr, EventTypeNames::error },
279             { onfocusAttr, EventTypeNames::focus },
280             { onfocusinAttr, EventTypeNames::focusin },
281             { onfocusoutAttr, EventTypeNames::focusout },
282             { oninputAttr, EventTypeNames::input },
283             { oninvalidAttr, EventTypeNames::invalid },
284             { onkeydownAttr, EventTypeNames::keydown },
285             { onkeypressAttr, EventTypeNames::keypress },
286             { onkeyupAttr, EventTypeNames::keyup },
287             { onloadAttr, EventTypeNames::load },
288             { onloadeddataAttr, EventTypeNames::loadeddata },
289             { onloadedmetadataAttr, EventTypeNames::loadedmetadata },
290             { onloadstartAttr, EventTypeNames::loadstart },
291             { onmousedownAttr, EventTypeNames::mousedown },
292             { onmouseenterAttr, EventTypeNames::mouseenter },
293             { onmouseleaveAttr, EventTypeNames::mouseleave },
294             { onmousemoveAttr, EventTypeNames::mousemove },
295             { onmouseoutAttr, EventTypeNames::mouseout },
296             { onmouseoverAttr, EventTypeNames::mouseover },
297             { onmouseupAttr, EventTypeNames::mouseup },
298             { onmousewheelAttr, EventTypeNames::mousewheel },
299             { onpasteAttr, EventTypeNames::paste },
300             { onpauseAttr, EventTypeNames::pause },
301             { onplayAttr, EventTypeNames::play },
302             { onplayingAttr, EventTypeNames::playing },
303             { onprogressAttr, EventTypeNames::progress },
304             { onratechangeAttr, EventTypeNames::ratechange },
305             { onresetAttr, EventTypeNames::reset },
306             { onresizeAttr, EventTypeNames::resize },
307             { onscrollAttr, EventTypeNames::scroll },
308             { onseekedAttr, EventTypeNames::seeked },
309             { onseekingAttr, EventTypeNames::seeking },
310             { onselectAttr, EventTypeNames::select },
311             { onselectstartAttr, EventTypeNames::selectstart },
312             { onshowAttr, EventTypeNames::show },
313             { onstalledAttr, EventTypeNames::stalled },
314             { onsubmitAttr, EventTypeNames::submit },
315             { onsuspendAttr, EventTypeNames::suspend },
316             { ontimeupdateAttr, EventTypeNames::timeupdate },
317             { ontoggleAttr, EventTypeNames::toggle },
318             { ontouchcancelAttr, EventTypeNames::touchcancel },
319             { ontouchendAttr, EventTypeNames::touchend },
320             { ontouchmoveAttr, EventTypeNames::touchmove },
321             { ontouchstartAttr, EventTypeNames::touchstart },
322             { ontransitionendAttr, EventTypeNames::webkitTransitionEnd },
323             { onvolumechangeAttr, EventTypeNames::volumechange },
324             { onwaitingAttr, EventTypeNames::waiting },
325             { onwebkitanimationendAttr, EventTypeNames::webkitAnimationEnd },
326             { onwebkitanimationiterationAttr, EventTypeNames::webkitAnimationIteration },
327             { onwebkitanimationstartAttr, EventTypeNames::webkitAnimationStart },
328             { onwebkitfullscreenchangeAttr, EventTypeNames::webkitfullscreenchange },
329             { onwebkitfullscreenerrorAttr, EventTypeNames::webkitfullscreenerror },
330             { onwebkittransitionendAttr, EventTypeNames::webkitTransitionEnd },
331             { onwheelAttr, EventTypeNames::wheel },
332         };
333
334         for (size_t i = 0; i < WTF_ARRAY_LENGTH(attrToEventNames); i++)
335             attributeNameToEventNameMap.set(attrToEventNames[i].attr.localName(), attrToEventNames[i].event);
336     }
337
338     return attributeNameToEventNameMap.get(attrName.localName());
339 }
340
341 void HTMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
342 {
343     if (name == tabindexAttr)
344         return Element::parseAttribute(name, value);
345
346     if (name == dirAttr) {
347         dirAttributeChanged(value);
348     } else {
349         const AtomicString& eventName = eventNameForAttributeName(name);
350         if (!eventName.isNull())
351             setAttributeEventListener(eventName, createAttributeEventListener(this, name, value, eventParameterName()));
352     }
353 }
354
355 PassRefPtrWillBeRawPtr<DocumentFragment> HTMLElement::textToFragment(const String& text, ExceptionState& exceptionState)
356 {
357     RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(document());
358     unsigned i, length = text.length();
359     UChar c = 0;
360     for (unsigned start = 0; start < length; ) {
361
362         // Find next line break.
363         for (i = start; i < length; i++) {
364           c = text[i];
365           if (c == '\r' || c == '\n')
366               break;
367         }
368
369         fragment->appendChild(Text::create(document(), text.substring(start, i - start)), exceptionState);
370         if (exceptionState.hadException())
371             return nullptr;
372
373         if (c == '\r' || c == '\n') {
374             fragment->appendChild(HTMLBRElement::create(document()), exceptionState);
375             if (exceptionState.hadException())
376                 return nullptr;
377             // Make sure \r\n doesn't result in two line breaks.
378             if (c == '\r' && i + 1 < length && text[i + 1] == '\n')
379                 i++;
380         }
381
382         start = i + 1; // Character after line break.
383     }
384
385     return fragment;
386 }
387
388 static inline bool shouldProhibitSetInnerOuterText(const HTMLElement& element)
389 {
390     return element.hasTagName(colTag)
391         || element.hasTagName(colgroupTag)
392         || element.hasTagName(framesetTag)
393         || element.hasTagName(headTag)
394         || element.hasTagName(htmlTag)
395         || element.hasTagName(tableTag)
396         || element.hasTagName(tbodyTag)
397         || element.hasTagName(tfootTag)
398         || element.hasTagName(theadTag)
399         || element.hasTagName(trTag);
400 }
401
402 void HTMLElement::setInnerText(const String& text, ExceptionState& exceptionState)
403 {
404     if (ieForbidsInsertHTML()) {
405         exceptionState.throwDOMException(NoModificationAllowedError, "The '" + localName() + "' element does not support text insertion.");
406         return;
407     }
408     if (shouldProhibitSetInnerOuterText(*this)) {
409         exceptionState.throwDOMException(NoModificationAllowedError, "The '" + localName() + "' element does not support text insertion.");
410         return;
411     }
412
413     // FIXME: This doesn't take whitespace collapsing into account at all.
414
415     if (!text.contains('\n') && !text.contains('\r')) {
416         if (text.isEmpty()) {
417             removeChildren();
418             return;
419         }
420         replaceChildrenWithText(this, text, exceptionState);
421         return;
422     }
423
424     // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
425     // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded?
426     // For example, for the contents of textarea elements that are display:none?
427     RenderObject* r = renderer();
428     if (r && r->style()->preserveNewline()) {
429         if (!text.contains('\r')) {
430             replaceChildrenWithText(this, text, exceptionState);
431             return;
432         }
433         String textWithConsistentLineBreaks = text;
434         textWithConsistentLineBreaks.replace("\r\n", "\n");
435         textWithConsistentLineBreaks.replace('\r', '\n');
436         replaceChildrenWithText(this, textWithConsistentLineBreaks, exceptionState);
437         return;
438     }
439
440     // Add text nodes and <br> elements.
441     RefPtrWillBeRawPtr<DocumentFragment> fragment = textToFragment(text, exceptionState);
442     if (!exceptionState.hadException())
443         replaceChildrenWithFragment(this, fragment.release(), exceptionState);
444 }
445
446 void HTMLElement::setOuterText(const String& text, ExceptionState& exceptionState)
447 {
448     if (ieForbidsInsertHTML()) {
449         exceptionState.throwDOMException(NoModificationAllowedError, "The '" + localName() + "' element does not support text insertion.");
450         return;
451     }
452     if (shouldProhibitSetInnerOuterText(*this)) {
453         exceptionState.throwDOMException(NoModificationAllowedError, "The '" + localName() + "' element does not support text insertion.");
454         return;
455     }
456
457     ContainerNode* parent = parentNode();
458     if (!parent) {
459         exceptionState.throwDOMException(NoModificationAllowedError, "The element has no parent.");
460         return;
461     }
462
463     RefPtrWillBeRawPtr<Node> prev = previousSibling();
464     RefPtrWillBeRawPtr<Node> next = nextSibling();
465     RefPtrWillBeRawPtr<Node> newChild = nullptr;
466
467     // Convert text to fragment with <br> tags instead of linebreaks if needed.
468     if (text.contains('\r') || text.contains('\n'))
469         newChild = textToFragment(text, exceptionState);
470     else
471         newChild = Text::create(document(), text);
472
473     // textToFragment might cause mutation events.
474     if (!parentNode())
475         exceptionState.throwDOMException(HierarchyRequestError, "The element has no parent.");
476
477     if (exceptionState.hadException())
478         return;
479
480     parent->replaceChild(newChild.release(), this, exceptionState);
481
482     RefPtrWillBeRawPtr<Node> node = next ? next->previousSibling() : nullptr;
483     if (!exceptionState.hadException() && node && node->isTextNode())
484         mergeWithNextTextNode(toText(node.get()), exceptionState);
485
486     if (!exceptionState.hadException() && prev && prev->isTextNode())
487         mergeWithNextTextNode(toText(prev.get()), exceptionState);
488 }
489
490 void HTMLElement::applyAlignmentAttributeToStyle(const AtomicString& alignment, MutableStylePropertySet* style)
491 {
492     // Vertical alignment with respect to the current baseline of the text
493     // right or left means floating images.
494     CSSValueID floatValue = CSSValueInvalid;
495     CSSValueID verticalAlignValue = CSSValueInvalid;
496
497     if (equalIgnoringCase(alignment, "absmiddle"))
498         verticalAlignValue = CSSValueMiddle;
499     else if (equalIgnoringCase(alignment, "absbottom"))
500         verticalAlignValue = CSSValueBottom;
501     else if (equalIgnoringCase(alignment, "left")) {
502         floatValue = CSSValueLeft;
503         verticalAlignValue = CSSValueTop;
504     } else if (equalIgnoringCase(alignment, "right")) {
505         floatValue = CSSValueRight;
506         verticalAlignValue = CSSValueTop;
507     } else if (equalIgnoringCase(alignment, "top"))
508         verticalAlignValue = CSSValueTop;
509     else if (equalIgnoringCase(alignment, "middle"))
510         verticalAlignValue = CSSValueWebkitBaselineMiddle;
511     else if (equalIgnoringCase(alignment, "center"))
512         verticalAlignValue = CSSValueMiddle;
513     else if (equalIgnoringCase(alignment, "bottom"))
514         verticalAlignValue = CSSValueBaseline;
515     else if (equalIgnoringCase(alignment, "texttop"))
516         verticalAlignValue = CSSValueTextTop;
517
518     if (floatValue != CSSValueInvalid)
519         addPropertyToPresentationAttributeStyle(style, CSSPropertyFloat, floatValue);
520
521     if (verticalAlignValue != CSSValueInvalid)
522         addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, verticalAlignValue);
523 }
524
525 bool HTMLElement::hasCustomFocusLogic() const
526 {
527     return false;
528 }
529
530 String HTMLElement::contentEditable() const
531 {
532     const AtomicString& value = fastGetAttribute(contenteditableAttr);
533
534     if (value.isNull())
535         return "inherit";
536     if (value.isEmpty() || equalIgnoringCase(value, "true"))
537         return "true";
538     if (equalIgnoringCase(value, "false"))
539          return "false";
540     if (equalIgnoringCase(value, "plaintext-only"))
541         return "plaintext-only";
542
543     return "inherit";
544 }
545
546 void HTMLElement::setContentEditable(const String& enabled, ExceptionState& exceptionState)
547 {
548     if (equalIgnoringCase(enabled, "true"))
549         setAttribute(contenteditableAttr, "true");
550     else if (equalIgnoringCase(enabled, "false"))
551         setAttribute(contenteditableAttr, "false");
552     else if (equalIgnoringCase(enabled, "plaintext-only"))
553         setAttribute(contenteditableAttr, "plaintext-only");
554     else if (equalIgnoringCase(enabled, "inherit"))
555         removeAttribute(contenteditableAttr);
556     else
557         exceptionState.throwDOMException(SyntaxError, "The value provided ('" + enabled + "') is not one of 'true', 'false', 'plaintext-only', or 'inherit'.");
558 }
559
560 bool HTMLElement::draggable() const
561 {
562     return equalIgnoringCase(getAttribute(draggableAttr), "true");
563 }
564
565 void HTMLElement::setDraggable(bool value)
566 {
567     setAttribute(draggableAttr, value ? "true" : "false");
568 }
569
570 bool HTMLElement::spellcheck() const
571 {
572     return isSpellCheckingEnabled();
573 }
574
575 void HTMLElement::setSpellcheck(bool enable)
576 {
577     setAttribute(spellcheckAttr, enable ? "true" : "false");
578 }
579
580
581 void HTMLElement::click()
582 {
583     dispatchSimulatedClick(0, SendNoEvents);
584 }
585
586 void HTMLElement::accessKeyAction(bool sendMouseEvents)
587 {
588     dispatchSimulatedClick(0, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
589 }
590
591 String HTMLElement::title() const
592 {
593     return fastGetAttribute(titleAttr);
594 }
595
596 short HTMLElement::tabIndex() const
597 {
598     if (supportsFocus())
599         return Element::tabIndex();
600     return -1;
601 }
602
603 TranslateAttributeMode HTMLElement::translateAttributeMode() const
604 {
605     const AtomicString& value = getAttribute(translateAttr);
606
607     if (value == nullAtom)
608         return TranslateAttributeInherit;
609     if (equalIgnoringCase(value, "yes") || equalIgnoringCase(value, ""))
610         return TranslateAttributeYes;
611     if (equalIgnoringCase(value, "no"))
612         return TranslateAttributeNo;
613
614     return TranslateAttributeInherit;
615 }
616
617 bool HTMLElement::translate() const
618 {
619     for (const HTMLElement* element = this; element; element = Traversal<HTMLElement>::firstAncestor(*element)) {
620         TranslateAttributeMode mode = element->translateAttributeMode();
621         if (mode != TranslateAttributeInherit) {
622             ASSERT(mode == TranslateAttributeYes || mode == TranslateAttributeNo);
623             return mode == TranslateAttributeYes;
624         }
625     }
626
627     // Default on the root element is translate=yes.
628     return true;
629 }
630
631 void HTMLElement::setTranslate(bool enable)
632 {
633     setAttribute(translateAttr, enable ? "yes" : "no");
634 }
635
636 // Returns the conforming 'dir' value associated with the state the attribute is in (in its canonical case), if any,
637 // or the empty string if the attribute is in a state that has no associated keyword value or if the attribute is
638 // not in a defined state (e.g. the attribute is missing and there is no missing value default).
639 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#limited-to-only-known-values
640 static inline const AtomicString& toValidDirValue(const AtomicString& value)
641 {
642     DEFINE_STATIC_LOCAL(const AtomicString, ltrValue, ("ltr", AtomicString::ConstructFromLiteral));
643     DEFINE_STATIC_LOCAL(const AtomicString, rtlValue, ("rtl", AtomicString::ConstructFromLiteral));
644     DEFINE_STATIC_LOCAL(const AtomicString, autoValue, ("auto", AtomicString::ConstructFromLiteral));
645
646     if (equalIgnoringCase(value, ltrValue))
647         return ltrValue;
648     if (equalIgnoringCase(value, rtlValue))
649         return rtlValue;
650     if (equalIgnoringCase(value, autoValue))
651         return autoValue;
652     return nullAtom;
653 }
654
655 const AtomicString& HTMLElement::dir()
656 {
657     return toValidDirValue(fastGetAttribute(dirAttr));
658 }
659
660 void HTMLElement::setDir(const AtomicString& value)
661 {
662     setAttribute(dirAttr, value);
663 }
664
665 HTMLFormElement* HTMLElement::findFormAncestor() const
666 {
667     return Traversal<HTMLFormElement>::firstAncestor(*this);
668 }
669
670 static inline bool elementAffectsDirectionality(const Node* node)
671 {
672     return node->isHTMLElement() && (isHTMLBDIElement(toHTMLElement(*node)) || toHTMLElement(*node).hasAttribute(dirAttr));
673 }
674
675 static void setHasDirAutoFlagRecursively(Node* firstNode, bool flag, Node* lastNode = 0)
676 {
677     firstNode->setSelfOrAncestorHasDirAutoAttribute(flag);
678
679     Node* node = firstNode->firstChild();
680
681     while (node) {
682         if (elementAffectsDirectionality(node)) {
683             if (node == lastNode)
684                 return;
685             node = NodeTraversal::nextSkippingChildren(*node, firstNode);
686             continue;
687         }
688         node->setSelfOrAncestorHasDirAutoAttribute(flag);
689         if (node == lastNode)
690             return;
691         node = NodeTraversal::next(*node, firstNode);
692     }
693 }
694
695 void HTMLElement::childrenChanged(const ChildrenChange& change)
696 {
697     Element::childrenChanged(change);
698     adjustDirectionalityIfNeededAfterChildrenChanged(change);
699 }
700
701 bool HTMLElement::hasDirectionAuto() const
702 {
703     const AtomicString& direction = fastGetAttribute(dirAttr);
704     return (isHTMLBDIElement(*this) && direction == nullAtom) || equalIgnoringCase(direction, "auto");
705 }
706
707 TextDirection HTMLElement::directionalityIfhasDirAutoAttribute(bool& isAuto) const
708 {
709     if (!(selfOrAncestorHasDirAutoAttribute() && hasDirectionAuto())) {
710         isAuto = false;
711         return LTR;
712     }
713
714     isAuto = true;
715     return directionality();
716 }
717
718 TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) const
719 {
720     if (isHTMLInputElement(*this)) {
721         HTMLInputElement* inputElement = toHTMLInputElement(const_cast<HTMLElement*>(this));
722         bool hasStrongDirectionality;
723         TextDirection textDirection = determineDirectionality(inputElement->value(), hasStrongDirectionality);
724         if (strongDirectionalityTextNode)
725             *strongDirectionalityTextNode = hasStrongDirectionality ? inputElement : 0;
726         return textDirection;
727     }
728
729     Node* node = firstChild();
730     while (node) {
731         // Skip bdi, script, style and text form controls.
732         if (equalIgnoringCase(node->nodeName(), "bdi") || isHTMLScriptElement(*node) || isHTMLStyleElement(*node)
733             || (node->isElementNode() && toElement(node)->isTextFormControl())) {
734             node = NodeTraversal::nextSkippingChildren(*node, this);
735             continue;
736         }
737
738         // Skip elements with valid dir attribute
739         if (node->isElementNode()) {
740             AtomicString dirAttributeValue = toElement(node)->fastGetAttribute(dirAttr);
741             if (isValidDirAttribute(dirAttributeValue)) {
742                 node = NodeTraversal::nextSkippingChildren(*node, this);
743                 continue;
744             }
745         }
746
747         if (node->isTextNode()) {
748             bool hasStrongDirectionality;
749             TextDirection textDirection = determineDirectionality(node->textContent(true), hasStrongDirectionality);
750             if (hasStrongDirectionality) {
751                 if (strongDirectionalityTextNode)
752                     *strongDirectionalityTextNode = node;
753                 return textDirection;
754             }
755         }
756         node = NodeTraversal::next(*node, this);
757     }
758     if (strongDirectionalityTextNode)
759         *strongDirectionalityTextNode = 0;
760     return LTR;
761 }
762
763 void HTMLElement::dirAttributeChanged(const AtomicString& value)
764 {
765     Element* parent = parentElement();
766
767     if (parent && parent->isHTMLElement() && parent->selfOrAncestorHasDirAutoAttribute())
768         toHTMLElement(parent)->adjustDirectionalityIfNeededAfterChildAttributeChanged(this);
769
770     if (equalIgnoringCase(value, "auto"))
771         calculateAndAdjustDirectionality();
772 }
773
774 void HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child)
775 {
776     ASSERT(selfOrAncestorHasDirAutoAttribute());
777     Node* strongDirectionalityTextNode;
778     TextDirection textDirection = directionality(&strongDirectionalityTextNode);
779     setHasDirAutoFlagRecursively(child, false);
780     if (renderer() && renderer()->style() && renderer()->style()->direction() != textDirection) {
781         Element* elementToAdjust = this;
782         for (; elementToAdjust; elementToAdjust = elementToAdjust->parentElement()) {
783             if (elementAffectsDirectionality(elementToAdjust)) {
784                 elementToAdjust->setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::WritingModeChange));
785                 return;
786             }
787         }
788     }
789 }
790
791 void HTMLElement::calculateAndAdjustDirectionality()
792 {
793     Node* strongDirectionalityTextNode;
794     TextDirection textDirection = directionality(&strongDirectionalityTextNode);
795     setHasDirAutoFlagRecursively(this, hasDirectionAuto(), strongDirectionalityTextNode);
796     for (ShadowRoot* root = youngestShadowRoot(); root; root = root->olderShadowRoot())
797         setHasDirAutoFlagRecursively(root, hasDirectionAuto());
798     if (renderer() && renderer()->style() && renderer()->style()->direction() != textDirection)
799         setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::WritingModeChange));
800 }
801
802 void HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged(const ChildrenChange& change)
803 {
804     if (!selfOrAncestorHasDirAutoAttribute())
805         return;
806
807     Node* oldMarkedNode = change.siblingBeforeChange ? NodeTraversal::nextSkippingChildren(*change.siblingBeforeChange) : 0;
808     while (oldMarkedNode && elementAffectsDirectionality(oldMarkedNode))
809         oldMarkedNode = NodeTraversal::nextSkippingChildren(*oldMarkedNode, this);
810     if (oldMarkedNode)
811         setHasDirAutoFlagRecursively(oldMarkedNode, false);
812
813     for (Element* elementToAdjust = this; elementToAdjust; elementToAdjust = elementToAdjust->parentElement()) {
814         if (elementAffectsDirectionality(elementToAdjust)) {
815             toHTMLElement(elementToAdjust)->calculateAndAdjustDirectionality();
816             return;
817         }
818     }
819 }
820
821 void HTMLElement::addHTMLLengthToStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, const String& value)
822 {
823     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
824     // length unit and make the appropriate parsed value.
825
826     // strip attribute garbage..
827     StringImpl* v = value.impl();
828     if (v) {
829         unsigned length = 0;
830
831         while (length < v->length() && (*v)[length] <= ' ')
832             length++;
833
834         for (; length < v->length(); length++) {
835             UChar cc = (*v)[length];
836             if (cc > '9')
837                 break;
838             if (cc < '0') {
839                 if (cc == '%' || cc == '*')
840                     length++;
841                 if (cc != '.')
842                     break;
843             }
844         }
845
846         if (length != v->length()) {
847             addPropertyToPresentationAttributeStyle(style, propertyID, v->substring(0, length));
848             return;
849         }
850     }
851
852     addPropertyToPresentationAttributeStyle(style, propertyID, value);
853 }
854
855 static RGBA32 parseColorStringWithCrazyLegacyRules(const String& colorString)
856 {
857     // Per spec, only look at the first 128 digits of the string.
858     const size_t maxColorLength = 128;
859     // We'll pad the buffer with two extra 0s later, so reserve two more than the max.
860     Vector<char, maxColorLength+2> digitBuffer;
861
862     size_t i = 0;
863     // Skip a leading #.
864     if (colorString[0] == '#')
865         i = 1;
866
867     // Grab the first 128 characters, replacing non-hex characters with 0.
868     // Non-BMP characters are replaced with "00" due to them appearing as two "characters" in the String.
869     for (; i < colorString.length() && digitBuffer.size() < maxColorLength; i++) {
870         if (!isASCIIHexDigit(colorString[i]))
871             digitBuffer.append('0');
872         else
873             digitBuffer.append(colorString[i]);
874     }
875
876     if (!digitBuffer.size())
877         return Color::black;
878
879     // Pad the buffer out to at least the next multiple of three in size.
880     digitBuffer.append('0');
881     digitBuffer.append('0');
882
883     if (digitBuffer.size() < 6)
884         return makeRGB(toASCIIHexValue(digitBuffer[0]), toASCIIHexValue(digitBuffer[1]), toASCIIHexValue(digitBuffer[2]));
885
886     // Split the digits into three components, then search the last 8 digits of each component.
887     ASSERT(digitBuffer.size() >= 6);
888     size_t componentLength = digitBuffer.size() / 3;
889     size_t componentSearchWindowLength = min<size_t>(componentLength, 8);
890     size_t redIndex = componentLength - componentSearchWindowLength;
891     size_t greenIndex = componentLength * 2 - componentSearchWindowLength;
892     size_t blueIndex = componentLength * 3 - componentSearchWindowLength;
893     // Skip digits until one of them is non-zero, or we've only got two digits left in the component.
894     while (digitBuffer[redIndex] == '0' && digitBuffer[greenIndex] == '0' && digitBuffer[blueIndex] == '0' && (componentLength - redIndex) > 2) {
895         redIndex++;
896         greenIndex++;
897         blueIndex++;
898     }
899     ASSERT(redIndex + 1 < componentLength);
900     ASSERT(greenIndex >= componentLength);
901     ASSERT(greenIndex + 1 < componentLength * 2);
902     ASSERT(blueIndex >= componentLength * 2);
903     ASSERT_WITH_SECURITY_IMPLICATION(blueIndex + 1 < digitBuffer.size());
904
905     int redValue = toASCIIHexValue(digitBuffer[redIndex], digitBuffer[redIndex + 1]);
906     int greenValue = toASCIIHexValue(digitBuffer[greenIndex], digitBuffer[greenIndex + 1]);
907     int blueValue = toASCIIHexValue(digitBuffer[blueIndex], digitBuffer[blueIndex + 1]);
908     return makeRGB(redValue, greenValue, blueValue);
909 }
910
911 // Color parsing that matches HTML's "rules for parsing a legacy color value"
912 void HTMLElement::addHTMLColorToStyle(MutableStylePropertySet* style, CSSPropertyID propertyID, const String& attributeValue)
913 {
914     // An empty string doesn't apply a color. (One containing only whitespace does, which is why this check occurs before stripping.)
915     if (attributeValue.isEmpty())
916         return;
917
918     String colorString = attributeValue.stripWhiteSpace();
919
920     // "transparent" doesn't apply a color either.
921     if (equalIgnoringCase(colorString, "transparent"))
922         return;
923
924     // If the string is a named CSS color or a 3/6-digit hex color, use that.
925     Color parsedColor;
926     if (!parsedColor.setFromString(colorString))
927         parsedColor.setRGB(parseColorStringWithCrazyLegacyRules(colorString));
928
929     style->setProperty(propertyID, cssValuePool().createColorValue(parsedColor.rgb()));
930 }
931
932 bool HTMLElement::isInteractiveContent() const
933 {
934     return false;
935 }
936
937
938 HTMLMenuElement* HTMLElement::contextMenu() const
939 {
940     const AtomicString& contextMenuId(fastGetAttribute(contextmenuAttr));
941     if (contextMenuId.isNull())
942         return nullptr;
943
944     Element* element = treeScope().getElementById(contextMenuId);
945     // Not checking if the menu element is of type "popup".
946     // Ignoring menu element type attribute is intentional according to the standard.
947     return isHTMLMenuElement(element) ? toHTMLMenuElement(element) : nullptr;
948 }
949
950 void HTMLElement::setContextMenu(HTMLMenuElement* contextMenu)
951 {
952     if (!contextMenu) {
953         setAttribute(contextmenuAttr, "");
954         return;
955     }
956
957     // http://www.whatwg.org/specs/web-apps/current-work/multipage/infrastructure.html#reflecting-content-attributes-in-idl-attributes
958     // On setting, if the given element has an id attribute, and has the same home
959     // subtree as the element of the attribute being set, and the given element is the
960     // first element in that home subtree whose ID is the value of that id attribute,
961     // then the content attribute must be set to the value of that id attribute.
962     // Otherwise, the content attribute must be set to the empty string.
963     const AtomicString& contextMenuId(contextMenu->fastGetAttribute(idAttr));
964
965     if (!contextMenuId.isNull() && contextMenu == treeScope().getElementById(contextMenuId))
966         setAttribute(contextmenuAttr, contextMenuId);
967     else
968         setAttribute(contextmenuAttr, "");
969 }
970
971 void HTMLElement::defaultEventHandler(Event* event)
972 {
973     if (event->type() == EventTypeNames::keypress && event->isKeyboardEvent()) {
974         handleKeypressEvent(toKeyboardEvent(event));
975         if (event->defaultHandled())
976             return;
977     }
978
979     Element::defaultEventHandler(event);
980 }
981
982 bool HTMLElement::matchesReadOnlyPseudoClass() const
983 {
984     return !matchesReadWritePseudoClass();
985 }
986
987 bool HTMLElement::matchesReadWritePseudoClass() const
988 {
989     if (fastHasAttribute(contenteditableAttr)) {
990         const AtomicString& value = fastGetAttribute(contenteditableAttr);
991
992         if (value.isEmpty() || equalIgnoringCase(value, "true") || equalIgnoringCase(value, "plaintext-only"))
993             return true;
994         if (equalIgnoringCase(value, "false"))
995             return false;
996         // All other values should be treated as "inherit".
997     }
998
999     return parentElement() && parentElement()->hasEditableStyle();
1000 }
1001
1002 void HTMLElement::handleKeypressEvent(KeyboardEvent* event)
1003 {
1004     if (!document().settings() || !document().settings()->spatialNavigationEnabled() || !supportsFocus())
1005         return;
1006     // if the element is a text form control (like <input type=text> or <textarea>)
1007     // or has contentEditable attribute on, we should enter a space or newline
1008     // even in spatial navigation mode instead of handling it as a "click" action.
1009     if (isTextFormControl() || isContentEditable())
1010         return;
1011     int charCode = event->charCode();
1012     if (charCode == '\r' || charCode == ' ') {
1013         dispatchSimulatedClick(event);
1014         event->setDefaultHandled();
1015     }
1016 }
1017
1018 const AtomicString& HTMLElement::eventParameterName()
1019 {
1020     DEFINE_STATIC_LOCAL(const AtomicString, eventString, ("event", AtomicString::ConstructFromLiteral));
1021     return eventString;
1022 }
1023
1024 } // namespace blink
1025
1026 #ifndef NDEBUG
1027
1028 // For use in the debugger
1029 void dumpInnerHTML(blink::HTMLElement*);
1030
1031 void dumpInnerHTML(blink::HTMLElement* element)
1032 {
1033     printf("%s\n", element->innerHTML().ascii().data());
1034 }
1035 #endif