27954960d52f444d4fee68acee8424a9a7289534
[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-2017 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 "CSSMarkup.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 "Frame.h"
39 #include "FrameLoader.h"
40 #include "FrameView.h"
41 #include "HTMLBDIElement.h"
42 #include "HTMLBRElement.h"
43 #include "HTMLButtonElement.h"
44 #include "HTMLCollection.h"
45 #include "HTMLDocument.h"
46 #include "HTMLElementFactory.h"
47 #include "HTMLFieldSetElement.h"
48 #include "HTMLFormElement.h"
49 #include "HTMLInputElement.h"
50 #include "HTMLNames.h"
51 #include "HTMLOptGroupElement.h"
52 #include "HTMLOptionElement.h"
53 #include "HTMLParserIdioms.h"
54 #include "HTMLSelectElement.h"
55 #include "HTMLTextAreaElement.h"
56 #include "HTMLTextFormControlElement.h"
57 #include "NodeTraversal.h"
58 #include "RenderElement.h"
59 #include "ScriptController.h"
60 #include "ShadowRoot.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 CSSValuePlaintext;
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 CSSValueIsolate;
100 }
101
102 unsigned HTMLElement::parseBorderWidthAttribute(const AtomicString& value) const
103 {
104     if (auto optionalBorderWidth = parseHTMLNonNegativeInteger(value))
105         return optionalBorderWidth.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, serializeString(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, CSSPropertyLineBreak, 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.get(),
226         &onanimationendAttr.get(),
227         &onanimationiterationAttr.get(),
228         &onanimationstartAttr.get(),
229         &onautocompleteAttr.get(),
230         &onautocompleteerrorAttr.get(),
231         &onbeforecopyAttr.get(),
232         &onbeforecutAttr.get(),
233         &onbeforeinputAttr.get(),
234         &onbeforeloadAttr.get(),
235         &onbeforepasteAttr.get(),
236         &onblurAttr.get(),
237         &oncanplayAttr.get(),
238         &oncanplaythroughAttr.get(),
239         &onchangeAttr.get(),
240         &onclickAttr.get(),
241         &oncontextmenuAttr.get(),
242         &oncopyAttr.get(),
243         &oncutAttr.get(),
244         &ondblclickAttr.get(),
245         &ondragAttr.get(),
246         &ondragendAttr.get(),
247         &ondragenterAttr.get(),
248         &ondragleaveAttr.get(),
249         &ondragoverAttr.get(),
250         &ondragstartAttr.get(),
251         &ondropAttr.get(),
252         &ondurationchangeAttr.get(),
253         &onemptiedAttr.get(),
254         &onendedAttr.get(),
255         &onerrorAttr.get(),
256         &onfocusAttr.get(),
257         &onfocusinAttr.get(),
258         &onfocusoutAttr.get(),
259         &ongesturechangeAttr.get(),
260         &ongestureendAttr.get(),
261         &ongesturestartAttr.get(),
262         &oninputAttr.get(),
263         &oninvalidAttr.get(),
264         &onkeydownAttr.get(),
265         &onkeypressAttr.get(),
266         &onkeyupAttr.get(),
267         &onloadAttr.get(),
268         &onloadeddataAttr.get(),
269         &onloadedmetadataAttr.get(),
270         &onloadstartAttr.get(),
271         &onmousedownAttr.get(),
272         &onmouseenterAttr.get(),
273         &onmouseleaveAttr.get(),
274         &onmousemoveAttr.get(),
275         &onmouseoutAttr.get(),
276         &onmouseoverAttr.get(),
277         &onmouseupAttr.get(),
278         &onmousewheelAttr.get(),
279         &onpasteAttr.get(),
280         &onpauseAttr.get(),
281         &onplayAttr.get(),
282         &onplayingAttr.get(),
283         &onprogressAttr.get(),
284         &onratechangeAttr.get(),
285         &onresetAttr.get(),
286         &onresizeAttr.get(),
287         &onscrollAttr.get(),
288         &onsearchAttr.get(),
289         &onseekedAttr.get(),
290         &onseekingAttr.get(),
291         &onselectAttr.get(),
292         &onselectstartAttr.get(),
293         &onstalledAttr.get(),
294         &onsubmitAttr.get(),
295         &onsuspendAttr.get(),
296         &ontimeupdateAttr.get(),
297         &ontoggleAttr.get(),
298         &ontouchcancelAttr.get(),
299         &ontouchendAttr.get(),
300         &ontouchforcechangeAttr.get(),
301         &ontouchmoveAttr.get(),
302         &ontouchstartAttr.get(),
303         &ontransitionendAttr.get(),
304         &onvolumechangeAttr.get(),
305         &onwaitingAttr.get(),
306         &onwebkitbeginfullscreenAttr.get(),
307         &onwebkitcurrentplaybacktargetiswirelesschangedAttr.get(),
308         &onwebkitendfullscreenAttr.get(),
309         &onwebkitfullscreenchangeAttr.get(),
310         &onwebkitfullscreenerrorAttr.get(),
311         &onwebkitkeyaddedAttr.get(),
312         &onwebkitkeyerrorAttr.get(),
313         &onwebkitkeymessageAttr.get(),
314         &onwebkitmouseforcechangedAttr.get(),
315         &onwebkitmouseforcedownAttr.get(),
316         &onwebkitmouseforcewillbeginAttr.get(),
317         &onwebkitmouseforceupAttr.get(),
318         &onwebkitneedkeyAttr.get(),
319         &onwebkitplaybacktargetavailabilitychangedAttr.get(),
320         &onwebkitpresentationmodechangedAttr.get(),
321         &onwebkitwillrevealbottomAttr.get(),
322         &onwebkitwillrevealleftAttr.get(),
323         &onwebkitwillrevealrightAttr.get(),
324         &onwebkitwillrevealtopAttr.get(),
325         &onwheelAttr.get(),
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* containingShadowRoot = node.containingShadowRoot();
406     if (containingShadowRoot && containingShadowRoot->mode() == ShadowRootMode::UserAgent)
407         return Editability::ReadOnly;
408
409     auto& document = node.document();
410     if (is<HTMLDocument>(document))
411         return downcast<HTMLDocument>(document).inDesignMode() ? Editability::CanEditRichly : Editability::ReadOnly;
412
413     return Editability::ReadOnly;
414 }
415
416 bool HTMLElement::matchesReadWritePseudoClass() const
417 {
418     return editabilityFromContentEditableAttr(*this) != Editability::ReadOnly;
419 }
420
421 void HTMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
422 {
423     if (name == dirAttr) {
424         dirAttributeChanged(value);
425         return;
426     }
427
428     if (name == tabindexAttr) {
429         if (value.isEmpty())
430             clearTabIndexExplicitlyIfNeeded();
431         else if (auto optionalTabIndex = parseHTMLInteger(value))
432             setTabIndexExplicitly(optionalTabIndex.value());
433         return;
434     }
435
436     auto& eventName = eventNameForEventHandlerAttribute(name);
437     if (!eventName.isNull())
438         setAttributeEventListener(eventName, name, value);
439 }
440
441 static Ref<DocumentFragment> textToFragment(Document& document, const String& text)
442 {
443     auto fragment = DocumentFragment::create(document);
444
445     // It's safe to dispatch events on the new fragment since author scripts have no access to it yet.
446     NoEventDispatchAssertion::EventAllowedScope allowedScope(fragment);
447
448     for (unsigned start = 0, length = text.length(); start < length; ) {
449         // Find next line break.
450         UChar c = 0;
451         unsigned i;
452         for (i = start; i < length; i++) {
453             c = text[i];
454             if (c == '\r' || c == '\n')
455                 break;
456         }
457
458         // If text is not the empty string, then append a new Text node whose data is text and node document is document to fragment.
459         if (i > start)
460             fragment->appendChild(Text::create(document, text.substring(start, i - start)));
461
462         if (i == length)
463             break;
464
465         fragment->appendChild(HTMLBRElement::create(document));
466         // Make sure \r\n doesn't result in two line breaks.
467         if (c == '\r' && i + 1 < length && text[i + 1] == '\n')
468             ++i;
469
470         start = i + 1; // Character after line break.
471     }
472
473     return fragment;
474 }
475
476 // Returns the conforming 'dir' value associated with the state the attribute is in (in its canonical case), if any,
477 // or the empty string if the attribute is in a state that has no associated keyword value or if the attribute is
478 // not in a defined state (e.g. the attribute is missing and there is no missing value default).
479 // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#limited-to-only-known-values
480 static inline const AtomicString& toValidDirValue(const AtomicString& value)
481 {
482     static NeverDestroyed<AtomicString> ltrValue("ltr", AtomicString::ConstructFromLiteral);
483     static NeverDestroyed<AtomicString> rtlValue("rtl", AtomicString::ConstructFromLiteral);
484     static NeverDestroyed<AtomicString> autoValue("auto", AtomicString::ConstructFromLiteral);
485     if (equalLettersIgnoringASCIICase(value, "ltr"))
486         return ltrValue;
487     if (equalLettersIgnoringASCIICase(value, "rtl"))
488         return rtlValue;
489     if (equalLettersIgnoringASCIICase(value, "auto"))
490         return autoValue;
491     return nullAtom();
492 }
493
494 const AtomicString& HTMLElement::dir() const
495 {
496     return toValidDirValue(attributeWithoutSynchronization(dirAttr));
497 }
498
499 void HTMLElement::setDir(const AtomicString& value)
500 {
501     setAttributeWithoutSynchronization(dirAttr, value);
502 }
503
504 ExceptionOr<void> HTMLElement::setInnerText(const String& text)
505 {
506     // FIXME: This doesn't take whitespace collapsing into account at all.
507
508     if (!text.contains('\n') && !text.contains('\r')) {
509         if (text.isEmpty())
510             replaceAllChildren(nullptr);
511         else
512             replaceAllChildren(document().createTextNode(text));
513         return { };
514     }
515
516     // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer?
517     // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded?
518     // For example, for the contents of textarea elements that are display:none?
519     auto* r = renderer();
520     if ((r && r->style().preserveNewline()) || (isConnected() && isTextControlInnerTextElement())) {
521         if (!text.contains('\r')) {
522             replaceAllChildren(document().createTextNode(text));
523             return { };
524         }
525         String textWithConsistentLineBreaks = text;
526         textWithConsistentLineBreaks.replace("\r\n", "\n");
527         textWithConsistentLineBreaks.replace('\r', '\n');
528         replaceAllChildren(document().createTextNode(textWithConsistentLineBreaks));
529         return { };
530     }
531
532     // Add text nodes and <br> elements.
533     auto fragment = textToFragment(document(), text);
534     // FIXME: This should use replaceAllChildren() once it accepts DocumentFragments as input.
535     // It's safe to dispatch events on the new fragment since author scripts have no access to it yet.
536     NoEventDispatchAssertion::EventAllowedScope allowedScope(fragment.get());
537     return replaceChildrenWithFragment(*this, WTFMove(fragment));
538 }
539
540 ExceptionOr<void> HTMLElement::setOuterText(const String& text)
541 {
542     RefPtr<ContainerNode> parent = parentNode();
543     if (!parent)
544         return Exception { NoModificationAllowedError };
545
546     RefPtr<Node> prev = previousSibling();
547     RefPtr<Node> next = nextSibling();
548     RefPtr<Node> newChild;
549
550     // Convert text to fragment with <br> tags instead of linebreaks if needed.
551     if (text.contains('\r') || text.contains('\n'))
552         newChild = textToFragment(document(), text);
553     else
554         newChild = Text::create(document(), text);
555
556     if (!parentNode())
557         return Exception { HierarchyRequestError };
558
559     auto replaceResult = parent->replaceChild(*newChild, *this);
560     if (replaceResult.hasException())
561         return replaceResult.releaseException();
562
563     RefPtr<Node> node = next ? next->previousSibling() : nullptr;
564     if (is<Text>(node)) {
565         auto result = mergeWithNextTextNode(downcast<Text>(*node));
566         if (result.hasException())
567             return result.releaseException();
568     }
569     if (is<Text>(prev)) {
570         auto result = mergeWithNextTextNode(downcast<Text>(*prev));
571         if (result.hasException())
572             return result.releaseException();
573     }
574     return { };
575 }
576
577 void HTMLElement::applyAlignmentAttributeToStyle(const AtomicString& alignment, MutableStyleProperties& style)
578 {
579     // Vertical alignment with respect to the current baseline of the text
580     // right or left means floating images.
581     CSSValueID floatValue = CSSValueInvalid;
582     CSSValueID verticalAlignValue = CSSValueInvalid;
583
584     if (equalLettersIgnoringASCIICase(alignment, "absmiddle"))
585         verticalAlignValue = CSSValueMiddle;
586     else if (equalLettersIgnoringASCIICase(alignment, "absbottom"))
587         verticalAlignValue = CSSValueBottom;
588     else if (equalLettersIgnoringASCIICase(alignment, "left")) {
589         floatValue = CSSValueLeft;
590         verticalAlignValue = CSSValueTop;
591     } else if (equalLettersIgnoringASCIICase(alignment, "right")) {
592         floatValue = CSSValueRight;
593         verticalAlignValue = CSSValueTop;
594     } else if (equalLettersIgnoringASCIICase(alignment, "top"))
595         verticalAlignValue = CSSValueTop;
596     else if (equalLettersIgnoringASCIICase(alignment, "middle"))
597         verticalAlignValue = CSSValueWebkitBaselineMiddle;
598     else if (equalLettersIgnoringASCIICase(alignment, "center"))
599         verticalAlignValue = CSSValueMiddle;
600     else if (equalLettersIgnoringASCIICase(alignment, "bottom"))
601         verticalAlignValue = CSSValueBaseline;
602     else if (equalLettersIgnoringASCIICase(alignment, "texttop"))
603         verticalAlignValue = CSSValueTextTop;
604
605     if (floatValue != CSSValueInvalid)
606         addPropertyToPresentationAttributeStyle(style, CSSPropertyFloat, floatValue);
607
608     if (verticalAlignValue != CSSValueInvalid)
609         addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, verticalAlignValue);
610 }
611
612 bool HTMLElement::hasCustomFocusLogic() const
613 {
614     return false;
615 }
616
617 bool HTMLElement::supportsFocus() const
618 {
619     return Element::supportsFocus() || (hasEditableStyle() && parentNode() && !parentNode()->hasEditableStyle());
620 }
621
622 String HTMLElement::contentEditable() const
623 {
624     switch (contentEditableType(*this)) {
625     case ContentEditableType::Inherit:
626         return ASCIILiteral("inherit");
627     case ContentEditableType::True:
628         return ASCIILiteral("true");
629     case ContentEditableType::False:
630         return ASCIILiteral("false");
631     case ContentEditableType::PlaintextOnly:
632         return ASCIILiteral("plaintext-only");
633     }
634     return ASCIILiteral("inherit");
635 }
636
637 ExceptionOr<void> HTMLElement::setContentEditable(const String& enabled)
638 {
639     if (equalLettersIgnoringASCIICase(enabled, "true"))
640         setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("true", AtomicString::ConstructFromLiteral));
641     else if (equalLettersIgnoringASCIICase(enabled, "false"))
642         setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("false", AtomicString::ConstructFromLiteral));
643     else if (equalLettersIgnoringASCIICase(enabled, "plaintext-only"))
644         setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("plaintext-only", AtomicString::ConstructFromLiteral));
645     else if (equalLettersIgnoringASCIICase(enabled, "inherit"))
646         removeAttribute(contenteditableAttr);
647     else
648         return Exception { SyntaxError };
649     return { };
650 }
651
652 bool HTMLElement::draggable() const
653 {
654     return equalLettersIgnoringASCIICase(attributeWithoutSynchronization(draggableAttr), "true");
655 }
656
657 void HTMLElement::setDraggable(bool value)
658 {
659     setAttributeWithoutSynchronization(draggableAttr, value
660         ? AtomicString("true", AtomicString::ConstructFromLiteral)
661         : AtomicString("false", AtomicString::ConstructFromLiteral));
662 }
663
664 bool HTMLElement::spellcheck() const
665 {
666     return isSpellCheckingEnabled();
667 }
668
669 void HTMLElement::setSpellcheck(bool enable)
670 {
671     setAttributeWithoutSynchronization(spellcheckAttr, enable
672         ? AtomicString("true", AtomicString::ConstructFromLiteral)
673         : AtomicString("false", AtomicString::ConstructFromLiteral));
674 }
675
676 void HTMLElement::click()
677 {
678     simulateClick(*this, nullptr, SendNoEvents, DoNotShowPressedLook, SimulatedClickSource::Bindings);
679 }
680
681 void HTMLElement::accessKeyAction(bool sendMouseEvents)
682 {
683     dispatchSimulatedClick(nullptr, sendMouseEvents ? SendMouseUpDownEvents : SendNoEvents);
684 }
685
686 String HTMLElement::title() const
687 {
688     return attributeWithoutSynchronization(titleAttr);
689 }
690
691 int HTMLElement::tabIndex() const
692 {
693     if (supportsFocus())
694         return Element::tabIndex();
695     return -1;
696 }
697
698 bool HTMLElement::translate() const
699 {
700     for (auto& element : lineageOfType<HTMLElement>(*this)) {
701         const AtomicString& value = element.attributeWithoutSynchronization(translateAttr);
702         if (equalLettersIgnoringASCIICase(value, "yes") || (value.isEmpty() && !value.isNull()))
703             return true;
704         if (equalLettersIgnoringASCIICase(value, "no"))
705             return false;
706     }
707     // Default on the root element is translate=yes.
708     return true;
709 }
710
711 void HTMLElement::setTranslate(bool enable)
712 {
713     setAttributeWithoutSynchronization(translateAttr, enable ? "yes" : "no");
714 }
715
716 bool HTMLElement::rendererIsNeeded(const RenderStyle& style)
717 {
718     if (hasTagName(noscriptTag)) {
719         RefPtr<Frame> frame = document().frame();
720         if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript))
721             return false;
722     } else if (hasTagName(noembedTag)) {
723         RefPtr<Frame> frame = document().frame();
724         if (frame && frame->loader().subframeLoader().allowPlugins())
725             return false;
726     }
727     return StyledElement::rendererIsNeeded(style);
728 }
729
730 RenderPtr<RenderElement> HTMLElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
731 {
732     return RenderElement::createFor(*this, WTFMove(style));
733 }
734
735 HTMLFormElement* HTMLElement::form() const
736 {
737     return HTMLFormElement::findClosestFormAncestor(*this);
738 }
739
740 static inline bool elementAffectsDirectionality(const Node& node)
741 {
742     if (!is<HTMLElement>(node))
743         return false;
744     const HTMLElement& element = downcast<HTMLElement>(node);
745     return is<HTMLBDIElement>(element) || element.hasAttributeWithoutSynchronization(dirAttr);
746 }
747
748 static void setHasDirAutoFlagRecursively(Node* firstNode, bool flag, Node* lastNode = nullptr)
749 {
750     firstNode->setSelfOrAncestorHasDirAutoAttribute(flag);
751
752     RefPtr<Node> node = firstNode->firstChild();
753
754     while (node) {
755         if (node->selfOrAncestorHasDirAutoAttribute() == flag)
756             return;
757
758         if (elementAffectsDirectionality(*node)) {
759             if (node == lastNode)
760                 return;
761             node = NodeTraversal::nextSkippingChildren(*node, firstNode);
762             continue;
763         }
764         node->setSelfOrAncestorHasDirAutoAttribute(flag);
765         if (node == lastNode)
766             return;
767         node = NodeTraversal::next(*node, firstNode);
768     }
769 }
770
771 void HTMLElement::childrenChanged(const ChildChange& change)
772 {
773     StyledElement::childrenChanged(change);
774     adjustDirectionalityIfNeededAfterChildrenChanged(change.previousSiblingElement, change.type);
775 }
776
777 bool HTMLElement::hasDirectionAuto() const
778 {
779     const AtomicString& direction = attributeWithoutSynchronization(dirAttr);
780     return (hasTagName(bdiTag) && direction.isNull()) || equalLettersIgnoringASCIICase(direction, "auto");
781 }
782
783 TextDirection HTMLElement::directionalityIfhasDirAutoAttribute(bool& isAuto) const
784 {
785     if (!(selfOrAncestorHasDirAutoAttribute() && hasDirectionAuto())) {
786         isAuto = false;
787         return LTR;
788     }
789
790     isAuto = true;
791     return directionality();
792 }
793
794 TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) const
795 {
796     if (is<HTMLTextFormControlElement>(*this)) {
797         HTMLTextFormControlElement& textElement = downcast<HTMLTextFormControlElement>(const_cast<HTMLElement&>(*this));
798         bool hasStrongDirectionality;
799         UCharDirection textDirection = textElement.value().defaultWritingDirection(&hasStrongDirectionality);
800         if (strongDirectionalityTextNode)
801             *strongDirectionalityTextNode = hasStrongDirectionality ? &textElement : nullptr;
802         return (textDirection == U_LEFT_TO_RIGHT) ? LTR : RTL;
803     }
804
805     RefPtr<Node> node = firstChild();
806     while (node) {
807         // Skip bdi, script, style and text form controls.
808         if (equalLettersIgnoringASCIICase(node->nodeName(), "bdi") || node->hasTagName(scriptTag) || node->hasTagName(styleTag)
809             || (is<Element>(*node) && downcast<Element>(*node).isTextFormControl())) {
810             node = NodeTraversal::nextSkippingChildren(*node, this);
811             continue;
812         }
813
814         // Skip elements with valid dir attribute
815         if (is<Element>(*node)) {
816             auto& dirAttributeValue = downcast<Element>(*node).attributeWithoutSynchronization(dirAttr);
817             if (isLTROrRTLIgnoringCase(dirAttributeValue) || equalLettersIgnoringASCIICase(dirAttributeValue, "auto")) {
818                 node = NodeTraversal::nextSkippingChildren(*node, this);
819                 continue;
820             }
821         }
822
823         if (node->isTextNode()) {
824             bool hasStrongDirectionality;
825             UCharDirection textDirection = node->textContent(true).defaultWritingDirection(&hasStrongDirectionality);
826             if (hasStrongDirectionality) {
827                 if (strongDirectionalityTextNode)
828                     *strongDirectionalityTextNode = node.get();
829                 return (textDirection == U_LEFT_TO_RIGHT) ? LTR : RTL;
830             }
831         }
832         node = NodeTraversal::next(*node, this);
833     }
834     if (strongDirectionalityTextNode)
835         *strongDirectionalityTextNode = nullptr;
836     return LTR;
837 }
838
839 void HTMLElement::dirAttributeChanged(const AtomicString& value)
840 {
841     RefPtr<Element> parent = parentElement();
842
843     if (is<HTMLElement>(parent) && parent->selfOrAncestorHasDirAutoAttribute())
844         downcast<HTMLElement>(*parent).adjustDirectionalityIfNeededAfterChildAttributeChanged(this);
845
846     if (equalLettersIgnoringASCIICase(value, "auto"))
847         calculateAndAdjustDirectionality();
848 }
849
850 void HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child)
851 {
852     ASSERT(selfOrAncestorHasDirAutoAttribute());
853     Node* strongDirectionalityTextNode;
854     TextDirection textDirection = directionality(&strongDirectionalityTextNode);
855     setHasDirAutoFlagRecursively(child, false);
856     if (!renderer() || renderer()->style().direction() == textDirection)
857         return;
858     for (auto& elementToAdjust : elementLineage(this)) {
859         if (elementAffectsDirectionality(elementToAdjust)) {
860             elementToAdjust.invalidateStyleForSubtree();
861             return;
862         }
863     }
864 }
865
866 void HTMLElement::calculateAndAdjustDirectionality()
867 {
868     Node* strongDirectionalityTextNode;
869     TextDirection textDirection = directionality(&strongDirectionalityTextNode);
870     setHasDirAutoFlagRecursively(this, true, strongDirectionalityTextNode);
871     if (renderer() && renderer()->style().direction() != textDirection)
872         invalidateStyleForSubtree();
873 }
874
875 void HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged(Element* beforeChange, ChildChangeType changeType)
876 {
877     // FIXME: This function looks suspicious.
878
879     if (!selfOrAncestorHasDirAutoAttribute())
880         return;
881
882     RefPtr<Node> oldMarkedNode;
883     if (beforeChange)
884         oldMarkedNode = changeType == ElementInserted ? ElementTraversal::nextSibling(*beforeChange) : beforeChange->nextSibling();
885
886     while (oldMarkedNode && elementAffectsDirectionality(*oldMarkedNode))
887         oldMarkedNode = oldMarkedNode->nextSibling();
888     if (oldMarkedNode)
889         setHasDirAutoFlagRecursively(oldMarkedNode.get(), false);
890
891     for (auto& elementToAdjust : lineageOfType<HTMLElement>(*this)) {
892         if (elementAffectsDirectionality(elementToAdjust)) {
893             elementToAdjust.calculateAndAdjustDirectionality();
894             return;
895         }
896     }
897 }
898
899 void HTMLElement::addHTMLLengthToStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& value)
900 {
901     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
902     // length unit and make the appropriate parsed value.
903
904     if (StringImpl* string = value.impl()) {
905         unsigned parsedLength = 0;
906
907         while (parsedLength < string->length() && (*string)[parsedLength] <= ' ')
908             ++parsedLength;
909
910         for (; parsedLength < string->length(); ++parsedLength) {
911             UChar cc = (*string)[parsedLength];
912             if (cc > '9')
913                 break;
914             if (cc < '0') {
915                 if (cc == '%' || cc == '*')
916                     ++parsedLength;
917                 if (cc != '.')
918                     break;
919             }
920         }
921
922         if (parsedLength != string->length()) {
923             addPropertyToPresentationAttributeStyle(style, propertyID, string->substring(0, parsedLength));
924             return;
925         }
926     }
927
928     addPropertyToPresentationAttributeStyle(style, propertyID, value);
929 }
930
931 static RGBA32 parseColorStringWithCrazyLegacyRules(const String& colorString)
932 {
933     // Per spec, only look at the first 128 digits of the string.
934     const size_t maxColorLength = 128;
935     // We'll pad the buffer with two extra 0s later, so reserve two more than the max.
936     Vector<char, maxColorLength+2> digitBuffer;
937
938     size_t i = 0;
939     // Skip a leading #.
940     if (colorString[0] == '#')
941         i = 1;
942
943     // Grab the first 128 characters, replacing non-hex characters with 0.
944     // Non-BMP characters are replaced with "00" due to them appearing as two "characters" in the String.
945     for (; i < colorString.length() && digitBuffer.size() < maxColorLength; i++) {
946         if (!isASCIIHexDigit(colorString[i]))
947             digitBuffer.append('0');
948         else
949             digitBuffer.append(colorString[i]);
950     }
951
952     if (!digitBuffer.size())
953         return Color::black;
954
955     // Pad the buffer out to at least the next multiple of three in size.
956     digitBuffer.append('0');
957     digitBuffer.append('0');
958
959     if (digitBuffer.size() < 6)
960         return makeRGB(toASCIIHexValue(digitBuffer[0]), toASCIIHexValue(digitBuffer[1]), toASCIIHexValue(digitBuffer[2]));
961
962     // Split the digits into three components, then search the last 8 digits of each component.
963     ASSERT(digitBuffer.size() >= 6);
964     size_t componentLength = digitBuffer.size() / 3;
965     size_t componentSearchWindowLength = std::min<size_t>(componentLength, 8);
966     size_t redIndex = componentLength - componentSearchWindowLength;
967     size_t greenIndex = componentLength * 2 - componentSearchWindowLength;
968     size_t blueIndex = componentLength * 3 - componentSearchWindowLength;
969     // Skip digits until one of them is non-zero, or we've only got two digits left in the component.
970     while (digitBuffer[redIndex] == '0' && digitBuffer[greenIndex] == '0' && digitBuffer[blueIndex] == '0' && (componentLength - redIndex) > 2) {
971         redIndex++;
972         greenIndex++;
973         blueIndex++;
974     }
975     ASSERT(redIndex + 1 < componentLength);
976     ASSERT(greenIndex >= componentLength);
977     ASSERT(greenIndex + 1 < componentLength * 2);
978     ASSERT(blueIndex >= componentLength * 2);
979     ASSERT_WITH_SECURITY_IMPLICATION(blueIndex + 1 < digitBuffer.size());
980
981     int redValue = toASCIIHexValue(digitBuffer[redIndex], digitBuffer[redIndex + 1]);
982     int greenValue = toASCIIHexValue(digitBuffer[greenIndex], digitBuffer[greenIndex + 1]);
983     int blueValue = toASCIIHexValue(digitBuffer[blueIndex], digitBuffer[blueIndex + 1]);
984     return makeRGB(redValue, greenValue, blueValue);
985 }
986
987 // Color parsing that matches HTML's "rules for parsing a legacy color value"
988 void HTMLElement::addHTMLColorToStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& attributeValue)
989 {
990     // An empty string doesn't apply a color. (One containing only whitespace does, which is why this check occurs before stripping.)
991     if (attributeValue.isEmpty())
992         return;
993
994     String colorString = attributeValue.stripWhiteSpace();
995
996     // "transparent" doesn't apply a color either.
997     if (equalLettersIgnoringASCIICase(colorString, "transparent"))
998         return;
999
1000     Color color;
1001     // We can't always use the default Color constructor because it accepts
1002     // 4/8-digit hex, which conflict with some legacy HTML content using attributes.
1003     if ((colorString.length() != 5 && colorString.length() != 9) || colorString[0] != '#')
1004         color = Color(colorString);
1005     if (!color.isValid())
1006         color = Color(parseColorStringWithCrazyLegacyRules(colorString));
1007
1008     style.setProperty(propertyID, CSSValuePool::singleton().createColorValue(color.rgb()));
1009 }
1010
1011 bool HTMLElement::willRespondToMouseMoveEvents()
1012 {
1013     return !isDisabledFormControl() && Element::willRespondToMouseMoveEvents();
1014 }
1015
1016 bool HTMLElement::willRespondToMouseWheelEvents()
1017 {
1018     return !isDisabledFormControl() && Element::willRespondToMouseWheelEvents();
1019 }
1020
1021 bool HTMLElement::willRespondToMouseClickEvents()
1022 {
1023     return !isDisabledFormControl() && Element::willRespondToMouseClickEvents();
1024 }
1025
1026 bool HTMLElement::canBeActuallyDisabled() const
1027 {
1028     return is<HTMLButtonElement>(*this)
1029         || is<HTMLInputElement>(*this)
1030         || is<HTMLSelectElement>(*this)
1031         || is<HTMLTextAreaElement>(*this)
1032         || is<HTMLOptGroupElement>(*this)
1033         || is<HTMLOptionElement>(*this)
1034         || is<HTMLFieldSetElement>(*this);
1035 }
1036
1037 bool HTMLElement::isActuallyDisabled() const
1038 {
1039     return canBeActuallyDisabled() && isDisabledFormControl();
1040 }
1041
1042 #if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE)
1043
1044 const AtomicString& HTMLElement::autocapitalize() const
1045 {
1046     return stringForAutocapitalizeType(autocapitalizeType());
1047 }
1048
1049 AutocapitalizeType HTMLElement::autocapitalizeType() const
1050 {
1051     return autocapitalizeTypeForAttributeValue(attributeWithoutSynchronization(HTMLNames::autocapitalizeAttr));
1052 }
1053
1054 void HTMLElement::setAutocapitalize(const AtomicString& value)
1055 {
1056     setAttributeWithoutSynchronization(autocapitalizeAttr, value);
1057 }
1058
1059 bool HTMLElement::shouldAutocorrect() const
1060 {
1061     auto& autocorrectValue = attributeWithoutSynchronization(HTMLNames::autocorrectAttr);
1062     // Unrecognized values fall back to "on".
1063     return !equalLettersIgnoringASCIICase(autocorrectValue, "off");
1064 }
1065
1066 void HTMLElement::setAutocorrect(bool autocorrect)
1067 {
1068     setAttributeWithoutSynchronization(autocorrectAttr, autocorrect ? AtomicString("on", AtomicString::ConstructFromLiteral) : AtomicString("off", AtomicString::ConstructFromLiteral));
1069 }
1070
1071 #endif
1072
1073 } // namespace WebCore
1074
1075 #ifndef NDEBUG
1076
1077 // For use in the debugger
1078 void dumpInnerHTML(WebCore::HTMLElement*);
1079
1080 void dumpInnerHTML(WebCore::HTMLElement* element)
1081 {
1082     printf("%s\n", element->innerHTML().ascii().data());
1083 }
1084
1085 #endif