summaryrefslogtreecommitdiff
path: root/Source/WebCore/html/HTMLInputElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/html/HTMLInputElement.cpp')
-rw-r--r--Source/WebCore/html/HTMLInputElement.cpp962
1 files changed, 534 insertions, 428 deletions
diff --git a/Source/WebCore/html/HTMLInputElement.cpp b/Source/WebCore/html/HTMLInputElement.cpp
index ed1c20ad5..ec557d0b1 100644
--- a/Source/WebCore/html/HTMLInputElement.cpp
+++ b/Source/WebCore/html/HTMLInputElement.cpp
@@ -2,7 +2,7 @@
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
* (C) 2006 Alexey Proskuryakov (ap@nypop.com)
* Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
* Copyright (C) 2010 Google Inc. All rights reserved.
@@ -49,7 +49,6 @@
#include "HTMLOptionElement.h"
#include "HTMLParserIdioms.h"
#include "IdTargetObserver.h"
-#include "InsertionPoint.h"
#include "KeyboardEvent.h"
#include "Language.h"
#include "LocalizedStrings.h"
@@ -57,22 +56,13 @@
#include "PlatformMouseEvent.h"
#include "RenderTextControlSingleLine.h"
#include "RenderTheme.h"
-#include "RuntimeEnabledFeatures.h"
#include "ScopedEventQueue.h"
#include "SearchInputType.h"
+#include "Settings.h"
#include "StyleResolver.h"
-#include "TextBreakIterator.h"
#include <wtf/MathExtras.h>
#include <wtf/Ref.h>
-#if ENABLE(INPUT_TYPE_COLOR)
-#include "ColorInputType.h"
-#endif
-
-#if ENABLE(INPUT_SPEECH)
-#include "RuntimeEnabledFeatures.h"
-#endif
-
#if ENABLE(TOUCH_EVENTS)
#include "TouchEvent.h"
#endif
@@ -85,12 +75,11 @@ using namespace HTMLNames;
class ListAttributeTargetObserver : IdTargetObserver {
WTF_MAKE_FAST_ALLOCATED;
public:
- static OwnPtr<ListAttributeTargetObserver> create(const AtomicString& id, HTMLInputElement*);
- virtual void idTargetChanged() override;
-
-private:
ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement*);
+ void idTargetChanged() override;
+
+private:
HTMLInputElement* m_element;
};
#endif
@@ -99,14 +88,13 @@ private:
// large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things
// get rather sluggish when a text field has a larger number of characters than
// this, even when just clicking in the text field.
-const int HTMLInputElement::maximumLength = 524288;
+const unsigned HTMLInputElement::maxEffectiveLength = 524288;
const int defaultSize = 20;
const int maxSavedResults = 256;
HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser)
: HTMLTextFormControlElement(tagName, document, form)
, m_size(defaultSize)
- , m_maxLength(maximumLength)
, m_maxResults(-1)
, m_isChecked(false)
, m_reflectsCheckedAttribute(true)
@@ -114,7 +102,8 @@ HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document& docum
, m_hasType(false)
, m_isActivatedSubmit(false)
, m_autocomplete(Uninitialized)
- , m_isAutofilled(false)
+ , m_isAutoFilled(false)
+ , m_autoFillButtonType(static_cast<uint8_t>(AutoFillButtonType::None))
#if ENABLE(DATALIST_ELEMENT)
, m_hasNonEmptyList(false)
#endif
@@ -126,35 +115,41 @@ HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document& docum
#if ENABLE(TOUCH_EVENTS)
, m_hasTouchEventHandler(false)
#endif
- , m_inputType(InputType::createText(*this))
+ , m_isSpellcheckDisabledExceptTextReplacement(false)
+ // m_inputType is lazily created when constructed by the parser to avoid constructing unnecessarily a text inputType and
+ // its shadow subtree, just to destroy them when the |type| attribute gets set by the parser to something else than 'text'.
+ , m_inputType(createdByParser ? nullptr : InputType::createText(*this))
{
- ASSERT(hasTagName(inputTag) || hasTagName(isindexTag));
+ ASSERT(hasTagName(inputTag));
setHasCustomStyleResolveCallbacks();
}
-PassRefPtr<HTMLInputElement> HTMLInputElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser)
+Ref<HTMLInputElement> HTMLInputElement::create(const QualifiedName& tagName, Document& document, HTMLFormElement* form, bool createdByParser)
{
- RefPtr<HTMLInputElement> inputElement = adoptRef(new HTMLInputElement(tagName, document, form, createdByParser));
- inputElement->ensureUserAgentShadowRoot();
- return inputElement.release();
+ bool shouldCreateShadowRootLazily = createdByParser;
+ Ref<HTMLInputElement> inputElement = adoptRef(*new HTMLInputElement(tagName, document, form, createdByParser));
+ if (!shouldCreateShadowRootLazily)
+ inputElement->ensureUserAgentShadowRoot();
+ return inputElement;
}
-HTMLImageLoader* HTMLInputElement::imageLoader()
+HTMLImageLoader& HTMLInputElement::ensureImageLoader()
{
if (!m_imageLoader)
- m_imageLoader = adoptPtr(new HTMLImageLoader(this));
- return m_imageLoader.get();
+ m_imageLoader = std::make_unique<HTMLImageLoader>(*this);
+ return *m_imageLoader;
}
void HTMLInputElement::didAddUserAgentShadowRoot(ShadowRoot*)
{
m_inputType->createShadowSubtree();
+ updateInnerTextElementEditability();
}
HTMLInputElement::~HTMLInputElement()
{
if (needsSuspensionCallback())
- document().unregisterForPageCacheSuspensionCallbacks(this);
+ document().unregisterForDocumentSuspensionCallbacks(this);
// Need to remove form association while this is still an HTMLInputElement
// so that virtual functions are called correctly.
@@ -162,10 +157,10 @@ HTMLInputElement::~HTMLInputElement()
// setForm(0) may register this to a document-level radio button group.
// We should unregister it to avoid accessing a deleted object.
if (isRadioButton())
- document().formController().checkedRadioButtons().removeButton(this);
+ document().formController().radioButtonGroups().removeButton(this);
#if ENABLE(TOUCH_EVENTS)
if (m_hasTouchEventHandler)
- document().didRemoveEventTargetNode(this);
+ document().didRemoveEventTargetNode(*this);
#endif
}
@@ -199,6 +194,16 @@ HTMLElement* HTMLInputElement::innerSpinButtonElement() const
return m_inputType->innerSpinButtonElement();
}
+HTMLElement* HTMLInputElement::capsLockIndicatorElement() const
+{
+ return m_inputType->capsLockIndicatorElement();
+}
+
+HTMLElement* HTMLInputElement::autoFillButtonElement() const
+{
+ return m_inputType->autoFillButtonElement();
+}
+
HTMLElement* HTMLInputElement::resultsButtonElement() const
{
return m_inputType->resultsButtonElement();
@@ -209,13 +214,6 @@ HTMLElement* HTMLInputElement::cancelButtonElement() const
return m_inputType->cancelButtonElement();
}
-#if ENABLE(INPUT_SPEECH)
-HTMLElement* HTMLInputElement::speechButtonElement() const
-{
- return m_inputType->speechButtonElement();
-}
-#endif
-
HTMLElement* HTMLInputElement::sliderThumbElement() const
{
return m_inputType->sliderThumbElement();
@@ -248,11 +246,17 @@ bool HTMLInputElement::isValidValue(const String& value) const
&& !m_inputType->stepMismatch(value)
&& !m_inputType->rangeUnderflow(value)
&& !m_inputType->rangeOverflow(value)
+ && !tooShort(value, IgnoreDirtyFlag)
&& !tooLong(value, IgnoreDirtyFlag)
&& !m_inputType->patternMismatch(value)
&& !m_inputType->valueMissing(value);
}
+bool HTMLInputElement::tooShort() const
+{
+ return willValidate() && tooShort(value(), CheckDirtyFlag);
+}
+
bool HTMLInputElement::tooLong() const
{
return willValidate() && tooLong(value(), CheckDirtyFlag);
@@ -278,22 +282,43 @@ bool HTMLInputElement::patternMismatch() const
return willValidate() && m_inputType->patternMismatch(value());
}
-bool HTMLInputElement::tooLong(const String& value, NeedsToCheckDirtyFlag check) const
+bool HTMLInputElement::tooShort(StringView value, NeedsToCheckDirtyFlag check) const
{
- // We use isTextType() instead of supportsMaxLength() because of the
- // 'virtual' overhead.
- if (!isTextType())
+ if (!supportsMinLength())
return false;
- int max = maxLength();
- if (max < 0)
+
+ int min = minLength();
+ if (min <= 0)
return false;
+
+ if (check == CheckDirtyFlag) {
+ // Return false for the default value or a value set by a script even if
+ // it is shorter than minLength.
+ if (!hasDirtyValue() || !m_wasModifiedByUser)
+ return false;
+ }
+
+ // The empty string is excluded from tooShort validation.
+ if (value.isEmpty())
+ return false;
+
+ // FIXME: The HTML specification says that the "number of characters" is measured using code-unit length.
+ return numGraphemeClusters(value) < static_cast<unsigned>(min);
+}
+
+bool HTMLInputElement::tooLong(StringView value, NeedsToCheckDirtyFlag check) const
+{
+ if (!supportsMaxLength())
+ return false;
+ unsigned max = effectiveMaxLength();
if (check == CheckDirtyFlag) {
// Return false for the default value or a value set by a script even if
// it is longer than maxLength.
if (!hasDirtyValue() || !m_wasModifiedByUser)
return false;
}
- return numGraphemeClusters(value) > static_cast<unsigned>(max);
+ // FIXME: The HTML specification says that the "number of characters" is measured using code-unit length.
+ return numGraphemeClusters(value) > max;
}
bool HTMLInputElement::rangeUnderflow() const
@@ -332,6 +357,18 @@ bool HTMLInputElement::stepMismatch() const
return willValidate() && m_inputType->stepMismatch(value());
}
+bool HTMLInputElement::isValid() const
+{
+ if (!willValidate())
+ return true;
+
+ String value = this->value();
+ bool someError = m_inputType->typeMismatch() || m_inputType->stepMismatch(value) || m_inputType->rangeUnderflow(value) || m_inputType->rangeOverflow(value)
+ || tooShort(value, CheckDirtyFlag) || tooLong(value, CheckDirtyFlag) || m_inputType->patternMismatch(value) || m_inputType->valueMissing(value)
+ || m_inputType->hasBadInput() || customError();
+ return !someError;
+}
+
bool HTMLInputElement::getAllowedValueStep(Decimal* step) const
{
return m_inputType->getAllowedValueStep(step);
@@ -343,20 +380,20 @@ StepRange HTMLInputElement::createStepRange(AnyStepHandling anyStepHandling) con
}
#if ENABLE(DATALIST_ELEMENT)
-Decimal HTMLInputElement::findClosestTickMarkValue(const Decimal& value)
+std::optional<Decimal> HTMLInputElement::findClosestTickMarkValue(const Decimal& value)
{
return m_inputType->findClosestTickMarkValue(value);
}
#endif
-void HTMLInputElement::stepUp(int n, ExceptionCode& ec)
+ExceptionOr<void> HTMLInputElement::stepUp(int n)
{
- m_inputType->stepUp(n, ec);
+ return m_inputType->stepUp(n);
}
-void HTMLInputElement::stepDown(int n, ExceptionCode& ec)
+ExceptionOr<void> HTMLInputElement::stepDown(int n)
{
- m_inputType->stepUp(-n, ec);
+ return m_inputType->stepUp(-n);
}
void HTMLInputElement::blur()
@@ -374,7 +411,7 @@ bool HTMLInputElement::hasCustomFocusLogic() const
return m_inputType->hasCustomFocusLogic();
}
-bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const
+bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent& event) const
{
return m_inputType->isKeyboardFocusable(event);
}
@@ -389,7 +426,7 @@ bool HTMLInputElement::isTextFormControlFocusable() const
return HTMLTextFormControlElement::isFocusable();
}
-bool HTMLInputElement::isTextFormControlKeyboardFocusable(KeyboardEvent* event) const
+bool HTMLInputElement::isTextFormControlKeyboardFocusable(KeyboardEvent& event) const
{
return HTMLTextFormControlElement::isKeyboardFocusable(event);
}
@@ -399,17 +436,17 @@ bool HTMLInputElement::isTextFormControlMouseFocusable() const
return HTMLTextFormControlElement::isMouseFocusable();
}
-void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
+void HTMLInputElement::updateFocusAppearance(SelectionRestorationMode restorationMode, SelectionRevealMode revealMode)
{
if (isTextField()) {
- if (!restorePreviousSelection || !hasCachedSelection())
- select();
+ if (restorationMode == SelectionRestorationMode::SetDefault || !hasCachedSelection())
+ select(Element::defaultFocusTextStateChangeIntent());
else
restoreCachedSelection();
if (document().frame())
- document().frame()->selection().revealSelection();
+ document().frame()->selection().revealSelection(revealMode);
} else
- HTMLTextFormControlElement::updateFocusAppearance(restorePreviousSelection);
+ HTMLTextFormControlElement::updateFocusAppearance(restorationMode, revealMode);
}
void HTMLInputElement::endEditing()
@@ -436,65 +473,42 @@ void HTMLInputElement::handleBlurEvent()
m_inputType->handleBlurEvent();
}
-void HTMLInputElement::setType(const String& type)
+void HTMLInputElement::setType(const AtomicString& type)
{
- // FIXME: This should just call setAttribute. No reason to handle the empty string specially.
- // We should write a test case to show that setting to the empty string does not remove the
- // attribute in other browsers and then fix this. Note that setting to null *does* remove
- // the attribute and setAttribute implements that.
- if (type.isEmpty())
- removeAttribute(typeAttr);
- else
- setAttribute(typeAttr, type);
+ setAttributeWithoutSynchronization(typeAttr, type);
}
void HTMLInputElement::updateType()
{
- auto newType = InputType::create(*this, fastGetAttribute(typeAttr));
- bool hadType = m_hasType;
+ ASSERT(m_inputType);
+ auto newType = InputType::create(*this, attributeWithoutSynchronization(typeAttr));
m_hasType = true;
if (m_inputType->formControlType() == newType->formControlType())
return;
- if (hadType && !newType->canChangeFromAnotherType()) {
- // Set the attribute back to the old value.
- // Useful in case we were called from inside parseAttribute.
- setAttribute(typeAttr, type());
- return;
- }
-
removeFromRadioButtonGroup();
bool didStoreValue = m_inputType->storesValueSeparateFromAttribute();
bool neededSuspensionCallback = needsSuspensionCallback();
bool didRespectHeightAndWidth = m_inputType->shouldRespectHeightAndWidthAttributes();
+ bool wasSuccessfulSubmitButtonCandidate = m_inputType->canBeSuccessfulSubmitButton();
m_inputType->destroyShadowSubtree();
- m_inputType = std::move(newType);
+ m_inputType = WTFMove(newType);
m_inputType->createShadowSubtree();
-
-#if ENABLE(TOUCH_EVENTS)
- bool hasTouchEventHandler = m_inputType->hasTouchEventHandler();
- if (hasTouchEventHandler != m_hasTouchEventHandler) {
- if (hasTouchEventHandler)
- document().didAddTouchEventHandler(this);
- else
- document().didRemoveTouchEventHandler(this);
- m_hasTouchEventHandler = hasTouchEventHandler;
- }
-#endif
+ updateInnerTextElementEditability();
setNeedsWillValidateCheck();
bool willStoreValue = m_inputType->storesValueSeparateFromAttribute();
if (didStoreValue && !willStoreValue && hasDirtyValue()) {
- setAttribute(valueAttr, m_valueIfDirty);
+ setAttributeWithoutSynchronization(valueAttr, m_valueIfDirty);
m_valueIfDirty = String();
}
if (!didStoreValue && willStoreValue) {
- AtomicString valueString = fastGetAttribute(valueAttr);
+ AtomicString valueString = attributeWithoutSynchronization(valueAttr);
m_valueIfDirty = sanitizeValue(valueString);
} else
updateValueIfNeeded();
@@ -520,21 +534,37 @@ void HTMLInputElement::updateType()
attributeChanged(alignAttr, nullAtom, align->value());
}
+ if (form() && wasSuccessfulSubmitButtonCandidate != m_inputType->canBeSuccessfulSubmitButton())
+ form()->resetDefaultButton();
+
+ runPostTypeUpdateTasks();
+}
+
+inline void HTMLInputElement::runPostTypeUpdateTasks()
+{
+ ASSERT(m_inputType);
+#if ENABLE(TOUCH_EVENTS)
+ bool hasTouchEventHandler = m_inputType->hasTouchEventHandler();
+ if (hasTouchEventHandler != m_hasTouchEventHandler) {
+ if (hasTouchEventHandler)
+ document().didAddTouchEventHandler(*this);
+ else
+ document().didRemoveTouchEventHandler(*this);
+ m_hasTouchEventHandler = hasTouchEventHandler;
+ }
+#endif
+
if (renderer())
- setNeedsStyleRecalc(ReconstructRenderTree);
+ invalidateStyleAndRenderersForSubtree();
if (document().focusedElement() == this)
- updateFocusAppearance(true);
-
- if (ShadowRoot* shadowRoot = shadowRootOfParentForDistribution(this))
- shadowRoot->invalidateDistribution();
+ updateFocusAppearance(SelectionRestorationMode::Restore, SelectionRevealMode::Reveal);
setChangedSinceLastFormControlChangeEvent(false);
addToRadioButtonGroup();
- setNeedsValidityCheck();
- notifyFormStateChanged();
+ updateValidity();
}
void HTMLInputElement::subtreeHasChanged()
@@ -614,15 +644,38 @@ void HTMLInputElement::collectStyleForPresentationAttribute(const QualifiedName&
HTMLTextFormControlElement::collectStyleForPresentationAttribute(name, value, style);
}
+inline void HTMLInputElement::initializeInputType()
+{
+ ASSERT(m_parsingInProgress);
+ ASSERT(!m_inputType);
+
+ const AtomicString& type = attributeWithoutSynchronization(typeAttr);
+ if (type.isNull()) {
+ m_inputType = InputType::createText(*this);
+ ensureUserAgentShadowRoot();
+ setNeedsWillValidateCheck();
+ return;
+ }
+
+ m_hasType = true;
+ m_inputType = InputType::create(*this, type);
+ ensureUserAgentShadowRoot();
+ setNeedsWillValidateCheck();
+ registerForSuspensionCallbackIfNeeded();
+ runPostTypeUpdateTasks();
+}
+
void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
{
+ ASSERT(m_inputType);
+
if (name == nameAttr) {
removeFromRadioButtonGroup();
m_name = value;
addToRadioButtonGroup();
HTMLTextFormControlElement::parseAttribute(name, value);
} else if (name == autocompleteAttr) {
- if (equalIgnoringCase(value, "off")) {
+ if (equalLettersIgnoringASCIICase(value, "off")) {
m_autocomplete = Off;
registerForSuspensionCallbackIfNeeded();
} else {
@@ -647,14 +700,16 @@ void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicStr
}
// We only need to setChanged if the form is looking at the default value right now.
if (!hasDirtyValue()) {
- updatePlaceholderVisibility(false);
- setNeedsStyleRecalc();
+ updatePlaceholderVisibility();
+ invalidateStyleForSubtree();
}
setFormControlValueMatchesRenderer(false);
- setNeedsValidityCheck();
+ updateValidity();
m_valueAttributeWasUpdatedAfterParsing = !m_parsingInProgress;
- m_inputType->valueAttributeChanged();
} else if (name == checkedAttr) {
+ if (m_inputType->isCheckable())
+ invalidateStyleForSubtree();
+
// Another radio button in the same group might be checked by state
// restore. We shouldn't call setChecked() even if this has the checked
// attribute. So, delay the setChecked() call until
@@ -664,11 +719,12 @@ void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicStr
m_reflectsCheckedAttribute = true;
}
} else if (name == maxlengthAttr)
- parseMaxLengthAttribute(value);
+ maxLengthAttributeChanged(value);
+ else if (name == minlengthAttr)
+ minLengthAttributeChanged(value);
else if (name == sizeAttr) {
- int oldSize = m_size;
- int valueAsInteger = value.toInt();
- m_size = valueAsInteger > 0 ? valueAsInteger : defaultSize;
+ unsigned oldSize = m_size;
+ m_size = limitToOnlyHTMLNonNegativeNumbersGreaterThanZero(value, defaultSize);
if (m_size != oldSize && renderer())
renderer()->setNeedsLayoutAndPrefWidthsRecalc();
} else if (name == altAttr)
@@ -677,45 +733,29 @@ void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicStr
m_inputType->srcAttributeChanged();
else if (name == usemapAttr || name == accesskeyAttr) {
// FIXME: ignore for the moment
- } else if (name == onsearchAttr) {
- // Search field and slider attributes all just cause updateFromElement to be called through style recalcing.
- setAttributeEventListener(eventNames().searchEvent, name, value);
} else if (name == resultsAttr) {
- int oldResults = m_maxResults;
m_maxResults = !value.isNull() ? std::min(value.toInt(), maxSavedResults) : -1;
-
- if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0))
- setNeedsStyleRecalc(ReconstructRenderTree);
- else
- setNeedsStyleRecalc();
- FeatureObserver::observe(&document(), FeatureObserver::ResultsAttribute);
+ m_inputType->maxResultsAttributeChanged();
} else if (name == autosaveAttr) {
- setNeedsStyleRecalc();
- FeatureObserver::observe(&document(), FeatureObserver::AutoSaveAttribute);
+ invalidateStyleForSubtree();
} else if (name == incrementalAttr) {
- setNeedsStyleRecalc();
- FeatureObserver::observe(&document(), FeatureObserver::IncrementalAttribute);
+ invalidateStyleForSubtree();
} else if (name == minAttr) {
m_inputType->minOrMaxAttributeChanged();
- setNeedsValidityCheck();
- FeatureObserver::observe(&document(), FeatureObserver::MinAttribute);
+ updateValidity();
} else if (name == maxAttr) {
m_inputType->minOrMaxAttributeChanged();
- setNeedsValidityCheck();
- FeatureObserver::observe(&document(), FeatureObserver::MaxAttribute);
+ updateValidity();
} else if (name == multipleAttr) {
m_inputType->multipleAttributeChanged();
- setNeedsValidityCheck();
+ updateValidity();
} else if (name == stepAttr) {
m_inputType->stepAttributeChanged();
- setNeedsValidityCheck();
- FeatureObserver::observe(&document(), FeatureObserver::StepAttribute);
+ updateValidity();
} else if (name == patternAttr) {
- setNeedsValidityCheck();
- FeatureObserver::observe(&document(), FeatureObserver::PatternAttribute);
+ updateValidity();
} else if (name == precisionAttr) {
- setNeedsValidityCheck();
- FeatureObserver::observe(&document(), FeatureObserver::PrecisionAttribute);
+ updateValidity();
} else if (name == disabledAttr) {
HTMLTextFormControlElement::parseAttribute(name, value);
m_inputType->disabledAttributeChanged();
@@ -730,41 +770,27 @@ void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicStr
resetListAttributeTargetObserver();
listAttributeTargetChanged();
}
- FeatureObserver::observe(&document(), FeatureObserver::ListAttribute);
- }
-#endif
-#if ENABLE(INPUT_SPEECH)
- else if (name == webkitspeechAttr) {
- m_inputType->destroyShadowSubtree();
- m_inputType->createShadowSubtree();
-
- // This renderer and its children have quite different layouts and styles depending on
- // whether the speech button is visible or not. So we reset the whole thing and recreate
- // to get the right styles and layout.
- setNeedsStyleRecalc(ReconstructRenderTree);
-
- setFormControlValueMatchesRenderer(false);
- FeatureObserver::observe(&document(), FeatureObserver::PrefixedSpeechAttribute);
- } else if (name == onwebkitspeechchangeAttr)
- setAttributeEventListener(eventNames().webkitspeechchangeEvent, name, value);
-#endif
-#if ENABLE(DIRECTORY_UPLOAD)
- else if (name == webkitdirectoryAttr) {
- HTMLTextFormControlElement::parseAttribute(name, value);
- FeatureObserver::observe(&document(), FeatureObserver::PrefixedDirectoryAttribute);
}
#endif
else
HTMLTextFormControlElement::parseAttribute(name, value);
- m_inputType->attributeChanged();
+
+ m_inputType->attributeChanged(name);
+}
+
+void HTMLInputElement::parserDidSetAttributes()
+{
+ ASSERT(m_parsingInProgress);
+ initializeInputType();
}
void HTMLInputElement::finishParsingChildren()
{
m_parsingInProgress = false;
+ ASSERT(m_inputType);
HTMLTextFormControlElement::finishParsingChildren();
if (!m_stateRestored) {
- bool checked = hasAttribute(checkedAttr);
+ bool checked = hasAttributeWithoutSynchronization(checkedAttr);
if (checked)
setChecked(checked);
m_reflectsCheckedAttribute = true;
@@ -776,9 +802,9 @@ bool HTMLInputElement::rendererIsNeeded(const RenderStyle& style)
return m_inputType->rendererIsNeeded() && HTMLTextFormControlElement::rendererIsNeeded(style);
}
-RenderPtr<RenderElement> HTMLInputElement::createElementRenderer(PassRef<RenderStyle> style)
+RenderPtr<RenderElement> HTMLInputElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
{
- return m_inputType->createInputRenderer(std::move(style));
+ return m_inputType->createInputRenderer(WTFMove(style));
}
void HTMLInputElement::willAttachRenderers()
@@ -794,7 +820,7 @@ void HTMLInputElement::didAttachRenderers()
m_inputType->attach();
if (document().focusedElement() == this)
- document().updateFocusAppearanceSoon(true /* restore selection */);
+ document().updateFocusAppearanceSoon(SelectionRestorationMode::Restore);
}
void HTMLInputElement::didDetachRenderers()
@@ -808,12 +834,12 @@ String HTMLInputElement::altText() const
// http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
// also heavily discussed by Hixie on bugzilla
// note this is intentionally different to HTMLImageElement::altText()
- String alt = fastGetAttribute(altAttr);
+ String alt = attributeWithoutSynchronization(altAttr);
// fall back to title attribute
if (alt.isNull())
- alt = getAttribute(titleAttr);
+ alt = attributeWithoutSynchronization(titleAttr);
if (alt.isNull())
- alt = getAttribute(valueAttr);
+ alt = attributeWithoutSynchronization(valueAttr);
if (alt.isEmpty())
alt = inputElementAltText();
return alt;
@@ -826,6 +852,14 @@ bool HTMLInputElement::isSuccessfulSubmitButton() const
return !isDisabledFormControl() && m_inputType->canBeSuccessfulSubmitButton();
}
+bool HTMLInputElement::matchesDefaultPseudoClass() const
+{
+ ASSERT(m_inputType);
+ if (m_inputType->canBeSuccessfulSubmitButton())
+ return !isDisabledFormControl() && form() && form()->defaultButton() == this;
+ return m_inputType->isCheckable() && hasAttributeWithoutSynchronization(checkedAttr);
+}
+
bool HTMLInputElement::isActivatedSubmit() const
{
return m_isActivatedSubmit;
@@ -846,8 +880,8 @@ void HTMLInputElement::reset()
if (m_inputType->storesValueSeparateFromAttribute())
setValue(String());
- setAutofilled(false);
- setChecked(hasAttribute(checkedAttr));
+ setAutoFilled(false);
+ setChecked(hasAttributeWithoutSynchronization(checkedAttr));
m_reflectsCheckedAttribute = true;
}
@@ -868,13 +902,13 @@ void HTMLInputElement::setChecked(bool nowChecked, TextFieldEventBehavior eventB
m_reflectsCheckedAttribute = false;
m_isChecked = nowChecked;
- setNeedsStyleRecalc();
+ invalidateStyleForSubtree();
- if (CheckedRadioButtons* buttons = checkedRadioButtons())
- buttons->updateCheckedState(this);
+ if (RadioButtonGroups* buttons = radioButtonGroups())
+ buttons->updateCheckedState(this);
if (renderer() && renderer()->style().hasAppearance())
- renderer()->theme().stateChanged(renderer(), CheckedState);
- setNeedsValidityCheck();
+ renderer()->theme().stateChanged(*renderer(), ControlStates::CheckedState);
+ updateValidity();
// Ideally we'd do this from the render tree (matching
// RenderTextView), but it's not possible to do it at the moment
@@ -889,12 +923,12 @@ void HTMLInputElement::setChecked(bool nowChecked, TextFieldEventBehavior eventB
// unchecked to match other browsers. DOM is not a useful standard for this
// because it says only to fire change events at "lose focus" time, which is
// definitely wrong in practice for these types of elements.
- if (eventBehavior != DispatchNoEvent && inDocument() && m_inputType->shouldSendChangeEventAfterCheckedChanged()) {
+ if (eventBehavior != DispatchNoEvent && isConnected() && m_inputType->shouldSendChangeEventAfterCheckedChanged()) {
setTextAsOfLastFormControlChangeEvent(String());
dispatchFormControlChangeEvent();
}
- didAffectSelector(AffectedSelectorChecked);
+ invalidateStyleForSubtree();
}
void HTMLInputElement::setIndeterminate(bool newValue)
@@ -904,13 +938,13 @@ void HTMLInputElement::setIndeterminate(bool newValue)
m_isIndeterminate = newValue;
- didAffectSelector(AffectedSelectorIndeterminate);
+ invalidateStyleForSubtree();
if (renderer() && renderer()->style().hasAppearance())
- renderer()->theme().stateChanged(renderer(), CheckedState);
+ renderer()->theme().stateChanged(*renderer(), ControlStates::CheckedState);
}
-int HTMLInputElement::size() const
+unsigned HTMLInputElement::size() const
{
return m_size;
}
@@ -927,7 +961,7 @@ float HTMLInputElement::decorationWidth() const
void HTMLInputElement::copyNonAttributePropertiesFromElement(const Element& source)
{
- const HTMLInputElement& sourceElement = static_cast<const HTMLInputElement&>(source);
+ auto& sourceElement = downcast<HTMLInputElement>(source);
m_valueIfDirty = sourceElement.m_valueIfDirty;
m_wasModifiedByUser = false;
@@ -937,6 +971,7 @@ void HTMLInputElement::copyNonAttributePropertiesFromElement(const Element& sour
HTMLTextFormControlElement::copyNonAttributePropertiesFromElement(source);
+ updateValidity();
setFormControlValueMatchesRenderer(false);
m_inputType->updateInnerTextValue();
}
@@ -951,7 +986,7 @@ String HTMLInputElement::value() const
if (!value.isNull())
return value;
- AtomicString valueString = fastGetAttribute(valueAttr);
+ auto& valueString = attributeWithoutSynchronization(valueAttr);
value = sanitizeValue(valueString);
if (!value.isNull())
return value;
@@ -974,21 +1009,6 @@ void HTMLInputElement::setValueForUser(const String& value)
setValue(value, DispatchChangeEvent);
}
-const String& HTMLInputElement::suggestedValue() const
-{
- return m_suggestedValue;
-}
-
-void HTMLInputElement::setSuggestedValue(const String& value)
-{
- if (!m_inputType->canSetSuggestedValue())
- return;
- setFormControlValueMatchesRenderer(false);
- m_suggestedValue = sanitizeValue(value);
- setNeedsStyleRecalc();
- m_inputType->updateInnerTextValue();
-}
-
void HTMLInputElement::setEditingValue(const String& value)
{
if (!renderer() || !isTextField())
@@ -1005,41 +1025,30 @@ void HTMLInputElement::setEditingValue(const String& value)
dispatchInputEvent();
}
-void HTMLInputElement::setValue(const String& value, ExceptionCode& ec, TextFieldEventBehavior eventBehavior)
+ExceptionOr<void> HTMLInputElement::setValue(const String& value, TextFieldEventBehavior eventBehavior)
{
- if (isFileUpload() && !value.isEmpty()) {
- ec = INVALID_STATE_ERR;
- return;
- }
- setValue(value, eventBehavior);
-}
+ if (isFileUpload() && !value.isEmpty())
+ return Exception { INVALID_STATE_ERR };
-void HTMLInputElement::setValue(const String& value, TextFieldEventBehavior eventBehavior)
-{
if (!m_inputType->canSetValue(value))
- return;
+ return { };
- Ref<HTMLInputElement> protect(*this);
+ Ref<HTMLInputElement> protectedThis(*this);
EventQueueScope scope;
String sanitizedValue = sanitizeValue(value);
bool valueChanged = sanitizedValue != this->value();
setLastChangeWasNotUserEdit();
setFormControlValueMatchesRenderer(false);
- m_suggestedValue = String(); // Prevent TextFieldInputType::setValue from using the suggested value.
m_inputType->setValue(sanitizedValue, valueChanged, eventBehavior);
-
- if (!valueChanged)
- return;
-
- notifyFormStateChanged();
+ return { };
}
void HTMLInputElement::setValueInternal(const String& sanitizedValue, TextFieldEventBehavior eventBehavior)
{
m_valueIfDirty = sanitizedValue;
m_wasModifiedByUser = eventBehavior != DispatchNoEvent;
- setNeedsValidityCheck();
+ updateValidity();
}
double HTMLInputElement::valueAsDate() const
@@ -1047,9 +1056,9 @@ double HTMLInputElement::valueAsDate() const
return m_inputType->valueAsDate();
}
-void HTMLInputElement::setValueAsDate(double value, ExceptionCode& ec)
+ExceptionOr<void> HTMLInputElement::setValueAsDate(double value)
{
- m_inputType->setValueAsDate(value, ec);
+ return m_inputType->setValueAsDate(value);
}
double HTMLInputElement::valueAsNumber() const
@@ -1057,13 +1066,11 @@ double HTMLInputElement::valueAsNumber() const
return m_inputType->valueAsDouble();
}
-void HTMLInputElement::setValueAsNumber(double newValue, ExceptionCode& ec, TextFieldEventBehavior eventBehavior)
+ExceptionOr<void> HTMLInputElement::setValueAsNumber(double newValue, TextFieldEventBehavior eventBehavior)
{
- if (!std::isfinite(newValue)) {
- ec = NOT_SUPPORTED_ERR;
- return;
- }
- m_inputType->setValueAsDouble(newValue, eventBehavior, ec);
+ if (!std::isfinite(newValue))
+ return Exception { NOT_SUPPORTED_ERR };
+ return m_inputType->setValueAsDouble(newValue, eventBehavior);
}
void HTMLInputElement::setValueFromRenderer(const String& value)
@@ -1071,13 +1078,15 @@ void HTMLInputElement::setValueFromRenderer(const String& value)
// File upload controls will never use this.
ASSERT(!isFileUpload());
- m_suggestedValue = String();
-
// Renderer and our event handler are responsible for sanitizing values.
- ASSERT(value == sanitizeValue(value) || sanitizeValue(value).isEmpty());
+ // Input types that support the selection API do *not* sanitize their
+ // user input in order to retain parity between what's in the model and
+ // what's on the screen.
+ ASSERT(m_inputType->supportsSelectionAPI() || value == sanitizeValue(value) || sanitizeValue(value).isEmpty());
// Workaround for bug where trailing \n is included in the result of textContent.
- // The assert macro above may also be simplified to: value == constrainValue(value)
+ // The assert macro above may also be simplified by removing the expression
+ // that calls isEmpty.
// http://bugs.webkit.org/show_bug.cgi?id=9661
m_valueIfDirty = value == "\n" ? emptyString() : value;
@@ -1087,19 +1096,18 @@ void HTMLInputElement::setValueFromRenderer(const String& value)
// Input event is fired by the Node::defaultEventHandler for editable controls.
if (!isTextField())
dispatchInputEvent();
- notifyFormStateChanged();
- setNeedsValidityCheck();
+ updateValidity();
- // Clear autofill flag (and yellow background) on user edit.
- setAutofilled(false);
+ // Clear auto fill flag (and yellow background) on user edit.
+ setAutoFilled(false);
}
void HTMLInputElement::willDispatchEvent(Event& event, InputElementClickState& state)
{
- if (event.type() == eventNames().textInputEvent && m_inputType->shouldSubmitImplicitly(&event))
+ if (event.type() == eventNames().textInputEvent && m_inputType->shouldSubmitImplicitly(event))
event.stopPropagation();
- if (event.type() == eventNames().clickEvent && event.isMouseEvent() && toMouseEvent(&event)->button() == LeftButton) {
+ if (event.type() == eventNames().clickEvent && is<MouseEvent>(event) && downcast<MouseEvent>(event).button() == LeftButton) {
m_inputType->willDispatchClick(state);
state.stateful = true;
}
@@ -1110,34 +1118,34 @@ void HTMLInputElement::didDispatchClickEvent(Event& event, const InputElementCli
m_inputType->didDispatchClick(&event, state);
}
-void HTMLInputElement::defaultEventHandler(Event* evt)
+void HTMLInputElement::defaultEventHandler(Event& event)
{
- if (evt->isMouseEvent() && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
- m_inputType->handleClickEvent(static_cast<MouseEvent*>(evt));
- if (evt->defaultHandled())
+ if (is<MouseEvent>(event) && event.type() == eventNames().clickEvent && downcast<MouseEvent>(event).button() == LeftButton) {
+ m_inputType->handleClickEvent(downcast<MouseEvent>(event));
+ if (event.defaultHandled())
return;
}
#if ENABLE(TOUCH_EVENTS)
- if (evt->isTouchEvent()) {
- m_inputType->handleTouchEvent(static_cast<TouchEvent*>(evt));
- if (evt->defaultHandled())
+ if (is<TouchEvent>(event)) {
+ m_inputType->handleTouchEvent(downcast<TouchEvent>(event));
+ if (event.defaultHandled())
return;
}
#endif
- if (evt->isKeyboardEvent() && evt->type() == eventNames().keydownEvent) {
- m_inputType->handleKeydownEvent(static_cast<KeyboardEvent*>(evt));
- if (evt->defaultHandled())
+ if (is<KeyboardEvent>(event) && event.type() == eventNames().keydownEvent) {
+ m_inputType->handleKeydownEvent(downcast<KeyboardEvent>(event));
+ if (event.defaultHandled())
return;
}
// Call the base event handler before any of our own event handling for almost all events in text fields.
// Makes editing keyboard handling take precedence over the keydown and keypress handling in this function.
- bool callBaseClassEarly = isTextField() && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);
+ bool callBaseClassEarly = isTextField() && (event.type() == eventNames().keydownEvent || event.type() == eventNames().keypressEvent);
if (callBaseClassEarly) {
- HTMLTextFormControlElement::defaultEventHandler(evt);
- if (evt->defaultHandled())
+ HTMLTextFormControlElement::defaultEventHandler(event);
+ if (event.defaultHandled())
return;
}
@@ -1145,27 +1153,28 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
// actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
// on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
// must dispatch a DOMActivate event - a click event will not do the job.
- if (evt->type() == eventNames().DOMActivateEvent) {
- m_inputType->handleDOMActivateEvent(evt);
- if (evt->defaultHandled())
+ if (event.type() == eventNames().DOMActivateEvent) {
+ m_inputType->handleDOMActivateEvent(event);
+ if (event.defaultHandled())
return;
}
// Use key press event here since sending simulated mouse events
// on key down blocks the proper sending of the key press event.
- if (evt->isKeyboardEvent() && evt->type() == eventNames().keypressEvent) {
- m_inputType->handleKeypressEvent(static_cast<KeyboardEvent*>(evt));
- if (evt->defaultHandled())
- return;
- }
-
- if (evt->isKeyboardEvent() && evt->type() == eventNames().keyupEvent) {
- m_inputType->handleKeyupEvent(static_cast<KeyboardEvent*>(evt));
- if (evt->defaultHandled())
- return;
+ if (is<KeyboardEvent>(event)) {
+ KeyboardEvent& keyboardEvent = downcast<KeyboardEvent>(event);
+ if (keyboardEvent.type() == eventNames().keypressEvent) {
+ m_inputType->handleKeypressEvent(keyboardEvent);
+ if (keyboardEvent.defaultHandled())
+ return;
+ } else if (keyboardEvent.type() == eventNames().keyupEvent) {
+ m_inputType->handleKeyupEvent(keyboardEvent);
+ if (keyboardEvent.defaultHandled())
+ return;
+ }
}
- if (m_inputType->shouldSubmitImplicitly(evt)) {
+ if (m_inputType->shouldSubmitImplicitly(event)) {
if (isSearchField()) {
addSearchResult();
onSearch();
@@ -1175,29 +1184,27 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
if (wasChangedSinceLastFormControlChangeEvent())
dispatchFormControlChangeEvent();
- RefPtr<HTMLFormElement> formForSubmission = m_inputType->formForSubmission();
// Form may never have been present, or may have been destroyed by code responding to the change event.
- if (formForSubmission)
- formForSubmission->submitImplicitly(evt, canTriggerImplicitSubmission());
+ if (auto* formElement = form())
+ formElement->submitImplicitly(event, canTriggerImplicitSubmission());
- evt->setDefaultHandled();
+ event.setDefaultHandled();
return;
}
- if (evt->isBeforeTextInsertedEvent())
- m_inputType->handleBeforeTextInsertedEvent(static_cast<BeforeTextInsertedEvent*>(evt));
+ if (is<BeforeTextInsertedEvent>(event))
+ m_inputType->handleBeforeTextInsertedEvent(downcast<BeforeTextInsertedEvent>(event));
- if (evt->isMouseEvent() && evt->type() == eventNames().mousedownEvent) {
- m_inputType->handleMouseDownEvent(static_cast<MouseEvent*>(evt));
- if (evt->defaultHandled())
+ if (is<MouseEvent>(event) && event.type() == eventNames().mousedownEvent) {
+ m_inputType->handleMouseDownEvent(downcast<MouseEvent>(event));
+ if (event.defaultHandled())
return;
}
- document().updateStyleIfNeeded();
- m_inputType->forwardEvent(evt);
+ m_inputType->forwardEvent(event);
- if (!callBaseClassEarly && !evt->defaultHandled())
- HTMLTextFormControlElement::defaultEventHandler(evt);
+ if (!callBaseClassEarly && !event.defaultHandled())
+ HTMLTextFormControlElement::defaultEventHandler(event);
}
bool HTMLInputElement::willRespondToMouseClickEvents()
@@ -1215,12 +1222,12 @@ bool HTMLInputElement::isURLAttribute(const Attribute& attribute) const
String HTMLInputElement::defaultValue() const
{
- return fastGetAttribute(valueAttr);
+ return attributeWithoutSynchronization(valueAttr);
}
void HTMLInputElement::setDefaultValue(const String &value)
{
- setAttribute(valueAttr, value);
+ setAttributeWithoutSynchronization(valueAttr, value);
}
static inline bool isRFC2616TokenCharacter(UChar ch)
@@ -1255,13 +1262,13 @@ static Vector<String> parseAcceptAttribute(const String& acceptString, bool (*pr
Vector<String> splitTypes;
acceptString.split(',', false, splitTypes);
- for (size_t i = 0; i < splitTypes.size(); ++i) {
- String trimmedType = stripLeadingAndTrailingHTMLSpaces(splitTypes[i]);
+ for (auto& splitType : splitTypes) {
+ String trimmedType = stripLeadingAndTrailingHTMLSpaces(splitType);
if (trimmedType.isEmpty())
continue;
if (!predicate(trimmedType))
continue;
- types.append(trimmedType.lower());
+ types.append(trimmedType.convertToASCIILowercase());
}
return types;
@@ -1269,67 +1276,64 @@ static Vector<String> parseAcceptAttribute(const String& acceptString, bool (*pr
Vector<String> HTMLInputElement::acceptMIMETypes()
{
- return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidMIMEType);
+ return parseAcceptAttribute(attributeWithoutSynchronization(acceptAttr), isValidMIMEType);
}
Vector<String> HTMLInputElement::acceptFileExtensions()
{
- return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidFileExtension);
+ return parseAcceptAttribute(attributeWithoutSynchronization(acceptAttr), isValidFileExtension);
}
String HTMLInputElement::accept() const
{
- return fastGetAttribute(acceptAttr);
+ return attributeWithoutSynchronization(acceptAttr);
}
String HTMLInputElement::alt() const
{
- return fastGetAttribute(altAttr);
-}
-
-int HTMLInputElement::maxLength() const
-{
- return m_maxLength;
+ return attributeWithoutSynchronization(altAttr);
}
-void HTMLInputElement::setMaxLength(int maxLength, ExceptionCode& ec)
+unsigned HTMLInputElement::effectiveMaxLength() const
{
- if (maxLength < 0)
- ec = INDEX_SIZE_ERR;
- else
- setIntegralAttribute(maxlengthAttr, maxLength);
+ // The number -1 represents no maximum at all; conveniently it becomes a super-large value when converted to unsigned.
+ return std::min<unsigned>(maxLength(), maxEffectiveLength);
}
bool HTMLInputElement::multiple() const
{
- return fastHasAttribute(multipleAttr);
+ return hasAttributeWithoutSynchronization(multipleAttr);
}
-void HTMLInputElement::setSize(unsigned size)
+ExceptionOr<void> HTMLInputElement::setSize(unsigned size)
{
- setUnsignedIntegralAttribute(sizeAttr, size);
+ if (!size)
+ return Exception { INDEX_SIZE_ERR };
+ setUnsignedIntegralAttribute(sizeAttr, limitToOnlyHTMLNonNegativeNumbersGreaterThanZero(size, defaultSize));
+ return { };
}
-void HTMLInputElement::setSize(unsigned size, ExceptionCode& ec)
+URL HTMLInputElement::src() const
{
- if (!size)
- ec = INDEX_SIZE_ERR;
- else
- setSize(size);
+ return document().completeURL(attributeWithoutSynchronization(srcAttr));
}
-URL HTMLInputElement::src() const
+void HTMLInputElement::setAutoFilled(bool autoFilled)
{
- return document().completeURL(fastGetAttribute(srcAttr));
+ if (autoFilled == m_isAutoFilled)
+ return;
+
+ m_isAutoFilled = autoFilled;
+ invalidateStyleForSubtree();
}
-void HTMLInputElement::setAutofilled(bool autofilled)
+void HTMLInputElement::setShowAutoFillButton(AutoFillButtonType autoFillButtonType)
{
- if (autofilled == m_isAutofilled)
+ if (static_cast<uint8_t>(autoFillButtonType) == m_autoFillButtonType)
return;
- m_isAutofilled = autofilled;
- setNeedsStyleRecalc();
+ m_autoFillButtonType = static_cast<uint8_t>(autoFillButtonType);
+ m_inputType->updateAutoFillButton();
}
FileList* HTMLInputElement::files()
@@ -1337,9 +1341,9 @@ FileList* HTMLInputElement::files()
return m_inputType->files();
}
-void HTMLInputElement::setFiles(PassRefPtr<FileList> files)
+void HTMLInputElement::setFiles(RefPtr<FileList>&& files)
{
- m_inputType->setFiles(files);
+ m_inputType->setFiles(WTFMove(files));
}
#if ENABLE(DRAG_SUPPORT)
@@ -1396,12 +1400,12 @@ String HTMLInputElement::localizeValue(const String& proposedValue) const
bool HTMLInputElement::isInRange() const
{
- return m_inputType->isInRange(value());
+ return willValidate() && m_inputType->isInRange(value());
}
bool HTMLInputElement::isOutOfRange() const
{
- return m_inputType->isOutOfRange(value());
+ return willValidate() && m_inputType->isOutOfRange(value());
}
bool HTMLInputElement::needsSuspensionCallback()
@@ -1422,13 +1426,13 @@ bool HTMLInputElement::needsSuspensionCallback()
void HTMLInputElement::registerForSuspensionCallbackIfNeeded()
{
if (needsSuspensionCallback())
- document().registerForPageCacheSuspensionCallbacks(this);
+ document().registerForDocumentSuspensionCallbacks(this);
}
void HTMLInputElement::unregisterForSuspensionCallbackIfNeeded()
{
if (!needsSuspensionCallback())
- document().unregisterForPageCacheSuspensionCallbacks(this);
+ document().unregisterForDocumentSuspensionCallbacks(this);
}
bool HTMLInputElement::isRequiredFormControl() const
@@ -1436,11 +1440,6 @@ bool HTMLInputElement::isRequiredFormControl() const
return m_inputType->supportsRequired() && isRequired();
}
-bool HTMLInputElement::matchesReadOnlyPseudoClass() const
-{
- return m_inputType->supportsReadOnly() && isReadOnly();
-}
-
bool HTMLInputElement::matchesReadWritePseudoClass() const
{
return m_inputType->supportsReadOnly() && !isDisabledOrReadOnly();
@@ -1453,23 +1452,22 @@ void HTMLInputElement::addSearchResult()
void HTMLInputElement::onSearch()
{
- ASSERT(isSearchField());
+ // The type of the input element could have changed during event handling. If we are no longer
+ // a search field, don't try to do search things.
+ if (!isSearchField())
+ return;
+
if (m_inputType)
- static_cast<SearchInputType*>(m_inputType.get())->stopSearchEventTimer();
+ downcast<SearchInputType>(*m_inputType.get()).stopSearchEventTimer();
dispatchEvent(Event::create(eventNames().searchEvent, true, false));
}
-void HTMLInputElement::updateClearButtonVisibility()
-{
- m_inputType->updateClearButtonVisibility();
-}
-
-void HTMLInputElement::documentDidResumeFromPageCache()
+void HTMLInputElement::resumeFromDocumentSuspension()
{
ASSERT(needsSuspensionCallback());
#if ENABLE(INPUT_TYPE_COLOR)
- // <input type=color> uses documentWillSuspendForPageCache to detach the color picker UI,
+ // <input type=color> uses prepareForDocumentSuspension to detach the color picker UI,
// so it should not be reset when being loaded from page cache.
if (isColorControl())
return;
@@ -1478,7 +1476,7 @@ void HTMLInputElement::documentDidResumeFromPageCache()
}
#if ENABLE(INPUT_TYPE_COLOR)
-void HTMLInputElement::documentWillSuspendForPageCache()
+void HTMLInputElement::prepareForDocumentSuspension()
{
if (!isColorControl())
return;
@@ -1502,49 +1500,52 @@ void HTMLInputElement::didChangeForm()
Node::InsertionNotificationRequest HTMLInputElement::insertedInto(ContainerNode& insertionPoint)
{
HTMLTextFormControlElement::insertedInto(insertionPoint);
- if (insertionPoint.inDocument() && !form())
- addToRadioButtonGroup();
#if ENABLE(DATALIST_ELEMENT)
resetListAttributeTargetObserver();
#endif
- return InsertionDone;
+ return InsertionShouldCallFinishedInsertingSubtree;
+}
+
+void HTMLInputElement::finishedInsertingSubtree()
+{
+ HTMLTextFormControlElement::finishedInsertingSubtree();
+ if (isConnected() && !form())
+ addToRadioButtonGroup();
}
void HTMLInputElement::removedFrom(ContainerNode& insertionPoint)
{
- if (insertionPoint.inDocument() && !form())
+ if (insertionPoint.isConnected() && !form())
removeFromRadioButtonGroup();
HTMLTextFormControlElement::removedFrom(insertionPoint);
- ASSERT(!inDocument());
+ ASSERT(!isConnected());
#if ENABLE(DATALIST_ELEMENT)
resetListAttributeTargetObserver();
#endif
}
-void HTMLInputElement::didMoveToNewDocument(Document* oldDocument)
+void HTMLInputElement::didMoveToNewDocument(Document& oldDocument)
{
- if (hasImageLoader())
+ if (imageLoader())
imageLoader()->elementDidMoveToNewDocument();
bool needsSuspensionCallback = this->needsSuspensionCallback();
- if (oldDocument) {
- // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
- if (needsSuspensionCallback)
- oldDocument->unregisterForPageCacheSuspensionCallbacks(this);
- if (isRadioButton())
- oldDocument->formController().checkedRadioButtons().removeButton(this);
+ // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
+ if (needsSuspensionCallback)
+ oldDocument.unregisterForDocumentSuspensionCallbacks(this);
+ if (isRadioButton())
+ oldDocument.formController().radioButtonGroups().removeButton(this);
#if ENABLE(TOUCH_EVENTS)
- if (m_hasTouchEventHandler)
- oldDocument->didRemoveEventTargetNode(this);
+ if (m_hasTouchEventHandler)
+ oldDocument.didRemoveEventTargetNode(*this);
#endif
- }
if (needsSuspensionCallback)
- document().registerForPageCacheSuspensionCallbacks(this);
+ document().registerForDocumentSuspensionCallbacks(this);
#if ENABLE(TOUCH_EVENTS)
if (m_hasTouchEventHandler)
- document().didAddTouchEventHandler(this);
+ document().didAddTouchEventHandler(*this);
#endif
HTMLTextFormControlElement::didMoveToNewDocument(oldDocument);
@@ -1557,28 +1558,29 @@ void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
addSubresourceURL(urls, src());
}
-bool HTMLInputElement::recalcWillValidate() const
+bool HTMLInputElement::computeWillValidate() const
{
- return m_inputType->supportsValidation() && HTMLTextFormControlElement::recalcWillValidate();
+ return m_inputType->supportsValidation() && HTMLTextFormControlElement::computeWillValidate();
}
void HTMLInputElement::requiredAttributeChanged()
{
HTMLTextFormControlElement::requiredAttributeChanged();
- if (CheckedRadioButtons* buttons = checkedRadioButtons())
+ if (RadioButtonGroups* buttons = radioButtonGroups())
buttons->requiredAttributeChanged(this);
m_inputType->requiredAttributeChanged();
}
-#if ENABLE(INPUT_TYPE_COLOR)
-void HTMLInputElement::selectColorInColorChooser(const Color& color)
+Color HTMLInputElement::valueAsColor() const
{
- if (!m_inputType->isColorControl())
- return;
- static_cast<ColorInputType*>(m_inputType.get())->didChooseColor(color);
+ return m_inputType->valueAsColor();
}
-#endif
-
+
+void HTMLInputElement::selectColor(const Color& color)
+{
+ m_inputType->selectColor(color);
+}
+
#if ENABLE(DATALIST_ELEMENT)
HTMLElement* HTMLInputElement::list() const
{
@@ -1588,24 +1590,22 @@ HTMLElement* HTMLInputElement::list() const
HTMLDataListElement* HTMLInputElement::dataList() const
{
if (!m_hasNonEmptyList)
- return 0;
+ return nullptr;
if (!m_inputType->shouldRespectListAttribute())
- return 0;
+ return nullptr;
- Element* element = treeScope().getElementById(fastGetAttribute(listAttr));
- if (!element)
- return 0;
- if (!element->hasTagName(datalistTag))
- return 0;
+ Element* element = treeScope().getElementById(attributeWithoutSynchronization(listAttr));
+ if (!is<HTMLDataListElement>(element))
+ return nullptr;
- return toHTMLDataListElement(element);
+ return downcast<HTMLDataListElement>(element);
}
void HTMLInputElement::resetListAttributeTargetObserver()
{
- if (inDocument())
- m_listAttributeTargetObserver = ListAttributeTargetObserver::create(fastGetAttribute(listAttr), this);
+ if (isConnected())
+ m_listAttributeTargetObserver = std::make_unique<ListAttributeTargetObserver>(attributeWithoutSynchronization(listAttr), this);
else
m_listAttributeTargetObserver = nullptr;
}
@@ -1621,16 +1621,6 @@ bool HTMLInputElement::isSteppable() const
return m_inputType->isSteppable();
}
-#if ENABLE(INPUT_SPEECH)
-
-bool HTMLInputElement::isSpeechEnabled() const
-{
- // FIXME: Add support for RANGE, EMAIL, URL, COLOR and DATE/TIME input types.
- return m_inputType->shouldRespectSpeechAttribute() && RuntimeEnabledFeatures::sharedFeatures().speechInputEnabled() && hasAttribute(webkitspeechAttr);
-}
-
-#endif
-
#if PLATFORM(IOS)
DateComponents::Type HTMLInputElement::dateType() const
{
@@ -1775,19 +1765,33 @@ void HTMLInputElement::updatePlaceholderText()
return m_inputType->updatePlaceholderText();
}
-void HTMLInputElement::parseMaxLengthAttribute(const AtomicString& value)
+bool HTMLInputElement::isEmptyValue() const
+{
+ return m_inputType->isEmptyValue();
+}
+
+void HTMLInputElement::maxLengthAttributeChanged(const AtomicString& newValue)
{
- int maxLength;
- if (!parseHTMLInteger(value, maxLength))
- maxLength = maximumLength;
- if (maxLength < 0 || maxLength > maximumLength)
- maxLength = maximumLength;
- int oldMaxLength = m_maxLength;
- m_maxLength = maxLength;
- if (oldMaxLength != maxLength)
+ unsigned oldEffectiveMaxLength = effectiveMaxLength();
+ internalSetMaxLength(parseHTMLNonNegativeInteger(newValue).value_or(-1));
+ if (oldEffectiveMaxLength != effectiveMaxLength())
updateValueIfNeeded();
- setNeedsStyleRecalc();
- setNeedsValidityCheck();
+
+ // FIXME: Do we really need to do this if the effective maxLength has not changed?
+ invalidateStyleForSubtree();
+ updateValidity();
+}
+
+void HTMLInputElement::minLengthAttributeChanged(const AtomicString& newValue)
+{
+ int oldMinLength = minLength();
+ internalSetMinLength(parseHTMLNonNegativeInteger(newValue).value_or(-1));
+ if (oldMinLength != minLength())
+ updateValueIfNeeded();
+
+ // FIXME: Do we really need to do this if the effective minLength has not changed?
+ invalidateStyleForSubtree();
+ updateValidity();
}
void HTMLInputElement::updateValueIfNeeded()
@@ -1803,69 +1807,83 @@ String HTMLInputElement::defaultToolTip() const
return m_inputType->defaultToolTip();
}
-bool HTMLInputElement::shouldAppearIndeterminate() const
+bool HTMLInputElement::matchesIndeterminatePseudoClass() const
{
- return m_inputType->supportsIndeterminateAppearance() && indeterminate();
+ // For input elements, matchesIndeterminatePseudoClass()
+ // is not equivalent to shouldAppearIndeterminate() because of radio button.
+ //
+ // A group of radio button without any checked button is indeterminate
+ // for the :indeterminate selector. On the other hand, RenderTheme
+ // currently only supports single element being indeterminate.
+ // Because of this, radio is indetermindate for CSS but not for render theme.
+ return m_inputType->matchesIndeterminatePseudoClass();
}
-#if ENABLE(MEDIA_CAPTURE)
-String HTMLInputElement::capture() const
+bool HTMLInputElement::shouldAppearIndeterminate() const
{
- if (!isFileUpload())
- return String();
-
- String capture = fastGetAttribute(captureAttr).lower();
- if (capture == "camera"
- || capture == "camcorder"
- || capture == "microphone"
- || capture == "filesystem")
- return capture;
-
- return "filesystem";
+ return m_inputType->shouldAppearIndeterminate();
}
-void HTMLInputElement::setCapture(const String& value)
+#if ENABLE(MEDIA_CAPTURE)
+MediaCaptureType HTMLInputElement::mediaCaptureType() const
{
- setAttribute(captureAttr, value);
+ if (!isFileUpload())
+ return MediaCaptureTypeNone;
+
+ auto& captureAttribute = attributeWithoutSynchronization(captureAttr);
+ if (captureAttribute.isNull())
+ return MediaCaptureTypeNone;
+
+ if (equalLettersIgnoringASCIICase(captureAttribute, "user"))
+ return MediaCaptureTypeUser;
+
+ return MediaCaptureTypeEnvironment;
}
-
#endif
bool HTMLInputElement::isInRequiredRadioButtonGroup()
{
ASSERT(isRadioButton());
- if (CheckedRadioButtons* buttons = checkedRadioButtons())
+ if (RadioButtonGroups* buttons = radioButtonGroups())
return buttons->isInRequiredGroup(this);
return false;
}
+Vector<HTMLInputElement*> HTMLInputElement::radioButtonGroup() const
+{
+ RadioButtonGroups* buttons = radioButtonGroups();
+ if (!buttons)
+ return { };
+ return buttons->groupMembers(*this);
+}
+
HTMLInputElement* HTMLInputElement::checkedRadioButtonForGroup() const
{
- if (CheckedRadioButtons* buttons = checkedRadioButtons())
+ if (RadioButtonGroups* buttons = radioButtonGroups())
return buttons->checkedButtonForGroup(name());
return 0;
}
-CheckedRadioButtons* HTMLInputElement::checkedRadioButtons() const
+RadioButtonGroups* HTMLInputElement::radioButtonGroups() const
{
if (!isRadioButton())
- return 0;
- if (HTMLFormElement* formElement = form())
- return &formElement->checkedRadioButtons();
- if (inDocument())
- return &document().formController().checkedRadioButtons();
- return 0;
+ return nullptr;
+ if (auto* formElement = form())
+ return &formElement->radioButtonGroups();
+ if (isConnected())
+ return &document().formController().radioButtonGroups();
+ return nullptr;
}
inline void HTMLInputElement::addToRadioButtonGroup()
{
- if (CheckedRadioButtons* buttons = checkedRadioButtons())
+ if (RadioButtonGroups* buttons = radioButtonGroups())
buttons->addButton(this);
}
inline void HTMLInputElement::removeFromRadioButtonGroup()
{
- if (CheckedRadioButtons* buttons = checkedRadioButtons())
+ if (RadioButtonGroups* buttons = radioButtonGroups())
buttons->removeButton(this);
}
@@ -1890,11 +1908,6 @@ void HTMLInputElement::setWidth(unsigned width)
}
#if ENABLE(DATALIST_ELEMENT)
-OwnPtr<ListAttributeTargetObserver> ListAttributeTargetObserver::create(const AtomicString& id, HTMLInputElement* element)
-{
- return adoptPtr(new ListAttributeTargetObserver(id, element));
-}
-
ListAttributeTargetObserver::ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement* element)
: IdTargetObserver(element->treeScope().idTargetObserverRegistry(), id)
, m_element(element)
@@ -1907,24 +1920,108 @@ void ListAttributeTargetObserver::idTargetChanged()
}
#endif
-void HTMLInputElement::setRangeText(const String& replacement, ExceptionCode& ec)
+ExceptionOr<void> HTMLInputElement::setRangeText(const String& replacement)
{
- if (!m_inputType->supportsSelectionAPI()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (!m_inputType->supportsSelectionAPI())
+ return Exception { INVALID_STATE_ERR };
- HTMLTextFormControlElement::setRangeText(replacement, ec);
+ return HTMLTextFormControlElement::setRangeText(replacement);
}
-void HTMLInputElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionCode& ec)
+ExceptionOr<void> HTMLInputElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode)
{
- if (!m_inputType->supportsSelectionAPI()) {
- ec = INVALID_STATE_ERR;
- return;
- }
+ if (!m_inputType->supportsSelectionAPI())
+ return Exception { INVALID_STATE_ERR };
+
+ return HTMLTextFormControlElement::setRangeText(replacement, start, end, selectionMode);
+}
+
+bool HTMLInputElement::shouldTruncateText(const RenderStyle& style) const
+{
+ if (!isTextField())
+ return false;
+ return document().focusedElement() != this && style.textOverflow() == TextOverflowEllipsis;
+}
+
+ExceptionOr<int> HTMLInputElement::selectionStartForBindings() const
+{
+ if (!canHaveSelection())
+ return Exception { TypeError };
+
+ return selectionStart();
+}
+
+ExceptionOr<void> HTMLInputElement::setSelectionStartForBindings(int start)
+{
+ if (!canHaveSelection())
+ return Exception { TypeError };
+
+ setSelectionStart(start);
+ return { };
+}
- HTMLTextFormControlElement::setRangeText(replacement, start, end, selectionMode, ec);
+ExceptionOr<int> HTMLInputElement::selectionEndForBindings() const
+{
+ if (!canHaveSelection())
+ return Exception { TypeError };
+
+ return selectionEnd();
+}
+
+ExceptionOr<void> HTMLInputElement::setSelectionEndForBindings(int end)
+{
+ if (!canHaveSelection())
+ return Exception { TypeError };
+
+ setSelectionEnd(end);
+ return { };
+}
+
+ExceptionOr<String> HTMLInputElement::selectionDirectionForBindings() const
+{
+ if (!canHaveSelection())
+ return Exception { TypeError };
+
+ return String { selectionDirection() };
+}
+
+ExceptionOr<void> HTMLInputElement::setSelectionDirectionForBindings(const String& direction)
+{
+ if (!canHaveSelection())
+ return Exception { TypeError };
+
+ setSelectionDirection(direction);
+ return { };
+}
+
+ExceptionOr<void> HTMLInputElement::setSelectionRangeForBindings(int start, int end, const String& direction)
+{
+ if (!canHaveSelection())
+ return Exception { TypeError };
+
+ setSelectionRange(start, end, direction);
+ return { };
+}
+
+RenderStyle HTMLInputElement::createInnerTextStyle(const RenderStyle& style) const
+{
+ auto textBlockStyle = RenderStyle::create();
+ textBlockStyle.inheritFrom(style);
+ adjustInnerTextStyle(style, textBlockStyle);
+
+ textBlockStyle.setWhiteSpace(PRE);
+ textBlockStyle.setOverflowWrap(NormalOverflowWrap);
+ textBlockStyle.setOverflowX(OHIDDEN);
+ textBlockStyle.setOverflowY(OHIDDEN);
+ textBlockStyle.setTextOverflow(shouldTruncateText(style) ? TextOverflowEllipsis : TextOverflowClip);
+
+ // Do not allow line-height to be smaller than our default.
+ if (textBlockStyle.fontMetrics().lineSpacing() > style.computedLineHeight())
+ textBlockStyle.setLineHeight(RenderStyle::initialLineHeight());
+
+ textBlockStyle.setDisplay(BLOCK);
+
+ return textBlockStyle;
}
#if ENABLE(DATE_AND_TIME_INPUT_TYPES)
@@ -1937,7 +2034,8 @@ bool HTMLInputElement::setupDateTimeChooserParameters(DateTimeChooserParameters&
parameters.minimum = minimum();
parameters.maximum = maximum();
parameters.required = isRequired();
- if (!RuntimeEnabledFeatures::sharedFeatures().langAttributeAwareFormControlUIEnabled())
+
+ if (!document().settings().langAttributeAwareFormControlUIEnabled())
parameters.locale = defaultLanguage();
else {
AtomicString computedLocale = computeInheritedLanguage();
@@ -1953,13 +2051,16 @@ bool HTMLInputElement::setupDateTimeChooserParameters(DateTimeChooserParameters&
parameters.stepBase = 0;
}
- parameters.anchorRectInRootView = document().view()->contentsToRootView(pixelSnappedBoundingBox());
+ if (RenderElement* renderer = this->renderer())
+ parameters.anchorRectInRootView = document().view()->contentsToRootView(renderer->absoluteBoundingBoxRect());
+ else
+ parameters.anchorRectInRootView = IntRect();
parameters.currentValue = value();
parameters.isAnchorElementRTL = computedStyle()->direction() == RTL;
#if ENABLE(DATALIST_ELEMENT)
if (HTMLDataListElement* dataList = this->dataList()) {
- RefPtr<HTMLCollection> options = dataList->options();
- for (unsigned i = 0; HTMLOptionElement* option = toHTMLOptionElement(options->item(i)); ++i) {
+ Ref<HTMLCollection> options = dataList->options();
+ for (unsigned i = 0; HTMLOptionElement* option = downcast<HTMLOptionElement>(options->item(i)); ++i) {
if (!isValidValue(option->value()))
continue;
parameters.suggestionValues.append(sanitizeValue(option->value()));
@@ -1972,4 +2073,9 @@ bool HTMLInputElement::setupDateTimeChooserParameters(DateTimeChooserParameters&
}
#endif
+void HTMLInputElement::capsLockStateMayHaveChanged()
+{
+ m_inputType->capsLockStateMayHaveChanged();
+}
+
} // namespace