summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/HTMLElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/html/HTMLElement.cpp')
-rw-r--r--Source/WebCore/html/HTMLElement.cpp815
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