diff options
Diffstat (limited to 'Source/WebCore/html/HTMLElement.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLElement.cpp | 815 |
1 files changed, 387 insertions, 428 deletions
diff --git a/Source/WebCore/html/HTMLElement.cpp b/Source/WebCore/html/HTMLElement.cpp index 3ce325d29..aae8e9ad3 100644 --- a/Source/WebCore/html/HTMLElement.cpp +++ b/Source/WebCore/html/HTMLElement.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004-2017 Apple Inc. All rights reserved. * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) * Copyright (C) 2011 Motorola Mobility. All rights reserved. * @@ -25,12 +25,11 @@ #include "config.h" #include "HTMLElement.h" -#include "Attribute.h" -#include "CSSParser.h" +#include "CSSMarkup.h" #include "CSSPropertyNames.h" #include "CSSValueKeywords.h" #include "CSSValuePool.h" -#include "DOMSettableTokenList.h" +#include "DOMTokenList.h" #include "DocumentFragment.h" #include "ElementAncestorIterator.h" #include "Event.h" @@ -40,23 +39,29 @@ #include "Frame.h" #include "FrameLoader.h" #include "FrameView.h" +#include "HTMLBDIElement.h" #include "HTMLBRElement.h" +#include "HTMLButtonElement.h" #include "HTMLCollection.h" #include "HTMLDocument.h" #include "HTMLElementFactory.h" +#include "HTMLFieldSetElement.h" #include "HTMLFormElement.h" +#include "HTMLInputElement.h" #include "HTMLNames.h" +#include "HTMLOptGroupElement.h" +#include "HTMLOptionElement.h" #include "HTMLParserIdioms.h" -#include "HTMLTemplateElement.h" +#include "HTMLSelectElement.h" +#include "HTMLTextAreaElement.h" #include "HTMLTextFormControlElement.h" #include "NodeTraversal.h" -#include "RenderLineBreak.h" +#include "RenderElement.h" #include "ScriptController.h" -#include "Settings.h" +#include "SimulatedClick.h" #include "StyleProperties.h" #include "SubframeLoader.h" #include "Text.h" -#include "TextIterator.h" #include "XMLNames.h" #include "markup.h" #include <wtf/NeverDestroyed.h> @@ -68,63 +73,26 @@ namespace WebCore { using namespace HTMLNames; using namespace WTF; -PassRefPtr<HTMLElement> HTMLElement::create(const QualifiedName& tagName, Document& document) +Ref<HTMLElement> HTMLElement::create(const QualifiedName& tagName, Document& document) { - return adoptRef(new HTMLElement(tagName, document)); + return adoptRef(*new HTMLElement(tagName, document)); } String HTMLElement::nodeName() const { - // FIXME: Would be nice to have an atomicstring lookup based off uppercase - // chars that does not have to copy the string on a hit in the hash. - // FIXME: We should have a way to detect XHTML elements and replace the hasPrefix() check with it. - if (document().isHTMLDocument() && !tagQName().hasPrefix()) - return tagQName().localNameUpper(); - return Element::nodeName(); -} - -bool HTMLElement::ieForbidsInsertHTML() const -{ - // FIXME: Supposedly IE disallows settting innerHTML, outerHTML - // and createContextualFragment on these tags. We have no tests to - // verify this however, so this list could be totally wrong. - // This list was moved from the previous endTagRequirement() implementation. - // This is also called from editing and assumed to be the list of tags - // for which no end tag should be serialized. It's unclear if the list for - // IE compat and the list for serialization sanity are the same. - if (hasLocalName(areaTag) - || hasLocalName(baseTag) - || hasLocalName(basefontTag) - || hasLocalName(brTag) - || hasLocalName(colTag) - || hasLocalName(embedTag) - || hasLocalName(frameTag) - || hasLocalName(hrTag) - || hasLocalName(imageTag) - || hasLocalName(imgTag) - || hasLocalName(inputTag) - || hasLocalName(isindexTag) - || hasLocalName(linkTag) - || hasLocalName(metaTag) - || hasLocalName(paramTag) - || hasLocalName(sourceTag) - || hasLocalName(wbrTag)) - return true; - // FIXME: I'm not sure why dashboard mode would want to change the - // serialization of <canvas>, that seems like a bad idea. -#if ENABLE(DASHBOARD_SUPPORT) - if (hasLocalName(canvasTag)) { - Settings* settings = document().settings(); - if (settings && settings->usesDashboardBackwardCompatibilityMode()) - return true; + // FIXME: Would be nice to have an AtomicString lookup based off uppercase + // ASCII characters that does not have to copy the string on a hit in the hash. + if (document().isHTMLDocument()) { + if (LIKELY(!tagQName().hasPrefix())) + return tagQName().localNameUpper(); + return Element::nodeName().convertToASCIIUppercase(); } -#endif - return false; + return Element::nodeName(); } static inline CSSValueID unicodeBidiAttributeForDirAuto(HTMLElement& element) { - if (element.hasLocalName(preTag) || element.hasLocalName(textareaTag)) + if (element.hasTagName(preTag) || element.hasTagName(textareaTag)) return CSSValueWebkitPlaintext; // FIXME: For bdo element, dir="auto" should result in "bidi-override isolate" but we don't support having multiple values in unicode-bidi yet. // See https://bugs.webkit.org/show_bug.cgi?id=73164. @@ -133,10 +101,10 @@ static inline CSSValueID unicodeBidiAttributeForDirAuto(HTMLElement& element) unsigned HTMLElement::parseBorderWidthAttribute(const AtomicString& value) const { - unsigned borderWidth = 0; - if (value.isEmpty() || !parseHTMLNonNegativeInteger(value, borderWidth)) - return hasLocalName(tableTag) ? 1 : borderWidth; - return borderWidth; + if (std::optional<unsigned> borderWidth = parseHTMLNonNegativeInteger(value)) + return borderWidth.value(); + + return hasTagName(tableTag) ? 1 : 0; } void HTMLElement::applyBorderAttributeToStyle(const AtomicString& value, MutableStyleProperties& style) @@ -149,7 +117,7 @@ void HTMLElement::mapLanguageAttributeToLocale(const AtomicString& value, Mutabl { if (!value.isEmpty()) { // Have to quote so the locale id is treated as a string instead of as a CSS keyword. - addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, quoteCSSString(value)); + addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, serializeString(value)); } else { // The empty string means the language is explicitly unknown. addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, CSSValueAuto); @@ -165,45 +133,73 @@ bool HTMLElement::isPresentationAttribute(const QualifiedName& name) const static bool isLTROrRTLIgnoringCase(const AtomicString& dirAttributeValue) { - return equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr"); + return equalLettersIgnoringASCIICase(dirAttributeValue, "rtl") || equalLettersIgnoringASCIICase(dirAttributeValue, "ltr"); +} + +enum class ContentEditableType { + Inherit, + True, + False, + PlaintextOnly +}; + +static inline ContentEditableType contentEditableType(const AtomicString& value) +{ + if (value.isNull()) + return ContentEditableType::Inherit; + if (value.isEmpty() || equalLettersIgnoringASCIICase(value, "true")) + return ContentEditableType::True; + if (equalLettersIgnoringASCIICase(value, "false")) + return ContentEditableType::False; + if (equalLettersIgnoringASCIICase(value, "plaintext-only")) + return ContentEditableType::PlaintextOnly; + + return ContentEditableType::Inherit; +} + +static ContentEditableType contentEditableType(const HTMLElement& element) +{ + return contentEditableType(element.attributeWithoutSynchronization(contenteditableAttr)); } void HTMLElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style) { if (name == alignAttr) { - if (equalIgnoringCase(value, "middle")) + if (equalLettersIgnoringASCIICase(value, "middle")) addPropertyToPresentationAttributeStyle(style, CSSPropertyTextAlign, CSSValueCenter); else addPropertyToPresentationAttributeStyle(style, CSSPropertyTextAlign, value); } else if (name == contenteditableAttr) { - if (value.isEmpty() || equalIgnoringCase(value, "true")) { - addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadWrite); - addPropertyToPresentationAttributeStyle(style, CSSPropertyWordWrap, CSSValueBreakWord); - addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitNbspMode, CSSValueSpace); - addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace); -#if PLATFORM(IOS) - addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitTextSizeAdjust, CSSValueNone); -#endif - } else if (equalIgnoringCase(value, "plaintext-only")) { - addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadWritePlaintextOnly); + CSSValueID userModifyValue = CSSValueReadWrite; + switch (contentEditableType(value)) { + case ContentEditableType::Inherit: + return; + case ContentEditableType::False: + userModifyValue = CSSValueReadOnly; + break; + case ContentEditableType::PlaintextOnly: + userModifyValue = CSSValueReadWritePlaintextOnly; + FALLTHROUGH; + case ContentEditableType::True: addPropertyToPresentationAttributeStyle(style, CSSPropertyWordWrap, CSSValueBreakWord); addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitNbspMode, CSSValueSpace); addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace); #if PLATFORM(IOS) addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitTextSizeAdjust, CSSValueNone); #endif - } else if (equalIgnoringCase(value, "false")) - addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, CSSValueReadOnly); + break; + } + addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserModify, userModifyValue); } else if (name == hiddenAttr) { addPropertyToPresentationAttributeStyle(style, CSSPropertyDisplay, CSSValueNone); } else if (name == draggableAttr) { - if (equalIgnoringCase(value, "true")) { + if (equalLettersIgnoringASCIICase(value, "true")) { addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserDrag, CSSValueElement); addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserSelect, CSSValueNone); - } else if (equalIgnoringCase(value, "false")) + } else if (equalLettersIgnoringASCIICase(value, "false")) addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitUserDrag, CSSValueNone); } else if (name == dirAttr) { - if (equalIgnoringCase(value, "auto")) + if (equalLettersIgnoringASCIICase(value, "auto")) addPropertyToPresentationAttributeStyle(style, CSSPropertyUnicodeBidi, unicodeBidiAttributeForDirAuto(*this)); else { if (isLTROrRTLIgnoringCase(value)) @@ -215,18 +211,27 @@ void HTMLElement::collectStyleForPresentationAttribute(const QualifiedName& name mapLanguageAttributeToLocale(value, style); else if (name == langAttr) { // xml:lang has a higher priority than lang. - if (!fastHasAttribute(XMLNames::langAttr)) + if (!hasAttributeWithoutSynchronization(XMLNames::langAttr)) mapLanguageAttributeToLocale(value, style); } else StyledElement::collectStyleForPresentationAttribute(name, value, style); } -static NEVER_INLINE void populateEventNameForAttributeLocalNameMap(HashMap<AtomicStringImpl*, AtomicString>& map) +HTMLElement::EventHandlerNameMap HTMLElement::createEventHandlerNameMap() { - static const QualifiedName* const simpleTable[] = { + EventHandlerNameMap map; + + static const QualifiedName* const table[] = { &onabortAttr, + &onanimationendAttr, + &onanimationiterationAttr, + &onanimationstartAttr, + &onautocompleteAttr, + &onautocompleteerrorAttr, &onbeforecopyAttr, &onbeforecutAttr, + &onbeforeinputAttr, + &onbeforeloadAttr, &onbeforepasteAttr, &onblurAttr, &oncanplayAttr, @@ -251,6 +256,9 @@ static NEVER_INLINE void populateEventNameForAttributeLocalNameMap(HashMap<Atomi &onfocusAttr, &onfocusinAttr, &onfocusoutAttr, + &ongesturechangeAttr, + &ongestureendAttr, + &ongesturestartAttr, &oninputAttr, &oninvalidAttr, &onkeydownAttr, @@ -275,7 +283,9 @@ static NEVER_INLINE void populateEventNameForAttributeLocalNameMap(HashMap<Atomi &onprogressAttr, &onratechangeAttr, &onresetAttr, + &onresizeAttr, &onscrollAttr, + &onsearchAttr, &onseekedAttr, &onseekingAttr, &onselectAttr, @@ -284,165 +294,171 @@ static NEVER_INLINE void populateEventNameForAttributeLocalNameMap(HashMap<Atomi &onsubmitAttr, &onsuspendAttr, &ontimeupdateAttr, + &ontoggleAttr, &ontouchcancelAttr, &ontouchendAttr, + &ontouchforcechangeAttr, &ontouchmoveAttr, &ontouchstartAttr, + &ontransitionendAttr, &onvolumechangeAttr, &onwaitingAttr, - &onwheelAttr, -#if ENABLE(IOS_GESTURE_EVENTS) - &ongesturechangeAttr, - &ongestureendAttr, - &ongesturestartAttr, -#endif -#if ENABLE(FULLSCREEN_API) + &onwebkitbeginfullscreenAttr, + &onwebkitcurrentplaybacktargetiswirelesschangedAttr, + &onwebkitendfullscreenAttr, &onwebkitfullscreenchangeAttr, &onwebkitfullscreenerrorAttr, -#endif + &onwebkitkeyaddedAttr, + &onwebkitkeyerrorAttr, + &onwebkitkeymessageAttr, + &onwebkitmouseforcechangedAttr, + &onwebkitmouseforcedownAttr, + &onwebkitmouseforcewillbeginAttr, + &onwebkitmouseforceupAttr, + &onwebkitneedkeyAttr, + &onwebkitplaybacktargetavailabilitychangedAttr, + &onwebkitpresentationmodechangedAttr, + &onwebkitwillrevealbottomAttr, + &onwebkitwillrevealleftAttr, + &onwebkitwillrevealrightAttr, + &onwebkitwillrevealtopAttr, + &onwheelAttr, }; - for (unsigned i = 0, size = WTF_ARRAY_LENGTH(simpleTable); i < size; ++i) { - // FIXME: Would be nice to check these against the actual event names in eventNames(). - // Not obvious how to do that simply, though. - const AtomicString& attributeName = simpleTable[i]->localName(); - - // Remove the "on" prefix. Requires some memory allocation and computing a hash, but - // by not using pointers from eventNames(), simpleTable can be initialized at compile time. - AtomicString eventName = attributeName.string().substring(2); + populateEventHandlerNameMap(map, table); - map.add(attributeName.impl(), eventName); - } - - struct CustomMapping { + struct UnusualMapping { const QualifiedName& attributeName; const AtomicString& eventName; }; - const CustomMapping customTable[] = { - { ontransitionendAttr, eventNames().webkitTransitionEndEvent }, + const UnusualMapping unusualPairsTable[] = { { onwebkitanimationendAttr, eventNames().webkitAnimationEndEvent }, { onwebkitanimationiterationAttr, eventNames().webkitAnimationIterationEvent }, { onwebkitanimationstartAttr, eventNames().webkitAnimationStartEvent }, { onwebkittransitionendAttr, eventNames().webkitTransitionEndEvent }, }; - for (unsigned i = 0, size = WTF_ARRAY_LENGTH(customTable); i < size; ++i) - map.add(customTable[i].attributeName.localName().impl(), customTable[i].eventName); + for (auto& entry : unusualPairsTable) + map.add(entry.attributeName.localName().impl(), entry.eventName); + + return map; } -void HTMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value) +void HTMLElement::populateEventHandlerNameMap(EventHandlerNameMap& map, const QualifiedName* const table[], size_t tableSize) { - if (isIdAttributeName(name) || name == classAttr || name == styleAttr) - return StyledElement::parseAttribute(name, value); + for (size_t i = 0; i < tableSize; ++i) { + auto* entry = table[i]; - if (name == dirAttr) - dirAttributeChanged(value); - else if (name == tabindexAttr) { - int tabindex = 0; - if (value.isEmpty()) - clearTabIndexExplicitlyIfNeeded(); - else if (parseHTMLInteger(value, tabindex)) { - // Clamp tabindex to the range of 'short' to match Firefox's behavior. - setTabIndexExplicitly(std::max(static_cast<int>(std::numeric_limits<short>::min()), std::min(tabindex, static_cast<int>(std::numeric_limits<short>::max())))); - } - } else if (name.namespaceURI().isNull()) { - // FIXME: Can we do this even faster by checking the local name "on" prefix before we do anything with the map? - static NeverDestroyed<HashMap<AtomicStringImpl*, AtomicString>> eventNamesGlobal; - auto& eventNames = eventNamesGlobal.get(); - if (eventNames.isEmpty()) - populateEventNameForAttributeLocalNameMap(eventNames); - const AtomicString& eventName = eventNames.get(name.localName().impl()); - if (!eventName.isNull()) - setAttributeEventListener(eventName, name, value); + // FIXME: Would be nice to check these against the actual event names in eventNames(). + // Not obvious how to do that simply, though. + auto& attributeName = entry->localName(); + + // Remove the "on" prefix. Requires some memory allocation and computing a hash, but by not + // using pointers from eventNames(), the passed-in table can be initialized at compile time. + AtomicString eventName = attributeName.string().substring(2); + + map.add(attributeName.impl(), WTFMove(eventName)); } } -String HTMLElement::innerHTML() const +const AtomicString& HTMLElement::eventNameForEventHandlerAttribute(const QualifiedName& attributeName, const EventHandlerNameMap& map) { - return createMarkup(*this, ChildrenOnly); + ASSERT(!attributeName.localName().isNull()); + + // Event handler attributes have no namespace. + if (!attributeName.namespaceURI().isNull()) + return nullAtom; + + // Fast early return for names that don't start with "on". + AtomicStringImpl& localName = *attributeName.localName().impl(); + if (localName.length() < 3 || localName[0] != 'o' || localName[1] != 'n') + return nullAtom; + + auto it = map.find(&localName); + return it == map.end() ? nullAtom : it->value; } -String HTMLElement::outerHTML() const +const AtomicString& HTMLElement::eventNameForEventHandlerAttribute(const QualifiedName& attributeName) { - return createMarkup(*this); + static NeverDestroyed<EventHandlerNameMap> map = createEventHandlerNameMap(); + return eventNameForEventHandlerAttribute(attributeName, map.get()); } -void HTMLElement::setInnerHTML(const String& html, ExceptionCode& ec) +Node::Editability HTMLElement::editabilityFromContentEditableAttr(const Node& node) { - if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(html, this, AllowScriptingContent, ec)) { - ContainerNode* container = this; -#if ENABLE(TEMPLATE_ELEMENT) - if (hasLocalName(templateTag)) - container = toHTMLTemplateElement(this)->content(); -#endif - replaceChildrenWithFragment(*container, fragment.release(), ec); + if (auto* startElement = is<Element>(node) ? &downcast<Element>(node) : node.parentElement()) { + for (auto& element : lineageOfType<HTMLElement>(*startElement)) { + switch (contentEditableType(element)) { + case ContentEditableType::True: + return Editability::CanEditRichly; + case ContentEditableType::PlaintextOnly: + return Editability::CanEditPlainText; + case ContentEditableType::False: + return Editability::ReadOnly; + case ContentEditableType::Inherit: + break; + } + } } + + auto& document = node.document(); + if (is<HTMLDocument>(document)) + return downcast<HTMLDocument>(document).inDesignMode() ? Editability::CanEditRichly : Editability::ReadOnly; + + return Editability::ReadOnly; } -static void mergeWithNextTextNode(Text& node, ExceptionCode& ec) +bool HTMLElement::matchesReadWritePseudoClass() const { - Node* next = node.nextSibling(); - if (!next || !next->isTextNode()) - return; - - Ref<Text> textNode(node); - Ref<Text> textNext(toText(*next)); - textNode->appendData(textNext->data(), ec); - if (ec) - return; - textNext->remove(ec); + return editabilityFromContentEditableAttr(*this) != Editability::ReadOnly; } -void HTMLElement::setOuterHTML(const String& html, ExceptionCode& ec) +void HTMLElement::parseAttribute(const QualifiedName& name, const AtomicString& value) { - Element* p = parentElement(); - if (!p || !p->isHTMLElement()) { - ec = NO_MODIFICATION_ALLOWED_ERR; + if (name == dirAttr) { + dirAttributeChanged(value); return; } - RefPtr<HTMLElement> parent = toHTMLElement(p); - RefPtr<Node> prev = previousSibling(); - RefPtr<Node> next = nextSibling(); - RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(html, parent.get(), AllowScriptingContent, ec); - if (ec) + if (name == tabindexAttr) { + if (value.isEmpty()) + clearTabIndexExplicitlyIfNeeded(); + else if (std::optional<int> tabIndex = parseHTMLInteger(value)) + setTabIndexExplicitly(tabIndex.value()); return; - - parent->replaceChild(fragment.release(), this, ec); - RefPtr<Node> node = next ? next->previousSibling() : nullptr; - if (!ec && node && node->isTextNode()) - mergeWithNextTextNode(toText(*node), ec); - if (!ec && prev && prev->isTextNode()) - mergeWithNextTextNode(toText(*prev), ec); + } + + auto& eventName = eventNameForEventHandlerAttribute(name); + if (!eventName.isNull()) + setAttributeEventListener(eventName, name, value); } -PassRefPtr<DocumentFragment> HTMLElement::textToFragment(const String& text, ExceptionCode& ec) +static Ref<DocumentFragment> textToFragment(Document& document, const String& text) { - RefPtr<DocumentFragment> fragment = DocumentFragment::create(document()); - unsigned int i, length = text.length(); - UChar c = 0; - for (unsigned int start = 0; start < length; ) { + auto fragment = DocumentFragment::create(document); + // It's safe to dispatch events on the new fragment since author scripts have no access to it yet. + NoEventDispatchAssertion::EventAllowedScope allowedScope(fragment); + + for (unsigned start = 0, length = text.length(); start < length; ) { // Find next line break. + UChar c = 0; + unsigned i; for (i = start; i < length; i++) { c = text[i]; if (c == '\r' || c == '\n') break; } - fragment->appendChild(Text::create(document(), text.substring(start, i - start)), ec); - if (ec) - return nullptr; - - if (c == '\r' || c == '\n') { - fragment->appendChild(HTMLBRElement::create(document()), ec); - if (ec) - return nullptr; - // Make sure \r\n doesn't result in two line breaks. - if (c == '\r' && i + 1 < length && text[i + 1] == '\n') - i++; - } + fragment->appendChild(Text::create(document, text.substring(start, i - start))); + if (i == length) + break; + + fragment->appendChild(HTMLBRElement::create(document)); + // Make sure \r\n doesn't result in two line breaks. + if (c == '\r' && i + 1 < length && text[i + 1] == '\n') + ++i; start = i + 1; // Character after line break. } @@ -450,174 +466,105 @@ PassRefPtr<DocumentFragment> HTMLElement::textToFragment(const String& text, Exc return fragment; } -void HTMLElement::setInnerText(const String& text, ExceptionCode& ec) +// Returns the conforming 'dir' value associated with the state the attribute is in (in its canonical case), if any, +// or the empty string if the attribute is in a state that has no associated keyword value or if the attribute is +// not in a defined state (e.g. the attribute is missing and there is no missing value default). +// http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#limited-to-only-known-values +static inline const AtomicString& toValidDirValue(const AtomicString& value) { - if (ieForbidsInsertHTML()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) || - hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || - hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) || - hasLocalName(trTag)) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } + static NeverDestroyed<AtomicString> ltrValue("ltr", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> rtlValue("rtl", AtomicString::ConstructFromLiteral); + static NeverDestroyed<AtomicString> autoValue("auto", AtomicString::ConstructFromLiteral); + if (equalLettersIgnoringASCIICase(value, "ltr")) + return ltrValue; + if (equalLettersIgnoringASCIICase(value, "rtl")) + return rtlValue; + if (equalLettersIgnoringASCIICase(value, "auto")) + return autoValue; + return nullAtom; +} + +const AtomicString& HTMLElement::dir() const +{ + return toValidDirValue(attributeWithoutSynchronization(dirAttr)); +} +void HTMLElement::setDir(const AtomicString& value) +{ + setAttributeWithoutSynchronization(dirAttr, value); +} + +ExceptionOr<void> HTMLElement::setInnerText(const String& text) +{ // FIXME: This doesn't take whitespace collapsing into account at all. if (!text.contains('\n') && !text.contains('\r')) { - if (text.isEmpty()) { - removeChildren(); - return; - } - replaceChildrenWithText(*this, text, ec); - return; + if (text.isEmpty()) + replaceAllChildren(nullptr); + else + replaceAllChildren(document().createTextNode(text)); + return { }; } // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer? // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded? // For example, for the contents of textarea elements that are display:none? - auto r = renderer(); - if ((r && r->style().preserveNewline()) || (inDocument() && isTextControlInnerTextElement())) { + auto* r = renderer(); + if ((r && r->style().preserveNewline()) || (isConnected() && isTextControlInnerTextElement())) { if (!text.contains('\r')) { - replaceChildrenWithText(*this, text, ec); - return; + replaceAllChildren(document().createTextNode(text)); + return { }; } String textWithConsistentLineBreaks = text; textWithConsistentLineBreaks.replace("\r\n", "\n"); textWithConsistentLineBreaks.replace('\r', '\n'); - replaceChildrenWithText(*this, textWithConsistentLineBreaks, ec); - return; + replaceAllChildren(document().createTextNode(textWithConsistentLineBreaks)); + return { }; } // Add text nodes and <br> elements. - ec = 0; - RefPtr<DocumentFragment> fragment = textToFragment(text, ec); - if (!ec) - replaceChildrenWithFragment(*this, fragment.release(), ec); + auto fragment = textToFragment(document(), text); + // FIXME: This should use replaceAllChildren() once it accepts DocumentFragments as input. + // It's safe to dispatch events on the new fragment since author scripts have no access to it yet. + NoEventDispatchAssertion::EventAllowedScope allowedScope(fragment.get()); + return replaceChildrenWithFragment(*this, WTFMove(fragment)); } -void HTMLElement::setOuterText(const String& text, ExceptionCode& ec) +ExceptionOr<void> HTMLElement::setOuterText(const String& text) { - if (ieForbidsInsertHTML()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) || - hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || - hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) || - hasLocalName(trTag)) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - RefPtr<ContainerNode> parent = parentNode(); - if (!parent) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } + if (!parent) + return Exception { NO_MODIFICATION_ALLOWED_ERR }; RefPtr<Node> prev = previousSibling(); RefPtr<Node> next = nextSibling(); RefPtr<Node> newChild; - ec = 0; - + // Convert text to fragment with <br> tags instead of linebreaks if needed. if (text.contains('\r') || text.contains('\n')) - newChild = textToFragment(text, ec); + newChild = textToFragment(document(), text); else newChild = Text::create(document(), text); - if (!this || !parentNode()) - ec = HIERARCHY_REQUEST_ERR; - if (ec) - return; - parent->replaceChild(newChild.release(), this, ec); - - RefPtr<Node> node = next ? next->previousSibling() : nullptr; - if (!ec && node && node->isTextNode()) - mergeWithNextTextNode(toText(*node), ec); - if (!ec && prev && prev->isTextNode()) - mergeWithNextTextNode(toText(*prev), ec); -} - -Node* HTMLElement::insertAdjacent(const String& where, Node* newChild, ExceptionCode& ec) -{ - // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd", - // a document fragment is created and the elements appended in the correct order. This document - // fragment isn't returned anywhere. - // - // This is impossible for us to implement as the DOM tree does not allow for such structures, - // Opera also appears to disallow such usage. - - if (equalIgnoringCase(where, "beforeBegin")) { - ContainerNode* parent = this->parentNode(); - return (parent && parent->insertBefore(newChild, this, ec)) ? newChild : nullptr; - } - - if (equalIgnoringCase(where, "afterBegin")) - return insertBefore(newChild, firstChild(), ec) ? newChild : nullptr; - - if (equalIgnoringCase(where, "beforeEnd")) - return appendChild(newChild, ec) ? newChild : nullptr; + if (!parentNode()) + return Exception { HIERARCHY_REQUEST_ERR }; - if (equalIgnoringCase(where, "afterEnd")) { - ContainerNode* parent = this->parentNode(); - return (parent && parent->insertBefore(newChild, nextSibling(), ec)) ? newChild : nullptr; - } - - // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative. - ec = NOT_SUPPORTED_ERR; - return nullptr; -} + auto replaceResult = parent->replaceChild(*newChild, *this); + if (replaceResult.hasException()) + return replaceResult.releaseException(); -Element* HTMLElement::insertAdjacentElement(const String& where, Element* newChild, ExceptionCode& ec) -{ - if (!newChild) { - // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative. - ec = TYPE_MISMATCH_ERR; - return nullptr; + RefPtr<Node> node = next ? next->previousSibling() : nullptr; + if (is<Text>(node.get())) { + auto result = mergeWithNextTextNode(downcast<Text>(*node)); + if (result.hasException()) + return result.releaseException(); } - - Node* returnValue = insertAdjacent(where, newChild, ec); - ASSERT_WITH_SECURITY_IMPLICATION(!returnValue || returnValue->isElementNode()); - return toElement(returnValue); -} - -// Step 3 of http://www.whatwg.org/specs/web-apps/current-work/multipage/apis-in-html-documents.html#insertadjacenthtml() -static Element* contextElementForInsertion(const String& where, Element* element, ExceptionCode& ec) -{ - if (equalIgnoringCase(where, "beforeBegin") || equalIgnoringCase(where, "afterEnd")) { - ContainerNode* parent = element->parentNode(); - if (parent && !parent->isElementNode()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return nullptr; - } - ASSERT_WITH_SECURITY_IMPLICATION(!parent || parent->isElementNode()); - return toElement(parent); + if (is<Text>(prev.get())) { + auto result = mergeWithNextTextNode(downcast<Text>(*prev)); + if (result.hasException()) + return result.releaseException(); } - if (equalIgnoringCase(where, "afterBegin") || equalIgnoringCase(where, "beforeEnd")) - return element; - ec = SYNTAX_ERR; - return nullptr; -} - -void HTMLElement::insertAdjacentHTML(const String& where, const String& markup, ExceptionCode& ec) -{ - Element* contextElement = contextElementForInsertion(where, this, ec); - if (!contextElement) - return; - RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, contextElement, AllowScriptingContent, ec); - if (!fragment) - return; - insertAdjacent(where, fragment.get(), ec); -} - -void HTMLElement::insertAdjacentText(const String& where, const String& text, ExceptionCode& ec) -{ - RefPtr<Text> textNode = document().createTextNode(text); - insertAdjacent(where, textNode.get(), ec); + return { }; } void HTMLElement::applyAlignmentAttributeToStyle(const AtomicString& alignment, MutableStyleProperties& style) @@ -627,25 +574,25 @@ void HTMLElement::applyAlignmentAttributeToStyle(const AtomicString& alignment, CSSValueID floatValue = CSSValueInvalid; CSSValueID verticalAlignValue = CSSValueInvalid; - if (equalIgnoringCase(alignment, "absmiddle")) + if (equalLettersIgnoringASCIICase(alignment, "absmiddle")) verticalAlignValue = CSSValueMiddle; - else if (equalIgnoringCase(alignment, "absbottom")) + else if (equalLettersIgnoringASCIICase(alignment, "absbottom")) verticalAlignValue = CSSValueBottom; - else if (equalIgnoringCase(alignment, "left")) { + else if (equalLettersIgnoringASCIICase(alignment, "left")) { floatValue = CSSValueLeft; verticalAlignValue = CSSValueTop; - } else if (equalIgnoringCase(alignment, "right")) { + } else if (equalLettersIgnoringASCIICase(alignment, "right")) { floatValue = CSSValueRight; verticalAlignValue = CSSValueTop; - } else if (equalIgnoringCase(alignment, "top")) + } else if (equalLettersIgnoringASCIICase(alignment, "top")) verticalAlignValue = CSSValueTop; - else if (equalIgnoringCase(alignment, "middle")) + else if (equalLettersIgnoringASCIICase(alignment, "middle")) verticalAlignValue = CSSValueWebkitBaselineMiddle; - else if (equalIgnoringCase(alignment, "center")) + else if (equalLettersIgnoringASCIICase(alignment, "center")) verticalAlignValue = CSSValueMiddle; - else if (equalIgnoringCase(alignment, "bottom")) + else if (equalLettersIgnoringASCIICase(alignment, "bottom")) verticalAlignValue = CSSValueBaseline; - else if (equalIgnoringCase(alignment, "texttop")) + else if (equalLettersIgnoringASCIICase(alignment, "texttop")) verticalAlignValue = CSSValueTextTop; if (floatValue != CSSValueInvalid) @@ -667,42 +614,42 @@ bool HTMLElement::supportsFocus() const String HTMLElement::contentEditable() const { - const AtomicString& value = fastGetAttribute(contenteditableAttr); - - if (value.isNull()) + switch (contentEditableType(*this)) { + case ContentEditableType::Inherit: return ASCIILiteral("inherit"); - if (value.isEmpty() || equalIgnoringCase(value, "true")) + case ContentEditableType::True: return ASCIILiteral("true"); - if (equalIgnoringCase(value, "false")) + case ContentEditableType::False: return ASCIILiteral("false"); - if (equalIgnoringCase(value, "plaintext-only")) + case ContentEditableType::PlaintextOnly: return ASCIILiteral("plaintext-only"); - + } return ASCIILiteral("inherit"); } -void HTMLElement::setContentEditable(const String& enabled, ExceptionCode& ec) +ExceptionOr<void> HTMLElement::setContentEditable(const String& enabled) { - if (equalIgnoringCase(enabled, "true")) - setAttribute(contenteditableAttr, AtomicString("true", AtomicString::ConstructFromLiteral)); - else if (equalIgnoringCase(enabled, "false")) - setAttribute(contenteditableAttr, AtomicString("false", AtomicString::ConstructFromLiteral)); - else if (equalIgnoringCase(enabled, "plaintext-only")) - setAttribute(contenteditableAttr, AtomicString("plaintext-only", AtomicString::ConstructFromLiteral)); - else if (equalIgnoringCase(enabled, "inherit")) + if (equalLettersIgnoringASCIICase(enabled, "true")) + setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("true", AtomicString::ConstructFromLiteral)); + else if (equalLettersIgnoringASCIICase(enabled, "false")) + setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("false", AtomicString::ConstructFromLiteral)); + else if (equalLettersIgnoringASCIICase(enabled, "plaintext-only")) + setAttributeWithoutSynchronization(contenteditableAttr, AtomicString("plaintext-only", AtomicString::ConstructFromLiteral)); + else if (equalLettersIgnoringASCIICase(enabled, "inherit")) removeAttribute(contenteditableAttr); else - ec = SYNTAX_ERR; + return Exception { SYNTAX_ERR }; + return { }; } bool HTMLElement::draggable() const { - return equalIgnoringCase(fastGetAttribute(draggableAttr), "true"); + return equalLettersIgnoringASCIICase(attributeWithoutSynchronization(draggableAttr), "true"); } void HTMLElement::setDraggable(bool value) { - setAttribute(draggableAttr, value + setAttributeWithoutSynchronization(draggableAttr, value ? AtomicString("true", AtomicString::ConstructFromLiteral) : AtomicString("false", AtomicString::ConstructFromLiteral)); } @@ -714,14 +661,14 @@ bool HTMLElement::spellcheck() const void HTMLElement::setSpellcheck(bool enable) { - setAttribute(spellcheckAttr, enable + setAttributeWithoutSynchronization(spellcheckAttr, enable ? AtomicString("true", AtomicString::ConstructFromLiteral) : AtomicString("false", AtomicString::ConstructFromLiteral)); } void HTMLElement::click() { - dispatchSimulatedClick(nullptr, SendNoEvents, DoNotShowPressedLook); + simulateClick(*this, nullptr, SendNoEvents, DoNotShowPressedLook, SimulatedClickSource::Bindings); } void HTMLElement::accessKeyAction(bool sendMouseEvents) @@ -731,88 +678,64 @@ void HTMLElement::accessKeyAction(bool sendMouseEvents) String HTMLElement::title() const { - return fastGetAttribute(titleAttr); + return attributeWithoutSynchronization(titleAttr); } -short HTMLElement::tabIndex() const +int HTMLElement::tabIndex() const { if (supportsFocus()) return Element::tabIndex(); return -1; } -void HTMLElement::setTabIndex(int value) -{ - setIntegralAttribute(tabindexAttr, value); -} - -TranslateAttributeMode HTMLElement::translateAttributeMode() const -{ - const AtomicString& value = fastGetAttribute(translateAttr); - - if (value.isNull()) - return TranslateAttributeInherit; - if (equalIgnoringCase(value, "yes") || value.isEmpty()) - return TranslateAttributeYes; - if (equalIgnoringCase(value, "no")) - return TranslateAttributeNo; - - return TranslateAttributeInherit; -} - bool HTMLElement::translate() const { for (auto& element : lineageOfType<HTMLElement>(*this)) { - TranslateAttributeMode mode = element.translateAttributeMode(); - if (mode == TranslateAttributeInherit) - continue; - ASSERT(mode == TranslateAttributeYes || mode == TranslateAttributeNo); - return mode == TranslateAttributeYes; + const AtomicString& value = element.attributeWithoutSynchronization(translateAttr); + if (equalLettersIgnoringASCIICase(value, "yes") || (value.isEmpty() && !value.isNull())) + return true; + if (equalLettersIgnoringASCIICase(value, "no")) + return false; } - // Default on the root element is translate=yes. return true; } void HTMLElement::setTranslate(bool enable) { - setAttribute(translateAttr, enable ? "yes" : "no"); -} - -PassRefPtr<HTMLCollection> HTMLElement::children() -{ - return ensureCachedHTMLCollection(NodeChildren); + setAttributeWithoutSynchronization(translateAttr, enable ? "yes" : "no"); } bool HTMLElement::rendererIsNeeded(const RenderStyle& style) { - if (hasLocalName(noscriptTag)) { + if (hasTagName(noscriptTag)) { Frame* frame = document().frame(); if (frame && frame->script().canExecuteScripts(NotAboutToExecuteScript)) return false; - } else if (hasLocalName(noembedTag)) { + } else if (hasTagName(noembedTag)) { Frame* frame = document().frame(); - if (frame && frame->loader().subframeLoader().allowPlugins(NotAboutToInstantiatePlugin)) + if (frame && frame->loader().subframeLoader().allowPlugins()) return false; } return StyledElement::rendererIsNeeded(style); } -RenderPtr<RenderElement> HTMLElement::createElementRenderer(PassRef<RenderStyle> style) +RenderPtr<RenderElement> HTMLElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&) { - if (hasLocalName(wbrTag)) - return createRenderer<RenderLineBreak>(*this, std::move(style)); - return RenderElement::createFor(*this, std::move(style)); + return RenderElement::createFor(*this, WTFMove(style)); } -HTMLFormElement* HTMLElement::virtualForm() const +HTMLFormElement* HTMLElement::form() const { return HTMLFormElement::findClosestFormAncestor(*this); } static inline bool elementAffectsDirectionality(const Node& node) { - return node.isHTMLElement() && (node.hasTagName(bdiTag) || toHTMLElement(node).hasAttribute(dirAttr)); + if (!is<HTMLElement>(node)) + return false; + const HTMLElement& element = downcast<HTMLElement>(node); + return is<HTMLBDIElement>(element) || element.hasAttributeWithoutSynchronization(dirAttr); } static void setHasDirAutoFlagRecursively(Node* firstNode, bool flag, Node* lastNode = nullptr) @@ -828,13 +751,13 @@ static void setHasDirAutoFlagRecursively(Node* firstNode, bool flag, Node* lastN if (elementAffectsDirectionality(*node)) { if (node == lastNode) return; - node = NodeTraversal::nextSkippingChildren(node, firstNode); + node = NodeTraversal::nextSkippingChildren(*node, firstNode); continue; } node->setSelfOrAncestorHasDirAutoAttribute(flag); if (node == lastNode) return; - node = NodeTraversal::next(node, firstNode); + node = NodeTraversal::next(*node, firstNode); } } @@ -846,8 +769,8 @@ void HTMLElement::childrenChanged(const ChildChange& change) bool HTMLElement::hasDirectionAuto() const { - const AtomicString& direction = fastGetAttribute(dirAttr); - return (hasTagName(bdiTag) && direction.isNull()) || equalIgnoringCase(direction, "auto"); + const AtomicString& direction = attributeWithoutSynchronization(dirAttr); + return (hasTagName(bdiTag) && direction.isNull()) || equalLettersIgnoringASCIICase(direction, "auto"); } TextDirection HTMLElement::directionalityIfhasDirAutoAttribute(bool& isAuto) const @@ -863,29 +786,29 @@ TextDirection HTMLElement::directionalityIfhasDirAutoAttribute(bool& isAuto) con TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) const { - if (isHTMLTextFormControlElement(*this)) { - HTMLTextFormControlElement* textElement = toHTMLTextFormControlElement(const_cast<HTMLElement*>(this)); + if (is<HTMLTextFormControlElement>(*this)) { + HTMLTextFormControlElement& textElement = downcast<HTMLTextFormControlElement>(const_cast<HTMLElement&>(*this)); bool hasStrongDirectionality; - UCharDirection textDirection = textElement->value().defaultWritingDirection(&hasStrongDirectionality); + UCharDirection textDirection = textElement.value().defaultWritingDirection(&hasStrongDirectionality); if (strongDirectionalityTextNode) - *strongDirectionalityTextNode = hasStrongDirectionality ? textElement : nullptr; + *strongDirectionalityTextNode = hasStrongDirectionality ? &textElement : nullptr; return (textDirection == U_LEFT_TO_RIGHT) ? LTR : RTL; } Node* node = firstChild(); while (node) { // Skip bdi, script, style and text form controls. - if (equalIgnoringCase(node->nodeName(), "bdi") || node->hasTagName(scriptTag) || node->hasTagName(styleTag) - || (node->isElementNode() && toElement(node)->isTextFormControl())) { - node = NodeTraversal::nextSkippingChildren(node, this); + if (equalLettersIgnoringASCIICase(node->nodeName(), "bdi") || node->hasTagName(scriptTag) || node->hasTagName(styleTag) + || (is<Element>(*node) && downcast<Element>(*node).isTextFormControl())) { + node = NodeTraversal::nextSkippingChildren(*node, this); continue; } // Skip elements with valid dir attribute - if (node->isElementNode()) { - AtomicString dirAttributeValue = toElement(node)->fastGetAttribute(dirAttr); - if (isLTROrRTLIgnoringCase(dirAttributeValue) || equalIgnoringCase(dirAttributeValue, "auto")) { - node = NodeTraversal::nextSkippingChildren(node, this); + if (is<Element>(*node)) { + AtomicString dirAttributeValue = downcast<Element>(*node).attributeWithoutSynchronization(dirAttr); + if (isLTROrRTLIgnoringCase(dirAttributeValue) || equalLettersIgnoringASCIICase(dirAttributeValue, "auto")) { + node = NodeTraversal::nextSkippingChildren(*node, this); continue; } } @@ -899,7 +822,7 @@ TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) c return (textDirection == U_LEFT_TO_RIGHT) ? LTR : RTL; } } - node = NodeTraversal::next(node, this); + node = NodeTraversal::next(*node, this); } if (strongDirectionalityTextNode) *strongDirectionalityTextNode = nullptr; @@ -910,10 +833,10 @@ void HTMLElement::dirAttributeChanged(const AtomicString& value) { Element* parent = parentElement(); - if (parent && parent->isHTMLElement() && parent->selfOrAncestorHasDirAutoAttribute()) - toHTMLElement(parent)->adjustDirectionalityIfNeededAfterChildAttributeChanged(this); + if (is<HTMLElement>(parent) && parent->selfOrAncestorHasDirAutoAttribute()) + downcast<HTMLElement>(*parent).adjustDirectionalityIfNeededAfterChildAttributeChanged(this); - if (equalIgnoringCase(value, "auto")) + if (equalLettersIgnoringASCIICase(value, "auto")) calculateAndAdjustDirectionality(); } @@ -927,7 +850,7 @@ void HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged(Element return; for (auto& elementToAdjust : elementLineage(this)) { if (elementAffectsDirectionality(elementToAdjust)) { - elementToAdjust.setNeedsStyleRecalc(); + elementToAdjust.invalidateStyleForSubtree(); return; } } @@ -939,28 +862,19 @@ void HTMLElement::calculateAndAdjustDirectionality() TextDirection textDirection = directionality(&strongDirectionalityTextNode); setHasDirAutoFlagRecursively(this, true, strongDirectionalityTextNode); if (renderer() && renderer()->style().direction() != textDirection) - setNeedsStyleRecalc(); + invalidateStyleForSubtree(); } void HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged(Element* beforeChange, ChildChangeType changeType) { // FIXME: This function looks suspicious. - if (document().renderView() && (changeType == ElementRemoved || changeType == TextRemoved)) { - Node* node = beforeChange ? beforeChange->nextSibling() : nullptr; - for (; node; node = node->nextSibling()) { - if (elementAffectsDirectionality(*node)) - continue; - - setHasDirAutoFlagRecursively(node, false); - } - } if (!selfOrAncestorHasDirAutoAttribute()) return; Node* oldMarkedNode = nullptr; if (beforeChange) - oldMarkedNode = changeType == ElementInserted ? ElementTraversal::nextSibling(beforeChange) : beforeChange->nextSibling(); + oldMarkedNode = changeType == ElementInserted ? ElementTraversal::nextSibling(*beforeChange) : beforeChange->nextSibling(); while (oldMarkedNode && elementAffectsDirectionality(*oldMarkedNode)) oldMarkedNode = oldMarkedNode->nextSibling(); @@ -975,11 +889,6 @@ void HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged(Element* befo } } -bool HTMLElement::isURLAttribute(const Attribute& attribute) const -{ - return StyledElement::isURLAttribute(attribute); -} - void HTMLElement::addHTMLLengthToStyle(MutableStyleProperties& style, CSSPropertyID propertyID, const String& value) { // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct @@ -1078,15 +987,18 @@ void HTMLElement::addHTMLColorToStyle(MutableStyleProperties& style, CSSProperty String colorString = attributeValue.stripWhiteSpace(); // "transparent" doesn't apply a color either. - if (equalIgnoringCase(colorString, "transparent")) + if (equalLettersIgnoringASCIICase(colorString, "transparent")) return; - // If the string is a named CSS color or a 3/6-digit hex color, use that. - Color parsedColor(colorString); - if (!parsedColor.isValid()) - parsedColor.setRGB(parseColorStringWithCrazyLegacyRules(colorString)); + Color color; + // We can't always use the default Color constructor because it accepts + // 4/8-digit hex, which conflict with some legacy HTML content using attributes. + if ((colorString.length() != 5 && colorString.length() != 9) || colorString[0] != '#') + color = Color(colorString); + if (!color.isValid()) + color = Color(parseColorStringWithCrazyLegacyRules(colorString)); - style.setProperty(propertyID, cssValuePool().createColorValue(parsedColor.rgb())); + style.setProperty(propertyID, CSSValuePool::singleton().createColorValue(color.rgb())); } bool HTMLElement::willRespondToMouseMoveEvents() @@ -1104,6 +1016,53 @@ bool HTMLElement::willRespondToMouseClickEvents() return !isDisabledFormControl() && Element::willRespondToMouseClickEvents(); } +bool HTMLElement::canBeActuallyDisabled() const +{ + return is<HTMLButtonElement>(*this) + || is<HTMLInputElement>(*this) + || is<HTMLSelectElement>(*this) + || is<HTMLTextAreaElement>(*this) + || is<HTMLOptGroupElement>(*this) + || is<HTMLOptionElement>(*this) + || is<HTMLFieldSetElement>(*this); +} + +bool HTMLElement::isActuallyDisabled() const +{ + return canBeActuallyDisabled() && isDisabledFormControl(); +} + +#if ENABLE(IOS_AUTOCORRECT_AND_AUTOCAPITALIZE) + +const AtomicString& HTMLElement::autocapitalize() const +{ + return stringForAutocapitalizeType(autocapitalizeType()); +} + +AutocapitalizeType HTMLElement::autocapitalizeType() const +{ + return autocapitalizeTypeForAttributeValue(attributeWithoutSynchronization(HTMLNames::autocapitalizeAttr)); +} + +void HTMLElement::setAutocapitalize(const AtomicString& value) +{ + setAttributeWithoutSynchronization(autocapitalizeAttr, value); +} + +bool HTMLElement::shouldAutocorrect() const +{ + auto autocorrectValue = attributeWithoutSynchronization(HTMLNames::autocorrectAttr); + // Unrecognized values fall back to "on". + return !equalLettersIgnoringASCIICase(autocorrectValue, "off"); +} + +void HTMLElement::setAutocorrect(bool autocorrect) +{ + setAttributeWithoutSynchronization(autocorrectAttr, autocorrect ? AtomicString("on", AtomicString::ConstructFromLiteral) : AtomicString("off", AtomicString::ConstructFromLiteral)); +} + +#endif + } // namespace WebCore #ifndef NDEBUG |