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