REGRESSION (r238078): Do not draw caps lock indicator when Strong Password button...
[WebKit-https.git] / Source / WebCore / html / TextFieldInputType.cpp
index 3b7fb92..471b77a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2010 Google Inc. All rights reserved.
- * Copyright (C) 2011, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2018 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
 #include "BeforeTextInsertedEvent.h"
 #include "Chrome.h"
 #include "ChromeClient.h"
+#include "DOMFormData.h"
 #include "Editor.h"
-#include "FormDataList.h"
+#include "EventNames.h"
 #include "Frame.h"
 #include "FrameSelection.h"
 #include "HTMLInputElement.h"
 #include "HTMLNames.h"
+#include "HTMLParserIdioms.h"
 #include "KeyboardEvent.h"
 #include "LocalizedStrings.h"
 #include "NodeRenderStyle.h"
 #include "RenderLayer.h"
 #include "RenderTextControlSingleLine.h"
 #include "RenderTheme.h"
+#include "RuntimeEnabledFeatures.h"
 #include "ShadowRoot.h"
-#include "TextBreakIterator.h"
 #include "TextControlInnerElements.h"
 #include "TextEvent.h"
 #include "TextIterator.h"
 #include "TextNodeTraversal.h"
 #include "WheelEvent.h"
 
+#if ENABLE(DATALIST_ELEMENT)
+#include "HTMLDataListElement.h"
+#include "HTMLOptionElement.h"
+#endif
+
 namespace WebCore {
 
 using namespace HTMLNames;
@@ -70,20 +77,25 @@ TextFieldInputType::~TextFieldInputType()
 {
     if (m_innerSpinButton)
         m_innerSpinButton->removeSpinButtonOwner();
+#if ENABLE(DATALIST_ELEMENT)
+    closeSuggestions();
+#endif
 }
 
 bool TextFieldInputType::isKeyboardFocusable(KeyboardEvent*) const
 {
-#if PLATFORM(IOS)
-    if (element().isReadOnly())
+    ASSERT(element());
+#if PLATFORM(IOS_FAMILY)
+    if (element()->isReadOnly())
         return false;
 #endif
-    return element().isTextFormControlFocusable();
+    return element()->isTextFormControlFocusable();
 }
 
 bool TextFieldInputType::isMouseFocusable() const
 {
-    return element().isTextFormControlFocusable();
+    ASSERT(element());
+    return element()->isTextFormControlFocusable();
 }
 
 bool TextFieldInputType::isTextField() const
@@ -93,10 +105,10 @@ bool TextFieldInputType::isTextField() const
 
 bool TextFieldInputType::isEmptyValue() const
 {
-    TextControlInnerTextElement* innerText = innerTextElement();
+    auto innerText = innerTextElement();
     ASSERT(innerText);
 
-    for (Text* text = TextNodeTraversal::firstWithin(*innerText); text; text = TextNodeTraversal::next(*text, innerText)) {
+    for (Text* text = TextNodeTraversal::firstWithin(*innerText); text; text = TextNodeTraversal::next(*text, innerText.get())) {
         if (text->length())
             return false;
     }
@@ -105,14 +117,17 @@ bool TextFieldInputType::isEmptyValue() const
 
 bool TextFieldInputType::valueMissing(const String& value) const
 {
-    return element().isRequired() && value.isEmpty();
+    ASSERT(element());
+    return element()->isRequired() && value.isEmpty();
 }
 
 void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
 {
+    ASSERT(element());
+
     // Grab this input element to keep reference even if JS event handler
     // changes input type.
-    Ref<HTMLInputElement> input(element());
+    Ref<HTMLInputElement> input(*element());
 
     // We don't ask InputType::setValue to dispatch events because
     // TextFieldInputType dispatches events different way from InputType.
@@ -155,109 +170,153 @@ void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChange
         input->setTextAsOfLastFormControlChangeEvent(sanitizedValue);
 }
 
-void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
+#if ENABLE(DATALIST_ELEMENT)
+void TextFieldInputType::handleClickEvent(MouseEvent&)
+{
+    if (element()->focused() && element()->list())
+        displaySuggestions(DataListSuggestionActivationType::ControlClicked);
+}
+#endif
+
+void TextFieldInputType::handleKeydownEvent(KeyboardEvent& event)
 {
-    if (!element().focused())
+    ASSERT(element());
+    if (!element()->focused())
         return;
-    Frame* frame = element().document().frame();
-    if (!frame || !frame->editor().doTextFieldCommandFromEvent(&element(), event))
+#if ENABLE(DATALIST_ELEMENT)
+    const String& key = event.keyIdentifier();
+    if (m_suggestionPicker && (key == "Enter" || key == "Up" || key == "Down")) {
+        m_suggestionPicker->handleKeydownWithIdentifier(key);
+        event.setDefaultHandled();
+    }
+#endif
+    RefPtr<Frame> frame = element()->document().frame();
+    if (!frame || !frame->editor().doTextFieldCommandFromEvent(element(), &event))
         return;
-    event->setDefaultHandled();
+    event.setDefaultHandled();
 }
 
-void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
+void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent& event)
 {
-    if (element().isDisabledOrReadOnly())
+    ASSERT(element());
+    if (element()->isDisabledOrReadOnly())
         return;
-    const String& key = event->keyIdentifier();
+#if ENABLE(DATALIST_ELEMENT)
+    if (m_suggestionPicker)
+        return;
+#endif
+    const String& key = event.keyIdentifier();
     if (key == "Up")
         spinButtonStepUp();
     else if (key == "Down")
         spinButtonStepDown();
     else
         return;
-    event->setDefaultHandled();
+    event.setDefaultHandled();
 }
 
-void TextFieldInputType::forwardEvent(Event* event)
+void TextFieldInputType::forwardEvent(Event& event)
 {
     if (m_innerSpinButton) {
         m_innerSpinButton->forwardEvent(event);
-        if (event->defaultHandled())
+        if (event.defaultHandled())
             return;
     }
 
-    if (event->isMouseEvent()
-        || event->type() == eventNames().blurEvent
-        || event->type() == eventNames().focusEvent)
-    {
-        element().document().updateStyleIfNeeded();
-
-        if (element().renderer()) {
-            RenderTextControlSingleLine& renderTextControl = downcast<RenderTextControlSingleLine>(*element().renderer());
-            if (event->type() == eventNames().blurEvent) {
-                if (RenderTextControlInnerBlock* innerTextRenderer = innerTextElement()->renderer()) {
-                    if (RenderLayer* innerLayer = innerTextRenderer->layer()) {
-                        ScrollOffset scrollOffset(!renderTextControl.style().isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0);
-                        innerLayer->scrollToOffset(scrollOffset, RenderLayer::ScrollOffsetClamped);
-                    }
-                }
-
-                capsLockStateMayHaveChanged();
-            } else if (event->type() == eventNames().focusEvent)
-                capsLockStateMayHaveChanged();
-
-            element().forwardEvent(event);
-        }
+    bool isFocusEvent = event.type() == eventNames().focusEvent;
+    bool isBlurEvent = event.type() == eventNames().blurEvent;
+    if (isFocusEvent || isBlurEvent)
+        capsLockStateMayHaveChanged();
+    if (event.isMouseEvent() || isFocusEvent || isBlurEvent) {
+        ASSERT(element());
+        element()->forwardEvent(event);
     }
 }
 
+void TextFieldInputType::elementDidBlur()
+{
+    ASSERT(element());
+    auto* renderer = element()->renderer();
+    if (!renderer)
+        return;
+
+    auto* innerTextRenderer = innerTextElement()->renderer();
+    if (!innerTextRenderer)
+        return;
+
+    auto* innerLayer = innerTextRenderer->layer();
+    if (!innerLayer)
+        return;
+
+    bool isLeftToRightDirection = downcast<RenderTextControlSingleLine>(*renderer).style().isLeftToRightDirection();
+    ScrollOffset scrollOffset(isLeftToRightDirection ? 0 : innerLayer->scrollWidth(), 0);
+    innerLayer->scrollToOffset(scrollOffset);
+
+#if ENABLE(DATALIST_ELEMENT)
+    closeSuggestions();
+#endif
+}
+
 void TextFieldInputType::handleFocusEvent(Node* oldFocusedNode, FocusDirection)
 {
-    ASSERT_UNUSED(oldFocusedNode, oldFocusedNode != &element());
-    if (Frame* frame = element().document().frame())
-        frame->editor().textFieldDidBeginEditing(&element());
+    ASSERT(element());
+    ASSERT_UNUSED(oldFocusedNode, oldFocusedNode != element());
+    if (RefPtr<Frame> frame = element()->document().frame()) {
+        frame->editor().textFieldDidBeginEditing(element());
+#if ENABLE(DATALIST_ELEMENT) && PLATFORM(IOS_FAMILY)
+        if (element()->list() && m_dataListDropdownIndicator)
+            m_dataListDropdownIndicator->setInlineStyleProperty(CSSPropertyDisplay, suggestions().size() ? CSSValueBlock : CSSValueNone, true);
+#endif
+    }
 }
 
 void TextFieldInputType::handleBlurEvent()
 {
     InputType::handleBlurEvent();
-    element().endEditing();
+    ASSERT(element());
+    element()->endEditing();
+#if ENABLE(DATALIST_ELEMENT) && PLATFORM(IOS_FAMILY)
+    if (element()->list() && m_dataListDropdownIndicator)
+        m_dataListDropdownIndicator->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone, true);
+#endif
 }
 
-bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
+bool TextFieldInputType::shouldSubmitImplicitly(Event& event)
 {
-    return (event->type() == eventNames().textInputEvent && is<TextEvent>(*event) && downcast<TextEvent>(*event).data() == "\n")
+    return (event.type() == eventNames().textInputEvent && is<TextEvent>(event) && downcast<TextEvent>(event).data() == "\n")
         || InputType::shouldSubmitImplicitly(event);
 }
 
-RenderPtr<RenderElement> TextFieldInputType::createInputRenderer(Ref<RenderStyle>&& style)
+RenderPtr<RenderElement> TextFieldInputType::createInputRenderer(RenderStyle&& style)
 {
-    return createRenderer<RenderTextControlSingleLine>(element(), WTFMove(style));
+    ASSERT(element());
+    return createRenderer<RenderTextControlSingleLine>(*element(), WTFMove(style));
 }
 
 bool TextFieldInputType::needsContainer() const
 {
+#if ENABLE(DATALIST_ELEMENT)
+    return element()->hasAttributeWithoutSynchronization(listAttr);
+#endif
     return false;
 }
 
 bool TextFieldInputType::shouldHaveSpinButton() const
 {
-    Document& document = element().document();
-    RefPtr<RenderTheme> theme = document.page() ? &document.page()->theme() : RenderTheme::defaultTheme();
-    return theme->shouldHaveSpinButton(element());
+    ASSERT(element());
+    return RenderTheme::singleton().shouldHaveSpinButton(*element());
 }
 
 bool TextFieldInputType::shouldHaveCapsLockIndicator() const
 {
-    Document& document = element().document();
-    RefPtr<RenderTheme> theme = document.page() ? &document.page()->theme() : RenderTheme::defaultTheme();
-    return theme->shouldHaveCapsLockIndicator(element());
+    ASSERT(element());
+    return RenderTheme::singleton().shouldHaveCapsLockIndicator(*element());
 }
 
 void TextFieldInputType::createShadowSubtree()
 {
-    ASSERT(element().shadowRoot());
+    ASSERT(element());
+    ASSERT(element()->shadowRoot());
 
     ASSERT(!m_innerText);
     ASSERT(!m_innerBlock);
@@ -265,7 +324,7 @@ void TextFieldInputType::createShadowSubtree()
     ASSERT(!m_capsLockIndicator);
     ASSERT(!m_autoFillButton);
 
-    Document& document = element().document();
+    Document& document = element()->document();
     bool shouldHaveSpinButton = this->shouldHaveSpinButton();
     bool shouldHaveCapsLockIndicator = this->shouldHaveCapsLockIndicator();
     bool createsContainer = shouldHaveSpinButton || shouldHaveCapsLockIndicator || needsContainer();
@@ -273,7 +332,7 @@ void TextFieldInputType::createShadowSubtree()
     m_innerText = TextControlInnerTextElement::create(document);
 
     if (!createsContainer) {
-        element().userAgentShadowRoot()->appendChild(*m_innerText, IGNORE_EXCEPTION);
+        element()->userAgentShadowRoot()->appendChild(*m_innerText);
         updatePlaceholderText();
         return;
     }
@@ -283,7 +342,7 @@ void TextFieldInputType::createShadowSubtree()
 
     if (shouldHaveSpinButton) {
         m_innerSpinButton = SpinButtonElement::create(document, *this);
-        m_container->appendChild(*m_innerSpinButton, IGNORE_EXCEPTION);
+        m_container->appendChild(*m_innerSpinButton);
     }
 
     if (shouldHaveCapsLockIndicator) {
@@ -293,10 +352,16 @@ void TextFieldInputType::createShadowSubtree()
         bool shouldDrawCapsLockIndicator = this->shouldDrawCapsLockIndicator();
         m_capsLockIndicator->setInlineStyleProperty(CSSPropertyDisplay, shouldDrawCapsLockIndicator ? CSSValueBlock : CSSValueNone, true);
 
-        m_container->appendChild(*m_capsLockIndicator, IGNORE_EXCEPTION);
+        m_container->appendChild(*m_capsLockIndicator);
     }
 
     updateAutoFillButton();
+
+#if ENABLE(DATALIST_ELEMENT)
+    m_dataListDropdownIndicator = DataListButtonElement::create(element()->document(), *this);
+    m_dataListDropdownIndicator->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone, true);
+    m_container->appendChild(*m_dataListDropdownIndicator);
+#endif
 }
 
 HTMLElement* TextFieldInputType::containerElement() const
@@ -309,10 +374,10 @@ HTMLElement* TextFieldInputType::innerBlockElement() const
     return m_innerBlock.get();
 }
 
-TextControlInnerTextElement* TextFieldInputType::innerTextElement() const
+RefPtr<TextControlInnerTextElement> TextFieldInputType::innerTextElement() const
 {
     ASSERT(m_innerText);
-    return m_innerText.get();
+    return m_innerText;
 }
 
 HTMLElement* TextFieldInputType::innerSpinButtonElement() const
@@ -346,17 +411,22 @@ void TextFieldInputType::destroyShadowSubtree()
     m_innerSpinButton = nullptr;
     m_capsLockIndicator = nullptr;
     m_autoFillButton = nullptr;
+#if ENABLE(DATALIST)
+    m_dataListDropdownIndicator = nullptr;
+#endif
     m_container = nullptr;
 }
 
-void TextFieldInputType::attributeChanged()
+void TextFieldInputType::attributeChanged(const QualifiedName& name)
 {
-    // FIXME: Updating the inner text on any attribute update should
-    // be unnecessary. We should figure out what attributes affect.
-    updateInnerTextValue();
+    if (name == valueAttr || name == placeholderAttr) {
+        if (element())
+            updateInnerTextValue();
+    }
+    InputType::attributeChanged(name);
 }
 
-void TextFieldInputType::disabledAttributeChanged()
+void TextFieldInputType::disabledStateChanged()
 {
     if (m_innerSpinButton)
         m_innerSpinButton->releaseCapture();
@@ -364,7 +434,7 @@ void TextFieldInputType::disabledAttributeChanged()
     updateAutoFillButton();
 }
 
-void TextFieldInputType::readonlyAttributeChanged()
+void TextFieldInputType::readOnlyStateChanged()
 {
     if (m_innerSpinButton)
         m_innerSpinButton->releaseCapture();
@@ -382,80 +452,107 @@ bool TextFieldInputType::shouldUseInputMethod() const
     return true;
 }
 
-static bool isASCIILineBreak(UChar c)
-{
-    return c == '\r' || c == '\n';
+// FIXME: The name of this function doesn't make clear the two jobs it does:
+// 1) Limits the string to a particular number of grapheme clusters.
+// 2) Truncates the string at the first character which is a control character other than tab.
+// FIXME: TextFieldInputType::sanitizeValue doesn't need a limit on grapheme clusters. A limit on code units would do.
+// FIXME: Where does the "truncate at first control character" rule come from?
+static String limitLength(const String& string, unsigned maxNumGraphemeClusters)
+{
+    StringView stringView { string };
+    unsigned firstNonTabControlCharacterIndex = stringView.find([] (UChar character) {
+        return character < ' ' && character != '\t';
+    });
+    unsigned limitedLength;
+    if (stringView.is8Bit())
+        limitedLength = std::min(firstNonTabControlCharacterIndex, maxNumGraphemeClusters);
+    else
+        limitedLength = numCodeUnitsInGraphemeClusters(stringView.substring(0, firstNonTabControlCharacterIndex), maxNumGraphemeClusters);
+    return string.left(limitedLength);
 }
 
-static String limitLength(const String& string, int maxLength)
+static String autoFillButtonTypeToAccessibilityLabel(AutoFillButtonType autoFillButtonType)
 {
-    unsigned newLength = numCharactersInGraphemeClusters(string, maxLength);
-    for (unsigned i = 0; i < newLength; ++i) {
-        const UChar current = string[i];
-        if (current < ' ' && current != '\t') {
-            newLength = i;
-            break;
-        }
+    switch (autoFillButtonType) {
+    case AutoFillButtonType::Contacts:
+        return AXAutoFillContactsLabel();
+    case AutoFillButtonType::Credentials:
+        return AXAutoFillCredentialsLabel();
+    case AutoFillButtonType::StrongPassword:
+        return AXAutoFillStrongPasswordLabel();
+    case AutoFillButtonType::CreditCard:
+        return AXAutoFillCreditCardLabel();
+    case AutoFillButtonType::None:
+        ASSERT_NOT_REACHED();
+        return { };
     }
-    return string.left(newLength);
+    ASSERT_NOT_REACHED();
+    return { };
 }
 
-static String autoFillButtonTypeToAccessibilityLabel(AutoFillButtonType autoFillButtonType)
+static String autoFillButtonTypeToAutoFillButtonText(AutoFillButtonType autoFillButtonType)
 {
     switch (autoFillButtonType) {
     case AutoFillButtonType::Contacts:
-        return AXAutoFillContactsLabel();
     case AutoFillButtonType::Credentials:
-        return AXAutoFillCredentialsLabel();
-    default:
+    case AutoFillButtonType::CreditCard:
+        return emptyString();
+    case AutoFillButtonType::StrongPassword:
+        return autoFillStrongPasswordLabel();
     case AutoFillButtonType::None:
         ASSERT_NOT_REACHED();
-        return AtomicString();
+        return { };
     }
+    ASSERT_NOT_REACHED();
+    return { };
 }
-    
+
 static AtomicString autoFillButtonTypeToAutoFillButtonPseudoClassName(AutoFillButtonType autoFillButtonType)
 {
-    AtomicString pseudoClassName;
     switch (autoFillButtonType) {
     case AutoFillButtonType::Contacts:
-        pseudoClassName = AtomicString("-webkit-contacts-auto-fill-button", AtomicString::ConstructFromLiteral);
-        break;
+        return { "-webkit-contacts-auto-fill-button", AtomicString::ConstructFromLiteral };
     case AutoFillButtonType::Credentials:
-        pseudoClassName = AtomicString("-webkit-credentials-auto-fill-button", AtomicString::ConstructFromLiteral);
-        break;
+        return { "-webkit-credentials-auto-fill-button", AtomicString::ConstructFromLiteral };
+    case AutoFillButtonType::StrongPassword:
+        return { "-webkit-strong-password-auto-fill-button", AtomicString::ConstructFromLiteral };
+    case AutoFillButtonType::CreditCard:
+        return { "-webkit-credit-card-auto-fill-button", AtomicString::ConstructFromLiteral };
     case AutoFillButtonType::None:
         ASSERT_NOT_REACHED();
-        break;
+        return emptyAtom();
     }
-
-    return pseudoClassName;
+    ASSERT_NOT_REACHED();
+    return { };
 }
 
 static bool isAutoFillButtonTypeChanged(const AtomicString& attribute, AutoFillButtonType autoFillButtonType)
 {
     if (attribute == "-webkit-contacts-auto-fill-button" && autoFillButtonType != AutoFillButtonType::Contacts)
         return true;
-
     if (attribute == "-webkit-credentials-auto-fill-button" && autoFillButtonType != AutoFillButtonType::Credentials)
         return true;
-
+    if (attribute == "-webkit-strong-password-auto-fill-button" && autoFillButtonType != AutoFillButtonType::StrongPassword)
+        return true;
+    if (attribute == "-webkit-credit-card-auto-fill-button" && autoFillButtonType != AutoFillButtonType::CreditCard)
+        return true;
     return false;
 }
 
 String TextFieldInputType::sanitizeValue(const String& proposedValue) const
 {
-    return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maxEffectiveLength);
+    return limitLength(proposedValue.removeCharacters(isHTMLLineBreak), HTMLInputElement::maxEffectiveLength);
 }
 
-void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
+void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent& event)
 {
+    ASSERT(element());
     // Make sure that the text to be inserted will not violate the maxLength.
 
     // We use RenderTextControlSingleLine::text() instead of InputElement::value()
     // because they can be mismatched by sanitizeValue() in
     // HTMLInputElement::subtreeHasChanged() in some cases.
-    String innerText = element().innerTextValue();
+    String innerText = element()->innerTextValue();
     unsigned oldLength = numGraphemeClusters(innerText);
 
     // selectionLength represents the selection length of this text field to be
@@ -464,63 +561,68 @@ void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*
     // selection length. The selection is the source of text drag-and-drop in
     // that case, and nothing in the text field will be removed.
     unsigned selectionLength = 0;
-    if (element().focused()) {
-        ASSERT(enclosingTextFormControl(element().document().frame()->selection().selection().start()) == &element());
-        int selectionStart = element().selectionStart();
-        ASSERT(selectionStart <= element().selectionEnd());
-        int selectionCodeUnitCount = element().selectionEnd() - selectionStart;
-        selectionLength = selectionCodeUnitCount ? numGraphemeClusters(innerText.substring(selectionStart, selectionCodeUnitCount)) : 0;
+    if (element()->focused()) {
+        ASSERT(enclosingTextFormControl(element()->document().frame()->selection().selection().start()) == element());
+        int selectionStart = element()->selectionStart();
+        ASSERT(selectionStart <= element()->selectionEnd());
+        int selectionCodeUnitCount = element()->selectionEnd() - selectionStart;
+        selectionLength = selectionCodeUnitCount ? numGraphemeClusters(StringView(innerText).substring(selectionStart, selectionCodeUnitCount)) : 0;
     }
     ASSERT(oldLength >= selectionLength);
 
     // Selected characters will be removed by the next text event.
     unsigned baseLength = oldLength - selectionLength;
-    unsigned maxLength = isTextType() ? element().effectiveMaxLength() : HTMLInputElement::maxEffectiveLength;
+    unsigned maxLength = isTextType() ? element()->effectiveMaxLength() : HTMLInputElement::maxEffectiveLength;
     unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
 
     // Truncate the inserted text to avoid violating the maxLength and other constraints.
-    String eventText = event->text();
+    String eventText = event.text();
     unsigned textLength = eventText.length();
-    while (textLength > 0 && isASCIILineBreak(eventText[textLength - 1]))
+    while (textLength > 0 && isHTMLLineBreak(eventText[textLength - 1]))
         textLength--;
     eventText.truncate(textLength);
     eventText.replace("\r\n", " ");
     eventText.replace('\r', ' ');
     eventText.replace('\n', ' ');
-
-    event->setText(limitLength(eventText, appendableLength));
+    event.setText(limitLength(eventText, appendableLength));
 }
 
 bool TextFieldInputType::shouldRespectListAttribute()
 {
+#if ENABLE(DATALIST_ELEMENT)
+    return RuntimeEnabledFeatures::sharedFeatures().dataListElementEnabled();
+#else
     return InputType::themeSupportsDataListUI(this);
+#endif
 }
 
 void TextFieldInputType::updatePlaceholderText()
 {
     if (!supportsPlaceholder())
         return;
-    String placeholderText = element().strippedPlaceholder();
+    ASSERT(element());
+    String placeholderText = element()->strippedPlaceholder();
     if (placeholderText.isEmpty()) {
         if (m_placeholder) {
-            m_placeholder->parentNode()->removeChild(*m_placeholder, ASSERT_NO_EXCEPTION);
+            m_placeholder->parentNode()->removeChild(*m_placeholder);
             m_placeholder = nullptr;
         }
         return;
     }
     if (!m_placeholder) {
-        m_placeholder = TextControlPlaceholderElement::create(element().document());
-        element().userAgentShadowRoot()->insertBefore(*m_placeholder, m_container ? m_container.get() : innerTextElement(), ASSERT_NO_EXCEPTION);        
+        m_placeholder = TextControlPlaceholderElement::create(element()->document());
+        element()->userAgentShadowRoot()->insertBefore(*m_placeholder, m_container ? m_container.get() : innerTextElement().get());
     }
-    m_placeholder->setInnerText(placeholderText, ASSERT_NO_EXCEPTION);
+    m_placeholder->setInnerText(placeholderText);
 }
 
-bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
+bool TextFieldInputType::appendFormData(DOMFormData& formData, bool multipart) const
 {
-    InputType::appendFormData(list, multipart);
-    const AtomicString& dirnameAttrValue = element().fastGetAttribute(dirnameAttr);
+    InputType::appendFormData(formData, multipart);
+    ASSERT(element());
+    auto& dirnameAttrValue = element()->attributeWithoutSynchronization(dirnameAttr);
     if (!dirnameAttrValue.isNull())
-        list.appendData(dirnameAttrValue, element().directionForFormData());
+        formData.append(dirnameAttrValue, element()->directionForFormData());
     return true;
 }
 
@@ -531,7 +633,8 @@ String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) c
 
 void TextFieldInputType::subtreeHasChanged()
 {
-    element().setChangedSinceLastFormControlChangeEvent(true);
+    ASSERT(element());
+    element()->setChangedSinceLastFormControlChangeEvent(true);
 
     // We don't need to call sanitizeUserInputValue() function here because
     // HTMLInputElement::handleBeforeTextInsertedEvent() has already called
@@ -543,23 +646,32 @@ void TextFieldInputType::subtreeHasChanged()
     // user input in order to retain parity between what's in the model and
     // what's on the screen. Otherwise, we retain the sanitization process for
     // backward compatibility. https://bugs.webkit.org/show_bug.cgi?id=150346
-    String innerText = convertFromVisibleValue(element().innerTextValue());
+    String innerText = convertFromVisibleValue(element()->innerTextValue());
     if (!supportsSelectionAPI())
         innerText = sanitizeValue(innerText);
-    element().setValueFromRenderer(innerText);
-    element().updatePlaceholderVisibility();
+    element()->setValueFromRenderer(innerText);
+    element()->updatePlaceholderVisibility();
     // Recalc for :invalid change.
-    element().setNeedsStyleRecalc();
+    element()->invalidateStyleForSubtree();
 
     didSetValueByUserEdit();
 }
 
 void TextFieldInputType::didSetValueByUserEdit()
 {
-    if (!element().focused())
+    ASSERT(element());
+    if (!element()->focused())
         return;
-    if (Frame* frame = element().document().frame())
-        frame->editor().textDidChangeInTextField(&element());
+    if (RefPtr<Frame> frame = element()->document().frame())
+        frame->editor().textDidChangeInTextField(element());
+#if ENABLE(DATALIST_ELEMENT)
+#if PLATFORM(IOS_FAMILY)
+    if (element()->list() && m_dataListDropdownIndicator)
+        m_dataListDropdownIndicator->setInlineStyleProperty(CSSPropertyDisplay, suggestions().size() ? CSSValueBlock : CSSValueNone, true);
+#endif
+    if (element()->list())
+        displaySuggestions(DataListSuggestionActivationType::TextChanged);
+#endif
 }
 
 void TextFieldInputType::spinButtonStepDown()
@@ -574,40 +686,48 @@ void TextFieldInputType::spinButtonStepUp()
 
 void TextFieldInputType::updateInnerTextValue()
 {
-    if (!element().formControlValueMatchesRenderer()) {
+    ASSERT(element());
+    if (!element()->formControlValueMatchesRenderer()) {
         // Update the renderer value if the formControlValueMatchesRenderer() flag is false.
         // It protects an unacceptable renderer value from being overwritten with the DOM value.
-        element().setInnerTextValue(visibleValue());
-        element().updatePlaceholderVisibility();
+        element()->setInnerTextValue(visibleValue());
+        element()->updatePlaceholderVisibility();
     }
 }
 
 void TextFieldInputType::focusAndSelectSpinButtonOwner()
 {
-    Ref<HTMLInputElement> input(element());
+    ASSERT(element());
+    Ref<HTMLInputElement> input(*element());
     input->focus();
     input->select();
 }
 
 bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents()
 {
-    return !element().isDisabledOrReadOnly();
+    ASSERT(element());
+    return !element()->isDisabledOrReadOnly();
 }
 
 bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents()
 {
-    return shouldSpinButtonRespondToMouseEvents() && element().focused();
+    ASSERT(element());
+    return shouldSpinButtonRespondToMouseEvents() && element()->focused();
 }
 
 bool TextFieldInputType::shouldDrawCapsLockIndicator() const
 {
-    if (element().document().focusedElement() != &element())
+    ASSERT(element());
+    if (element()->document().focusedElement() != element())
+        return false;
+
+    if (element()->isDisabledOrReadOnly())
         return false;
 
-    if (element().isDisabledOrReadOnly())
+    if (element()->hasAutoFillStrongPasswordButton())
         return false;
 
-    Frame* frame = element().document().frame();
+    RefPtr<Frame> frame = element()->document().frame();
     if (!frame)
         return false;
 
@@ -628,30 +748,33 @@ void TextFieldInputType::capsLockStateMayHaveChanged()
 
 bool TextFieldInputType::shouldDrawAutoFillButton() const
 {
-    return !element().isDisabledOrReadOnly() && element().autoFillButtonType() != AutoFillButtonType::None;
+    ASSERT(element());
+    return !element()->isDisabledOrReadOnly() && element()->autoFillButtonType() != AutoFillButtonType::None;
 }
 
 void TextFieldInputType::autoFillButtonElementWasClicked()
 {
-    Page* page = element().document().page();
+    ASSERT(element());
+    Page* page = element()->document().page();
     if (!page)
         return;
 
-    page->chrome().client().handleAutoFillButtonClick(element());
+    page->chrome().client().handleAutoFillButtonClick(*element());
 }
 
 void TextFieldInputType::createContainer()
 {
     ASSERT(!m_container);
+    ASSERT(element());
 
-    m_container = TextControlInnerContainer::create(element().document());
+    m_container = TextControlInnerContainer::create(element()->document());
     m_container->setPseudo(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral));
 
-    m_innerBlock = TextControlInnerElement::create(element().document());
-    m_innerBlock->appendChild(*m_innerText, IGNORE_EXCEPTION);
-    m_container->appendChild(*m_innerBlock, IGNORE_EXCEPTION);
+    m_innerBlock = TextControlInnerElement::create(element()->document());
+    m_innerBlock->appendChild(*m_innerText);
+    m_container->appendChild(*m_innerBlock);
 
-    element().userAgentShadowRoot()->appendChild(*m_container, IGNORE_EXCEPTION);
+    element()->userAgentShadowRoot()->appendChild(*m_container);
 }
 
 void TextFieldInputType::createAutoFillButton(AutoFillButtonType autoFillButtonType)
@@ -661,27 +784,34 @@ void TextFieldInputType::createAutoFillButton(AutoFillButtonType autoFillButtonT
     if (autoFillButtonType == AutoFillButtonType::None)
         return;
 
-    m_autoFillButton = AutoFillButtonElement::create(element().document(), *this);
+    ASSERT(element());
+    m_autoFillButton = AutoFillButtonElement::create(element()->document(), *this);
     m_autoFillButton->setPseudo(autoFillButtonTypeToAutoFillButtonPseudoClassName(autoFillButtonType));
-    m_autoFillButton->setAttribute(roleAttr, "button");
-    m_autoFillButton->setAttribute(aria_labelAttr, autoFillButtonTypeToAccessibilityLabel(autoFillButtonType));
-    m_container->appendChild(*m_autoFillButton, IGNORE_EXCEPTION);
+    m_autoFillButton->setAttributeWithoutSynchronization(roleAttr, AtomicString("button", AtomicString::ConstructFromLiteral));
+    m_autoFillButton->setAttributeWithoutSynchronization(aria_labelAttr, autoFillButtonTypeToAccessibilityLabel(autoFillButtonType));
+    m_autoFillButton->setTextContent(autoFillButtonTypeToAutoFillButtonText(autoFillButtonType));
+    m_container->appendChild(*m_autoFillButton);
 }
 
 void TextFieldInputType::updateAutoFillButton()
 {
+    capsLockStateMayHaveChanged();
+
     if (shouldDrawAutoFillButton()) {
         if (!m_container)
             createContainer();
 
+        ASSERT(element());
+        AutoFillButtonType autoFillButtonType = element()->autoFillButtonType();
         if (!m_autoFillButton)
-            createAutoFillButton(element().autoFillButtonType());
+            createAutoFillButton(autoFillButtonType);
 
-        const AtomicString& attribute = m_autoFillButton->fastGetAttribute(pseudoAttr);
-        bool shouldUpdateAutoFillButtonType = isAutoFillButtonTypeChanged(attribute, element().autoFillButtonType());
+        const AtomicString& attribute = m_autoFillButton->attributeWithoutSynchronization(pseudoAttr);
+        bool shouldUpdateAutoFillButtonType = isAutoFillButtonTypeChanged(attribute, autoFillButtonType);
         if (shouldUpdateAutoFillButtonType) {
-            m_autoFillButton->setPseudo(autoFillButtonTypeToAutoFillButtonPseudoClassName(element().autoFillButtonType()));
-            m_autoFillButton->setAttribute(aria_labelAttr, autoFillButtonTypeToAccessibilityLabel(element().autoFillButtonType()));
+            m_autoFillButton->setPseudo(autoFillButtonTypeToAutoFillButtonPseudoClassName(autoFillButtonType));
+            m_autoFillButton->setAttributeWithoutSynchronization(aria_labelAttr, autoFillButtonTypeToAccessibilityLabel(autoFillButtonType));
+            m_autoFillButton->setTextContent(autoFillButtonTypeToAutoFillButtonText(autoFillButtonType));
         }
         m_autoFillButton->setInlineStyleProperty(CSSPropertyDisplay, CSSValueBlock, true);
         return;
@@ -691,4 +821,111 @@ void TextFieldInputType::updateAutoFillButton()
         m_autoFillButton->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone, true);        
 }
 
+#if ENABLE(DATALIST_ELEMENT)
+
+void TextFieldInputType::listAttributeTargetChanged()
+{
+    m_cachedSuggestions = std::make_pair(String(), Vector<String>());
+
+    if (!m_dataListDropdownIndicator)
+        return;
+
+#if !PLATFORM(IOS_FAMILY)
+    m_dataListDropdownIndicator->setInlineStyleProperty(CSSPropertyDisplay, element()->list() ? CSSValueBlock : CSSValueNone, true);
+#endif
+}
+
+HTMLElement* TextFieldInputType::dataListButtonElement() const
+{
+    return m_dataListDropdownIndicator.get();
+}
+
+void TextFieldInputType::dataListButtonElementWasClicked()
+{
+    if (element()->list())
+        displaySuggestions(DataListSuggestionActivationType::IndicatorClicked);
+}
+
+IntRect TextFieldInputType::elementRectInRootViewCoordinates() const
+{
+    if (!element()->renderer())
+        return IntRect();
+    return element()->document().view()->contentsToRootView(element()->renderer()->absoluteBoundingBoxRect());
+}
+
+Vector<String> TextFieldInputType::suggestions()
+{
+    Vector<String> suggestions;
+    Vector<String> matchesContainingValue;
+
+    String elementValue = element()->value();
+
+    if (!m_cachedSuggestions.first.isNull() && equalIgnoringASCIICase(m_cachedSuggestions.first, elementValue))
+        return m_cachedSuggestions.second;
+
+    if (auto dataList = element()->dataList()) {
+        Ref<HTMLCollection> options = dataList->options();
+        for (unsigned i = 0; auto* option = downcast<HTMLOptionElement>(options->item(i)); ++i) {
+            if (!element()->isValidValue(option->value()))
+                continue;
+
+            String value = sanitizeValue(option->value());
+            if (elementValue.isEmpty())
+                suggestions.append(value);
+            else if (value.startsWithIgnoringASCIICase(elementValue))
+                suggestions.append(value);
+            else if (value.containsIgnoringASCIICase(elementValue))
+                matchesContainingValue.append(value);
+        }
+    }
+
+    suggestions.appendVector(matchesContainingValue);
+    m_cachedSuggestions = std::make_pair(elementValue, suggestions);
+
+    return suggestions;
+}
+
+void TextFieldInputType::didSelectDataListOption(const String& selectedOption)
+{
+    element()->setValue(selectedOption, DispatchInputAndChangeEvent);
+}
+
+void TextFieldInputType::didCloseSuggestions()
+{
+    m_cachedSuggestions = std::make_pair(String(), Vector<String>());
+    m_suggestionPicker = nullptr;
+    if (element()->renderer())
+        element()->renderer()->repaint();
+}
+
+void TextFieldInputType::displaySuggestions(DataListSuggestionActivationType type)
+{
+    if (element()->isDisabledFormControl() || !element()->renderer())
+        return;
+
+    if (!UserGestureIndicator::processingUserGesture() && type != DataListSuggestionActivationType::TextChanged)
+        return;
+
+    if (!m_suggestionPicker && suggestions().size() > 0)
+        m_suggestionPicker = chrome()->createDataListSuggestionPicker(*this);
+
+    if (!m_suggestionPicker)
+        return;
+
+    m_suggestionPicker->displayWithActivationType(type);
+}
+
+void TextFieldInputType::closeSuggestions()
+{
+    if (m_suggestionPicker)
+        m_suggestionPicker->close();
+}
+
+bool TextFieldInputType::isPresentingAttachedView() const
+{
+    return !!m_suggestionPicker;
+}
+
+#endif
+
 } // namespace WebCore