[Win] Warning fixes.
[WebKit-https.git] / Source / WebCore / html / HTMLElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5  * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
6  * Copyright (C) 2011 Motorola Mobility. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "HTMLElement.h"
27
28 #include "CSSParser.h"
29 #include "CSSPropertyNames.h"
30 #include "CSSValueKeywords.h"
31 #include "CSSValuePool.h"
32 #include "DOMTokenList.h"
33 #include "DocumentFragment.h"
34 #include "ElementAncestorIterator.h"
35 #include "Event.h"
36 #include "EventListener.h"
37 #include "EventNames.h"
38 #include "ExceptionCode.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "FrameView.h"
42 #include "HTMLBDIElement.h"
43 #include "HTMLBRElement.h"
44 #include "HTMLCollection.h"
45 #include "HTMLDocument.h"
46 #include "HTMLElementFactory.h"
47 #include "HTMLFormElement.h"
48 #include "HTMLNames.h"
49 #include "HTMLParserIdioms.h"
50 #include "HTMLTextFormControlElement.h"
51 #include "NodeTraversal.h"
52 #include "RenderElement.h"
53 #include "ScriptController.h"
54 #include "Settings.h"
55 #include "StyleProperties.h"
56 #include "SubframeLoader.h"
57 #include "Text.h"
58 #include "XMLNames.h"
59 #include "markup.h"
60 #include <wtf/NeverDestroyed.h>
61 #include <wtf/StdLibExtras.h>
62 #include <wtf/text/CString.h>
63
64 namespace WebCore {
65
66 using namespace HTMLNames;
67 using namespace WTF;
68
69 Ref<HTMLElement> HTMLElement::create(const QualifiedName& tagName, Document& document)
70 {
71     return adoptRef(*new HTMLElement(tagName, document));
72 }
73
74 String HTMLElement::nodeName() const
75 {
76     // FIXME: Would be nice to have an AtomicString lookup based off uppercase
77     // ASCII characters that does not have to copy the string on a hit in the hash.
78     if (document().isHTMLDocument()) {
79         if (LIKELY(!tagQName().hasPrefix()))
80             return tagQName().localNameUpper();
81         return Element::nodeName().convertToASCIIUppercase();
82     }
83     return Element::nodeName();
84 }
85
86 static inline CSSValueID unicodeBidiAttributeForDirAuto(HTMLElement& element)
87 {
88     if (element.hasTagName(preTag) || element.hasTagName(textareaTag))
89         return CSSValueWebkitPlaintext;
90     // FIXME: For bdo element, dir="auto" should result in "bidi-override isolate" but we don't support having multiple values in unicode-bidi yet.
91     // See https://bugs.webkit.org/show_bug.cgi?id=73164.
92     return CSSValueWebkitIsolate;
93 }
94
95 unsigned HTMLElement::parseBorderWidthAttribute(const AtomicString& value) const
96 {
97     if (Optional<int> borderWidth = parseHTMLNonNegativeInteger(value))
98         return borderWidth.value();
99
100     return hasTagName(tableTag) ? 1 : 0;
101 }
102
103 void HTMLElement::applyBorderAttributeToStyle(const AtomicString& value, MutableStyleProperties& style)
104 {
105     addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderWidth, parseBorderWidthAttribute(value), CSSPrimitiveValue::CSS_PX);
106     addPropertyToPresentationAttributeStyle(style, CSSPropertyBorderStyle, CSSValueSolid);
107 }
108
109 void HTMLElement::mapLanguageAttributeToLocale(const AtomicString& value, MutableStyleProperties& style)
110 {
111     if (!value.isEmpty()) {
112         // Have to quote so the locale id is treated as a string instead of as a CSS keyword.
113         addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, quoteCSSString(value));
114     } else {
115         // The empty string means the language is explicitly unknown.
116         addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, CSSValueAuto);
117     }
118 }
119
120 bool HTMLElement::isPresentationAttribute(const QualifiedName& name) const
121 {
122     if (name == alignAttr || name == contenteditableAttr || name == hiddenAttr || name == langAttr || name.matches(XMLNames::langAttr) || name == draggableAttr || name == dirAttr)
123         return true;
124     return StyledElement::isPresentationAttribute(name);
125 }
126
127 static bool isLTROrRTLIgnoringCase(const AtomicString& dirAttributeValue)
128 {
129     return equalLettersIgnoringASCIICase(dirAttributeValue, "rtl") || equalLettersIgnoringASCIICase(dirAttributeValue, "ltr");
130 }
131
132 enum class ContentEditableType {
133     Inherit,
134     True,
135     False,
136     PlaintextOnly
137 };
138
139 static inline ContentEditableType contentEditableType(const AtomicString& value)
140 {
141     if (value.isNull())
142         return ContentEditableType::Inherit;
143     if (value.isEmpty() || equalLettersIgnoringASCIICase(value, "true"))
144         return ContentEditableType::True;
145     if (equalLettersIgnoringASCIICase(value, "false"))
146         return ContentEditableType::False;
147     if (equalLettersIgnoringASCIICase(value, "plaintext-only"))
148         return ContentEditableType::PlaintextOnly;
149
150     return ContentEditableType::Inherit;
151 }
152
153 static ContentEditableType contentEditableType(const HTMLElement& element)
154 {
155     return contentEditableType(element.attributeWithoutSynchronization(contenteditableAttr));
156 }
157
158 void HTMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
159 {
160     if (name == alignAttr) {
161         if (equalLettersIgnoringASCIICase(value, "middle"))
162             addPropertyToPresentationAttributeStyle(style, CSSPropertyTextAlign, CSSValueCenter);
163         else
164             addPropertyToPresentationAttributeStyle(style, CSSPropertyTextAlign, value);
165     } else if (name == contenteditableAttr) {
166         CSSValueID userModifyValue = CSSValueReadWrite;
167         switch (contentEditableType(value)) {
168         case ContentEditableType::Inherit:
169             return;
170         case ContentEditableType::False:
171             userModifyValue = CSSValueReadOnly;
172             break;
173         case ContentEditableType::PlaintextOnly:
174             userModifyValue = CSSValueReadWritePlaintextOnly;
175             FALLTHROUGH;
176         case ContentEditableType::True:
177             addPropertyToPresentationAttributeStyle(style, CSSPropertyWordWrap, CSSValueBreakWord);
178             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitNbspMode, CSSValueSpace);
179             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace);
180 #if PLATFORM(IOS)
181             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitTextSizeAdjust, CSSValueNone);
182 #endif
183             break;
184         }
185         addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, userModifyValue);
186     } else if (name == hiddenAttr) {
187         addPropertyToPresentationAttributeStyle(style, CSSPropertyDisplay, CSSValueNone);
188     } else if (name == draggableAttr) {
189         if (equalLettersIgnoringASCIICase(value, "true")) {
190             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserDrag, CSSValueElement);
191             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserSelect, CSSValueNone);
192         } else if (equalLettersIgnoringASCIICase(value, "false"))
193             addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserDrag, CSSValueNone);
194     } else if (name == dirAttr) {
195         if (equalLettersIgnoringASCIICase(value, "auto"))
196             addPropertyToPresentationAttributeStyle(style, CSSPropertyUnicodeBidi, unicodeBidiAttributeForDirAuto(*this));
197         else {
198             if (isLTROrRTLIgnoringCase(value))
199                 addPropertyToPresentationAttributeStyle(style, CSSPropertyDirection, value);
200             if (!hasTagName(bdiTag) && !hasTagName(bdoTag) && !hasTagName(outputTag))
201                 addPropertyToPresentationAttributeStyle(style, CSSPropertyUnicodeBidi, CSSValueEmbed);
202         }
203     } else if (name.matches(XMLNames::langAttr))
204         mapLanguageAttributeToLocale(value, style);
205     else if (name == langAttr) {
206         // xml:lang has a higher priority than lang.
207         if (!hasAttributeWithoutSynchronization(XMLNames::langAttr))
208             mapLanguageAttributeToLocale(value, style);
209     } else
210         StyledElement::collectStyleForPresentationAttribute(name, value, style);
211 }
212
213 HTMLElement::EventHandlerNameMap HTMLElement::createEventHandlerNameMap()
214 {
215     EventHandlerNameMap map;
216
217     static const QualifiedName* const table[] = {
218         &onabortAttr,
219         &onanimationendAttr,
220         &onanimationiterationAttr,
221         &onanimationstartAttr,
222         &onautocompleteAttr,
223         &onautocompleteerrorAttr,
224         &onbeforecopyAttr,
225         &onbeforecutAttr,
226         &onbeforeloadAttr,
227         &onbeforepasteAttr,
228         &onblurAttr,
229         &oncanplayAttr,
230         &oncanplaythroughAttr,
231         &onchangeAttr,
232         &onclickAttr,
233         &oncontextmenuAttr,
234         &oncopyAttr,
235         &oncutAttr,
236         &ondblclickAttr,
237         &ondragAttr,
238         &ondragendAttr,
239         &ondragenterAttr,
240         &ondragleaveAttr,
241         &ondragoverAttr,
242         &ondragstartAttr,
243         &ondropAttr,
244         &ondurationchangeAttr,
245         &onemptiedAttr,
246         &onendedAttr,
247         &onerrorAttr,
248         &onfocusAttr,
249         &onfocusinAttr,
250         &onfocusoutAttr,
251         &ongesturechangeAttr,
252         &ongestureendAttr,
253         &ongesturestartAttr,
254         &oninputAttr,
255         &oninvalidAttr,
256         &onkeydownAttr,
257         &onkeypressAttr,
258         &onkeyupAttr,
259         &onloadAttr,
260         &onloadeddataAttr,
261         &onloadedmetadataAttr,
262         &onloadstartAttr,
263         &onmousedownAttr,
264         &onmouseenterAttr,
265         &onmouseleaveAttr,
266         &onmousemoveAttr,
267         &onmouseoutAttr,
268         &onmouseoverAttr,
269         &onmouseupAttr,
270         &onmousewheelAttr,
271         &onpasteAttr,
272         &onpauseAttr,
273         &onplayAttr,
274         &onplayingAttr,
275         &onprogressAttr,
276         &onratechangeAttr,
277         &onresetAttr,
278         &onresizeAttr,
279         &onscrollAttr,
280         &onsearchAttr,
281         &onseekedAttr,
282         &onseekingAttr,
283         &onselectAttr,
284         &onselectstartAttr,
285         &onstalledAttr,
286         &onsubmitAttr,
287         &onsuspendAttr,
288         &ontimeupdateAttr,
289         &ontouchcancelAttr,
290         &ontouchendAttr,
291         &ontouchforcechangeAttr,
292         &ontouchmoveAttr,
293         &ontouchstartAttr,
294         &ontransitionendAttr,
295         &onvolumechangeAttr,
296         &onwaitingAttr,
297         &onwebkitbeginfullscreenAttr,
298         &onwebkitcurrentplaybacktargetiswirelesschangedAttr,
299         &onwebkitendfullscreenAttr,
300         &onwebkitfullscreenchangeAttr,
301         &onwebkitfullscreenerrorAttr,
302         &onwebkitkeyaddedAttr,
303         &onwebkitkeyerrorAttr,
304         &onwebkitkeymessageAttr,
305         &onwebkitmouseforcechangedAttr,
306         &onwebkitmouseforcedownAttr,
307         &onwebkitmouseforcewillbeginAttr,
308         &onwebkitmouseforceupAttr,
309         &onwebkitneedkeyAttr,
310         &onwebkitplaybacktargetavailabilitychangedAttr,
311         &onwebkitpresentationmodechangedAttr,
312         &onwebkitwillrevealbottomAttr,
313         &onwebkitwillrevealleftAttr,
314         &onwebkitwillrevealrightAttr,
315         &onwebkitwillrevealtopAttr,
316         &onwheelAttr,
317     };
318
319     populateEventHandlerNameMap(map, table);
320
321     struct UnusualMapping {
322         const QualifiedName& attributeName;
323         const AtomicString& eventName;
324     };
325
326     const UnusualMapping unusualPairsTable[] = {
327         { onwebkitanimationendAttr, eventNames().webkitAnimationEndEvent },
328         { onwebkitanimationiterationAttr, eventNames().webkitAnimationIterationEvent },
329         { onwebkitanimationstartAttr, eventNames().webkitAnimationStartEvent },
330         { onwebkittransitionendAttr, eventNames().webkitTransitionEndEvent },
331     };
332
333     for (auto& entry : unusualPairsTable)
334         map.add(entry.attributeName.localName().impl(), entry.eventName);
335
336     return map;
337 }
338
339 void HTMLElement::populateEventHandlerNameMap(EventHandlerNameMap& map, const QualifiedName* const table[], size_t tableSize)
340 {
341     for (size_t i = 0; i < tableSize; ++i) {
342         auto* entry = table[i];
343
344         // FIXME: Would be nice to check these against the actual event names in eventNames().
345         // Not obvious how to do that simply, though.
346         auto& attributeName = entry->localName();
347
348         // Remove the "on" prefix. Requires some memory allocation and computing a hash, but by not
349         // using pointers from eventNames(), the passed-in table can be initialized at compile time.
350         AtomicString eventName = attributeName.string().substring(2);
351
352         map.add(attributeName.impl(), WTFMove(eventName));
353     }
354 }
355
356 const AtomicString& HTMLElement::eventNameForEventHandlerAttribute(const QualifiedName& attributeName, const EventHandlerNameMap& map)
357 {
358     ASSERT(!attributeName.localName().isNull());
359
360     // Event handler attributes have no namespace.
361     if (!attributeName.namespaceURI().isNull())
362         return nullAtom;
363
364     // Fast early return for names that don't start with "on".
365     AtomicStringImpl& localName = *attributeName.localName().impl();
366     if (localName.length() < 3 || localName[0] != 'o' || localName[1] != 'n')
367         return nullAtom;
368
369     auto it = map.find(&localName);
370     return it == map.end() ? nullAtom : it->value;
371 }
372
373 const AtomicString& HTMLElement::eventNameForEventHandlerAttribute(const QualifiedName& attributeName)
374 {
375     static NeverDestroyed<EventHandlerNameMap> map = createEventHandlerNameMap();
376     return eventNameForEventHandlerAttribute(attributeName, map.get());
377 }
378
379 Node::Editability HTMLElement::editabilityFromContentEditableAttr(const Node& node)
380 {
381     if (auto* startElement = is<Element>(node) ? &downcast<Element>(node) : node.parentElement()) {
382         for (auto& element : lineageOfType<HTMLElement>(*startElement)) {
383             switch (contentEditableType(element)) {
384             case ContentEditableType::True:
385                 return Editability::CanEditRichly;
386             case ContentEditableType::PlaintextOnly:
387                 return Editability::CanEditPlainText;
388             case ContentEditableType::False:
389                 return Editability::ReadOnly;
390             case ContentEditableType::Inherit:
391                 break;
392             }
393         }
394     }
395
396     auto& document = node.document();
397     if (is<HTMLDocument>(document))
398         return downcast<HTMLDocument>(document).inDesignMode() ? Editability::CanEditRichly : Editability::ReadOnly;
399
400     return Editability::ReadOnly;
401 }
402
403 bool HTMLElement::matchesReadWritePseudoClass() const
404 {
405     return editabilityFromContentEditableAttr(*this) != Editability::ReadOnly;
406 }
407
408 void HTMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
409 {
410     if (name == dirAttr) {
411         dirAttributeChanged(value);
412         return;
413     }
414
415     if (name == tabindexAttr) {
416         if (value.isEmpty())
417             clearTabIndexExplicitlyIfNeeded();
418         else if (Optional<int> tabIndex = parseHTMLInteger(value))
419             setTabIndexExplicitly(tabIndex.value());
420         return;
421     }
422
423     auto& eventName = eventNameForEventHandlerAttribute(name);
424     if (!eventName.isNull())
425         setAttributeEventListener(eventName, name, value);
426 }
427
428 Ref<DocumentFragment> HTMLElement::textToFragment(const String& text, ExceptionCode& ec)
429 {
430     ec = 0;
431
432     auto fragment = DocumentFragment::create(document());
433
434     for (unsigned start = 0, length = text.length(); start < length; ) {
435
436         // Find next line break.
437         UChar c = 0;
438         unsigned i;
439         for (i = start; i < length; i++) {
440             c = text[i];
441             if (c == '\r' || c == '\n')
442                 break;
443         }
444
445         fragment->appendChild(Text::create(document(), text.substring(start, i - start)), ec);
446         if (ec)
447             break;
448
449         if (i == length)
450             break;
451
452         fragment->appendChild(HTMLBRElement::create(document()), ec);
453         if (ec)
454             break;
455
456         // Make sure \r\n doesn't result in two line breaks.
457         if (c == '\r' && i + 1 < length && text[i + 1] == '\n')
458             ++i;
459
460         start = i + 1; // Character after line break.
461     }
462
463     return fragment;
464 }
465
466 static inline bool shouldProhibitSetInnerOuterText(const HTMLElement& element)
467 {
468     return element.hasTagName(colTag)
469         || element.hasTagName(colgroupTag)
470         || element.hasTagName(framesetTag)
471         || element.hasTagName(headTag)
472         || element.hasTagName(htmlTag)
473         || element.hasTagName(tableTag)
474         || element.hasTagName(tbodyTag)
475         || element.hasTagName(tfootTag)
476         || element.hasTagName(theadTag)
477         || element.hasTagName(trTag);
478 }
479
480 // Returns the conforming 'dir' value associated with the state the attribute is in (in its canonical case), if any,
481 // or the empty string if the attribute is in a state that has no associated keyword value or if the attribute is
482 // not in a defined state (e.g. the attribute is missing and there is no missing value default).
483 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#limited-to-only-known-values
484 static inline const AtomicString& toValidDirValue(const AtomicString& value)
485 {
486     static NeverDestroyed<AtomicString> ltrValue("ltr", AtomicString::ConstructFromLiteral);
487     static NeverDestroyed<AtomicString> rtlValue("rtl", AtomicString::ConstructFromLiteral);
488     static NeverDestroyed<AtomicString> autoValue("auto", AtomicString::ConstructFromLiteral);
489     if (equalLettersIgnoringASCIICase(value, "ltr"))
490         return ltrValue;
491     if (equalLettersIgnoringASCIICase(value, "rtl"))
492         return rtlValue;
493     if (equalLettersIgnoringASCIICase(value, "auto"))
494         return autoValue;
495     return nullAtom;
496 }
497
498 const AtomicString& HTMLElement::dir() const
499 {
500     return toValidDirValue(attributeWithoutSynchronization(dirAttr));
501 }
502
503 void HTMLElement::setDir(const AtomicString& value)
504 {
505     setAttributeWithoutSynchronization(dirAttr, value);
506 }
507
508 void HTMLElement::setInnerText(const String& text, ExceptionCode& ec)
509 {
510     if (ieForbidsInsertHTML()) {
511         ec = NO_MODIFICATION_ALLOWED_ERR;
512         return;
513     }
514     if (shouldProhibitSetInnerOuterText(*this)) {
515         ec = NO_MODIFICATION_ALLOWED_ERR;
516         return;
517     }
518
519     // FIXME: This doesn't take whitespace collapsing into account at all.
520
521     if (!text.contains('\n') && !text.contains('\r')) {
522         if (text.isEmpty()) {
523             removeChildren();
524             return;
525         }
526         replaceChildrenWithText(*this, text, ec);
527         return;
528     }
529
530     // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
531     // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded?
532     // For example, for the contents of textarea elements that are display:none?
533     auto r = renderer();
534     if ((r && r->style().preserveNewline()) || (inDocument() && isTextControlInnerTextElement())) {
535         if (!text.contains('\r')) {
536             replaceChildrenWithText(*this, text, ec);
537             return;
538         }
539         String textWithConsistentLineBreaks = text;
540         textWithConsistentLineBreaks.replace("\r\n", "\n");
541         textWithConsistentLineBreaks.replace('\r', '\n');
542         replaceChildrenWithText(*this, textWithConsistentLineBreaks, ec);
543         return;
544     }
545
546     // Add text nodes and <br> elements.
547     ec = 0;
548     Ref<DocumentFragment> fragment = textToFragment(text, ec);
549     if (!ec)
550         replaceChildrenWithFragment(*this, WTFMove(fragment), ec);
551 }
552
553 void HTMLElement::setOuterText(const String& text, ExceptionCode& ec)
554 {
555     if (ieForbidsInsertHTML()) {
556         ec = NO_MODIFICATION_ALLOWED_ERR;
557         return;
558     }
559     if (shouldProhibitSetInnerOuterText(*this)) {
560         ec = NO_MODIFICATION_ALLOWED_ERR;
561         return;
562     }
563
564     RefPtr<ContainerNode> parent = parentNode();
565     if (!parent) {
566         ec = NO_MODIFICATION_ALLOWED_ERR;
567         return;
568     }
569
570     RefPtr<Node> prev = previousSibling();
571     RefPtr<Node> next = nextSibling();
572     RefPtr<Node> newChild;
573     ec = 0;
574     
575     // Convert text to fragment with <br> tags instead of linebreaks if needed.
576     if (text.contains('\r') || text.contains('\n'))
577         newChild = textToFragment(text, ec);
578     else
579         newChild = Text::create(document(), text);
580
581     if (!parentNode())
582         ec = HIERARCHY_REQUEST_ERR;
583     if (ec)
584         return;
585     parent->replaceChild(*newChild, *this, ec);
586
587     RefPtr<Node> node = next ? next->previousSibling() : nullptr;
588     if (!ec && is<Text>(node.get()))
589         mergeWithNextTextNode(downcast<Text>(*node), ec);
590     if (!ec && is<Text>(prev.get()))
591         mergeWithNextTextNode(downcast<Text>(*prev), ec);
592 }
593
594 void HTMLElement::applyAlignmentAttributeToStyle(const AtomicString& alignment, MutableStyleProperties& style)
595 {
596     // Vertical alignment with respect to the current baseline of the text
597     // right or left means floating images.
598     CSSValueID floatValue = CSSValueInvalid;
599     CSSValueID verticalAlignValue = CSSValueInvalid;
600
601     if (equalLettersIgnoringASCIICase(alignment, "absmiddle"))
602         verticalAlignValue = CSSValueMiddle;
603     else if (equalLettersIgnoringASCIICase(alignment, "absbottom"))
604         verticalAlignValue = CSSValueBottom;
605     else if (equalLettersIgnoringASCIICase(alignment, "left")) {
606         floatValue = CSSValueLeft;
607         verticalAlignValue = CSSValueTop;
608     } else if (equalLettersIgnoringASCIICase(alignment, "right")) {
609         floatValue = CSSValueRight;
610         verticalAlignValue = CSSValueTop;
611     } else if (equalLettersIgnoringASCIICase(alignment, "top"))
612         verticalAlignValue = CSSValueTop;
613     else if (equalLettersIgnoringASCIICase(alignment, "middle"))
614         verticalAlignValue = CSSValueWebkitBaselineMiddle;
615     else if (equalLettersIgnoringASCIICase(alignment, "center"))
616         verticalAlignValue = CSSValueMiddle;
617     else if (equalLettersIgnoringASCIICase(alignment, "bottom"))
618         verticalAlignValue = CSSValueBaseline;
619     else if (equalLettersIgnoringASCIICase(alignment, "texttop"))
620         verticalAlignValue = CSSValueTextTop;
621
622     if (floatValue != CSSValueInvalid)
623         addPropertyToPresentationAttributeStyle(style, CSSPropertyFloat, floatValue);
624
625     if (verticalAlignValue != CSSValueInvalid)
626         addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, verticalAlignValue);
627 }
628
629 bool HTMLElement::hasCustomFocusLogic() const
630 {
631     return false;
632 }
633
634 bool HTMLElement::supportsFocus() const
635 {
636     return Element::supportsFocus() || (hasEditableStyle() && parentNode() && !parentNode()->hasEditableStyle());
637 }
638
639 String HTMLElement::contentEditable() const
640 {
641     switch (contentEditableType(*this)) {
642     case ContentEditableType::Inherit:
643         return ASCIILiteral("inherit");
644     case ContentEditableType::True:
645         return ASCIILiteral("true");
646     case ContentEditableType::False:
647         return ASCIILiteral("false");
648     case ContentEditableType::PlaintextOnly:
649         return ASCIILiteral("plaintext-only");
650     }
651     return ASCIILiteral("inherit");
652 }
653
654 void HTMLElement::setContentEditable(const String& enabled, ExceptionCode& ec)
655 {
656     if (equalLettersIgnoringASCIICase(enabled, "true"))
657         setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("true", AtomicString::ConstructFromLiteral));
658     else if (equalLettersIgnoringASCIICase(enabled, "false"))
659         setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("false", AtomicString::ConstructFromLiteral));
660     else if (equalLettersIgnoringASCIICase(enabled, "plaintext-only"))
661         setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("plaintext-only", AtomicString::ConstructFromLiteral));
662     else if (equalLettersIgnoringASCIICase(enabled, "inherit"))
663         removeAttribute(contenteditableAttr);
664     else
665         ec = SYNTAX_ERR;
666 }
667
668 bool HTMLElement::draggable() const
669 {
670     return equalLettersIgnoringASCIICase(attributeWithoutSynchronization(draggableAttr), "true");
671 }
672
673 void HTMLElement::setDraggable(bool value)
674 {
675     setAttributeWithoutSynchronization(draggableAttr, value
676         ? AtomicString("true", AtomicString::ConstructFromLiteral)
677         : AtomicString("false", AtomicString::ConstructFromLiteral));
678 }
679
680 bool HTMLElement::spellcheck() const
681 {
682     return isSpellCheckingEnabled();
683 }
684
685 void HTMLElement::setSpellcheck(bool enable)
686 {
687     setAttributeWithoutSynchronization(spellcheckAttr, enable
688         ? AtomicString("true", AtomicString::ConstructFromLiteral)
689         : AtomicString("false", AtomicString::ConstructFromLiteral));
690 }
691
692 void HTMLElement::click()
693 {
694     dispatchSimulatedClickForBindings(nullptr);
695 }
696
697 void HTMLElement::accessKeyAction(bool sendMouseEvents)
698 {
699     dispatchSimulatedClick(nullptr, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
700 }
701
702 String HTMLElement::title() const
703 {
704     return attributeWithoutSynchronization(titleAttr);
705 }
706
707 int HTMLElement::tabIndex() const
708 {
709     if (supportsFocus())
710         return Element::tabIndex();
711     return -1;
712 }
713
714 TranslateAttributeMode HTMLElement::translateAttributeMode() const
715 {
716     const AtomicString& value = attributeWithoutSynchronization(translateAttr);
717
718     if (value.isNull())
719         return TranslateAttributeInherit;
720     if (equalLettersIgnoringASCIICase(value, "yes") || value.isEmpty())
721         return TranslateAttributeYes;
722     if (equalLettersIgnoringASCIICase(value, "no"))
723         return TranslateAttributeNo;
724
725     return TranslateAttributeInherit;
726 }
727
728 bool HTMLElement::translate() const
729 {
730     for (auto& element : lineageOfType<HTMLElement>(*this)) {
731         TranslateAttributeMode mode = element.translateAttributeMode();
732         if (mode == TranslateAttributeInherit)
733             continue;
734         ASSERT(mode == TranslateAttributeYes || mode == TranslateAttributeNo);
735         return mode == TranslateAttributeYes;
736     }
737
738     // Default on the root element is translate=yes.
739     return true;
740 }
741
742 void HTMLElement::setTranslate(bool enable)
743 {
744     setAttributeWithoutSynchronization(translateAttr, enable ? "yes" : "no");
745 }
746
747 bool HTMLElement::rendererIsNeeded(const RenderStyle& style)
748 {
749     if (hasTagName(noscriptTag)) {
750         Frame* frame = document().frame();
751         if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript))
752             return false;
753     } else if (hasTagName(noembedTag)) {
754         Frame* frame = document().frame();
755         if (frame && frame->loader().subframeLoader().allowPlugins())
756             return false;
757     }
758     return StyledElement::rendererIsNeeded(style);
759 }
760
761 RenderPtr<RenderElement> HTMLElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
762 {
763     return RenderElement::createFor(*this, WTFMove(style));
764 }
765
766 HTMLFormElement* HTMLElement::virtualForm() const
767 {
768     return HTMLFormElement::findClosestFormAncestor(*this);
769 }
770
771 static inline bool elementAffectsDirectionality(const Node& node)
772 {
773     if (!is<HTMLElement>(node))
774         return false;
775     const HTMLElement& element = downcast<HTMLElement>(node);
776     return is<HTMLBDIElement>(element) || element.hasAttributeWithoutSynchronization(dirAttr);
777 }
778
779 static void setHasDirAutoFlagRecursively(Node* firstNode, bool flag, Node* lastNode = nullptr)
780 {
781     firstNode->setSelfOrAncestorHasDirAutoAttribute(flag);
782
783     Node* node = firstNode->firstChild();
784
785     while (node) {
786         if (node->selfOrAncestorHasDirAutoAttribute() == flag)
787             return;
788
789         if (elementAffectsDirectionality(*node)) {
790             if (node == lastNode)
791                 return;
792             node = NodeTraversal::nextSkippingChildren(*node, firstNode);
793             continue;
794         }
795         node->setSelfOrAncestorHasDirAutoAttribute(flag);
796         if (node == lastNode)
797             return;
798         node = NodeTraversal::next(*node, firstNode);
799     }
800 }
801
802 void HTMLElement::childrenChanged(const ChildChange& change)
803 {
804     StyledElement::childrenChanged(change);
805     adjustDirectionalityIfNeededAfterChildrenChanged(change.previousSiblingElement, change.type);
806 }
807
808 bool HTMLElement::hasDirectionAuto() const
809 {
810     const AtomicString& direction = attributeWithoutSynchronization(dirAttr);
811     return (hasTagName(bdiTag) && direction.isNull()) || equalLettersIgnoringASCIICase(direction, "auto");
812 }
813
814 TextDirection HTMLElement::directionalityIfhasDirAutoAttribute(bool& isAuto) const
815 {
816     if (!(selfOrAncestorHasDirAutoAttribute() && hasDirectionAuto())) {
817         isAuto = false;
818         return LTR;
819     }
820
821     isAuto = true;
822     return directionality();
823 }
824
825 TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) const
826 {
827     if (is<HTMLTextFormControlElement>(*this)) {
828         HTMLTextFormControlElement& textElement = downcast<HTMLTextFormControlElement>(const_cast<HTMLElement&>(*this));
829         bool hasStrongDirectionality;
830         UCharDirection textDirection = textElement.value().defaultWritingDirection(&hasStrongDirectionality);
831         if (strongDirectionalityTextNode)
832             *strongDirectionalityTextNode = hasStrongDirectionality ? &textElement : nullptr;
833         return (textDirection == U_LEFT_TO_RIGHT) ? LTR : RTL;
834     }
835
836     Node* node = firstChild();
837     while (node) {
838         // Skip bdi, script, style and text form controls.
839         if (equalLettersIgnoringASCIICase(node->nodeName(), "bdi") || node->hasTagName(scriptTag) || node->hasTagName(styleTag)
840             || (is<Element>(*node) && downcast<Element>(*node).isTextFormControl())) {
841             node = NodeTraversal::nextSkippingChildren(*node, this);
842             continue;
843         }
844
845         // Skip elements with valid dir attribute
846         if (is<Element>(*node)) {
847             AtomicString dirAttributeValue = downcast<Element>(*node).attributeWithoutSynchronization(dirAttr);
848             if (isLTROrRTLIgnoringCase(dirAttributeValue) || equalLettersIgnoringASCIICase(dirAttributeValue, "auto")) {
849                 node = NodeTraversal::nextSkippingChildren(*node, this);
850                 continue;
851             }
852         }
853
854         if (node->isTextNode()) {
855             bool hasStrongDirectionality;
856             UCharDirection textDirection = node->textContent(true).defaultWritingDirection(&hasStrongDirectionality);
857             if (hasStrongDirectionality) {
858                 if (strongDirectionalityTextNode)
859                     *strongDirectionalityTextNode = node;
860                 return (textDirection == U_LEFT_TO_RIGHT) ? LTR : RTL;
861             }
862         }
863         node = NodeTraversal::next(*node, this);
864     }
865     if (strongDirectionalityTextNode)
866         *strongDirectionalityTextNode = nullptr;
867     return LTR;
868 }
869
870 void HTMLElement::dirAttributeChanged(const AtomicString& value)
871 {
872     Element* parent = parentElement();
873
874     if (is<HTMLElement>(parent) && parent->selfOrAncestorHasDirAutoAttribute())
875         downcast<HTMLElement>(*parent).adjustDirectionalityIfNeededAfterChildAttributeChanged(this);
876
877     if (equalLettersIgnoringASCIICase(value, "auto"))
878         calculateAndAdjustDirectionality();
879 }
880
881 void HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child)
882 {
883     ASSERT(selfOrAncestorHasDirAutoAttribute());
884     Node* strongDirectionalityTextNode;
885     TextDirection textDirection = directionality(&strongDirectionalityTextNode);
886     setHasDirAutoFlagRecursively(child, false);
887     if (!renderer() || renderer()->style().direction() == textDirection)
888         return;
889     for (auto& elementToAdjust : elementLineage(this)) {
890         if (elementAffectsDirectionality(elementToAdjust)) {
891             elementToAdjust.setNeedsStyleRecalc();
892             return;
893         }
894     }
895 }
896
897 void HTMLElement::calculateAndAdjustDirectionality()
898 {
899     Node* strongDirectionalityTextNode;
900     TextDirection textDirection = directionality(&strongDirectionalityTextNode);
901     setHasDirAutoFlagRecursively(this, true, strongDirectionalityTextNode);
902     if (renderer() && renderer()->style().direction() != textDirection)
903         setNeedsStyleRecalc();
904 }
905
906 void HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged(Element* beforeChange, ChildChangeType changeType)
907 {
908     // FIXME: This function looks suspicious.
909
910     if (!selfOrAncestorHasDirAutoAttribute())
911         return;
912
913     Node* oldMarkedNode = nullptr;
914     if (beforeChange)
915         oldMarkedNode = changeType == ElementInserted ? ElementTraversal::nextSibling(*beforeChange) : beforeChange->nextSibling();
916
917     while (oldMarkedNode && elementAffectsDirectionality(*oldMarkedNode))
918         oldMarkedNode = oldMarkedNode->nextSibling();
919     if (oldMarkedNode)
920         setHasDirAutoFlagRecursively(oldMarkedNode, false);
921
922     for (auto& elementToAdjust : lineageOfType<HTMLElement>(*this)) {
923         if (elementAffectsDirectionality(elementToAdjust)) {
924             elementToAdjust.calculateAndAdjustDirectionality();
925             return;
926         }
927     }
928 }
929
930 void HTMLElement::addHTMLLengthToStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& value)
931 {
932     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
933     // length unit and make the appropriate parsed value.
934
935     if (StringImpl* string = value.impl()) {
936         unsigned parsedLength = 0;
937
938         while (parsedLength < string->length() && (*string)[parsedLength] <= ' ')
939             ++parsedLength;
940
941         for (; parsedLength < string->length(); ++parsedLength) {
942             UChar cc = (*string)[parsedLength];
943             if (cc > '9')
944                 break;
945             if (cc < '0') {
946                 if (cc == '%' || cc == '*')
947                     ++parsedLength;
948                 if (cc != '.')
949                     break;
950             }
951         }
952
953         if (parsedLength != string->length()) {
954             addPropertyToPresentationAttributeStyle(style, propertyID, string->substring(0, parsedLength));
955             return;
956         }
957     }
958
959     addPropertyToPresentationAttributeStyle(style, propertyID, value);
960 }
961
962 static RGBA32 parseColorStringWithCrazyLegacyRules(const String& colorString)
963 {
964     // Per spec, only look at the first 128 digits of the string.
965     const size_t maxColorLength = 128;
966     // We'll pad the buffer with two extra 0s later, so reserve two more than the max.
967     Vector<char, maxColorLength+2> digitBuffer;
968
969     size_t i = 0;
970     // Skip a leading #.
971     if (colorString[0] == '#')
972         i = 1;
973
974     // Grab the first 128 characters, replacing non-hex characters with 0.
975     // Non-BMP characters are replaced with "00" due to them appearing as two "characters" in the String.
976     for (; i < colorString.length() && digitBuffer.size() < maxColorLength; i++) {
977         if (!isASCIIHexDigit(colorString[i]))
978             digitBuffer.append('0');
979         else
980             digitBuffer.append(colorString[i]);
981     }
982
983     if (!digitBuffer.size())
984         return Color::black;
985
986     // Pad the buffer out to at least the next multiple of three in size.
987     digitBuffer.append('0');
988     digitBuffer.append('0');
989
990     if (digitBuffer.size() < 6)
991         return makeRGB(toASCIIHexValue(digitBuffer[0]), toASCIIHexValue(digitBuffer[1]), toASCIIHexValue(digitBuffer[2]));
992
993     // Split the digits into three components, then search the last 8 digits of each component.
994     ASSERT(digitBuffer.size() >= 6);
995     size_t componentLength = digitBuffer.size() / 3;
996     size_t componentSearchWindowLength = std::min<size_t>(componentLength, 8);
997     size_t redIndex = componentLength - componentSearchWindowLength;
998     size_t greenIndex = componentLength * 2 - componentSearchWindowLength;
999     size_t blueIndex = componentLength * 3 - componentSearchWindowLength;
1000     // Skip digits until one of them is non-zero, or we've only got two digits left in the component.
1001     while (digitBuffer[redIndex] == '0' && digitBuffer[greenIndex] == '0' && digitBuffer[blueIndex] == '0' && (componentLength - redIndex) > 2) {
1002         redIndex++;
1003         greenIndex++;
1004         blueIndex++;
1005     }
1006     ASSERT(redIndex + 1 < componentLength);
1007     ASSERT(greenIndex >= componentLength);
1008     ASSERT(greenIndex + 1 < componentLength * 2);
1009     ASSERT(blueIndex >= componentLength * 2);
1010     ASSERT_WITH_SECURITY_IMPLICATION(blueIndex + 1 < digitBuffer.size());
1011
1012     int redValue = toASCIIHexValue(digitBuffer[redIndex], digitBuffer[redIndex + 1]);
1013     int greenValue = toASCIIHexValue(digitBuffer[greenIndex], digitBuffer[greenIndex + 1]);
1014     int blueValue = toASCIIHexValue(digitBuffer[blueIndex], digitBuffer[blueIndex + 1]);
1015     return makeRGB(redValue, greenValue, blueValue);
1016 }
1017
1018 // Color parsing that matches HTML's "rules for parsing a legacy color value"
1019 void HTMLElement::addHTMLColorToStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& attributeValue)
1020 {
1021     // An empty string doesn't apply a color. (One containing only whitespace does, which is why this check occurs before stripping.)
1022     if (attributeValue.isEmpty())
1023         return;
1024
1025     String colorString = attributeValue.stripWhiteSpace();
1026
1027     // "transparent" doesn't apply a color either.
1028     if (equalLettersIgnoringASCIICase(colorString, "transparent"))
1029         return;
1030
1031     // If the string is a named CSS color or a 3/6-digit hex color, use that.
1032     // We can't use the default Color constructor because it accepts
1033     // 4/8-digit hex, which conflict with some legacy HTML content using attributes.
1034
1035     Color color;
1036
1037     if ((colorString.length() == 4 || colorString.length() == 7) && colorString[0] == '#')
1038         color = Color(colorString);
1039     if (!color.isValid())
1040         color.setNamedColor(colorString);
1041     if (!color.isValid())
1042         color.setRGB(parseColorStringWithCrazyLegacyRules(colorString));
1043
1044     style.setProperty(propertyID, CSSValuePool::singleton().createColorValue(color.rgb()));
1045 }
1046
1047 bool HTMLElement::willRespondToMouseMoveEvents()
1048 {
1049     return !isDisabledFormControl() && Element::willRespondToMouseMoveEvents();
1050 }
1051
1052 bool HTMLElement::willRespondToMouseWheelEvents()
1053 {
1054     return !isDisabledFormControl() && Element::willRespondToMouseWheelEvents();
1055 }
1056
1057 bool HTMLElement::willRespondToMouseClickEvents()
1058 {
1059     return !isDisabledFormControl() && Element::willRespondToMouseClickEvents();
1060 }
1061
1062 } // namespace WebCore
1063
1064 #ifndef NDEBUG
1065
1066 // For use in the debugger
1067 void dumpInnerHTML(WebCore::HTMLElement*);
1068
1069 void dumpInnerHTML(WebCore::HTMLElement* element)
1070 {
1071     printf("%s\n", element->innerHTML().ascii().data());
1072 }
1073
1074 #endif