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