summaryrefslogtreecommitdiffstats
path: root/WebCore/html/HTMLInputElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/html/HTMLInputElement.cpp')
-rw-r--r--WebCore/html/HTMLInputElement.cpp306
1 files changed, 107 insertions, 199 deletions
diff --git a/WebCore/html/HTMLInputElement.cpp b/WebCore/html/HTMLInputElement.cpp
index b704fc1..765e4fc 100644
--- a/WebCore/html/HTMLInputElement.cpp
+++ b/WebCore/html/HTMLInputElement.cpp
@@ -283,6 +283,11 @@ String HTMLInputElement::typeMismatchText() const
return m_inputType->typeMismatchText();
}
+String HTMLInputElement::valueMissingText() const
+{
+ return m_inputType->valueMissingText();
+}
+
bool HTMLInputElement::getAllowedValueStep(double* step) const
{
return getAllowedValueStepWithDecimalPlaces(step, 0);
@@ -332,13 +337,13 @@ bool HTMLInputElement::getAllowedValueStepWithDecimalPlaces(double* step, unsign
void HTMLInputElement::applyStep(double count, ExceptionCode& ec)
{
double step;
- unsigned stepDecimalPlaces;
+ unsigned stepDecimalPlaces, currentDecimalPlaces;
if (!getAllowedValueStepWithDecimalPlaces(&step, &stepDecimalPlaces)) {
ec = INVALID_STATE_ERR;
return;
}
const double nan = numeric_limits<double>::quiet_NaN();
- double current = m_inputType->parseToDouble(value(), nan);
+ double current = m_inputType->parseToDoubleWithDecimalPlaces(value(), nan, &currentDecimalPlaces);
if (!isfinite(current)) {
ec = INVALID_STATE_ERR;
return;
@@ -359,8 +364,13 @@ void HTMLInputElement::applyStep(double count, ExceptionCode& ec)
double base = m_inputType->stepBaseWithDecimalPlaces(&baseDecimalPlaces);
baseDecimalPlaces = min(baseDecimalPlaces, 16u);
if (newValue < pow(10.0, 21.0)) {
- double scale = pow(10.0, static_cast<double>(max(stepDecimalPlaces, baseDecimalPlaces)));
- newValue = round((base + round((newValue - base) / step) * step) * scale) / scale;
+ if (stepMismatch(value())) {
+ double scale = pow(10.0, static_cast<double>(max(stepDecimalPlaces, currentDecimalPlaces)));
+ newValue = round(newValue * scale) / scale;
+ } else {
+ double scale = pow(10.0, static_cast<double>(max(stepDecimalPlaces, baseDecimalPlaces)));
+ newValue = round((base + round((newValue - base) / step) * step) * scale) / scale;
+ }
}
if (newValue - m_inputType->maximum() > acceptableError) {
ec = INVALID_STATE_ERR;
@@ -774,7 +784,7 @@ void HTMLInputElement::parseMappedAttribute(Attribute* attr)
#if ENABLE(INPUT_SPEECH)
else if (attr->name() == webkitspeechAttr) {
if (renderer())
- renderer()->updateFromElement();
+ toRenderTextControlSingleLine(renderer())->speechAttributeChanged();
setNeedsStyleRecalc();
} else if (attr->name() == onwebkitspeechchangeAttr)
setAttributeEventListener(eventNames().webkitspeechchangeEvent, createAttributeEventListener(this, attr));
@@ -1293,11 +1303,6 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
// FIXME: It would be better to refactor this for the different types of input element.
// Having them all in one giant function makes this hard to read, and almost all the handling is type-specific.
- bool implicitSubmission = false;
-
- if (isTextField() && evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n")
- implicitSubmission = true;
-
if (evt->isMouseEvent() && evt->type() == eventNames().clickEvent && m_inputType->handleClickEvent(static_cast<MouseEvent*>(evt)))
return;
@@ -1306,8 +1311,7 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
// 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() && !implicitSubmission
- && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);
+ bool callBaseClassEarly = isTextField() && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);
if (callBaseClassEarly) {
HTMLFormControlElementWithState::defaultEventHandler(evt);
if (evt->defaultHandled())
@@ -1323,180 +1327,13 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
// Use key press event here since sending simulated mouse events
// on key down blocks the proper sending of the key press event.
- if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) {
- bool clickElement = false;
-
- int charCode = static_cast<KeyboardEvent*>(evt)->charCode();
-
- if (charCode == '\r') {
- switch (deprecatedInputType()) {
- case CHECKBOX:
- case COLOR:
- case DATE:
- case DATETIME:
- case DATETIMELOCAL:
- case EMAIL:
- case HIDDEN:
- case ISINDEX:
- case MONTH:
- case NUMBER:
- case PASSWORD:
- case RADIO:
- case RANGE:
- case SEARCH:
- case TELEPHONE:
- case TEXT:
- case TIME:
- case URL:
- case WEEK:
- // Simulate mouse click on the default form button for enter for these types of elements.
- implicitSubmission = true;
- break;
- case BUTTON:
- case FILE:
- case IMAGE:
- case RESET:
- case SUBMIT:
- // Simulate mouse click for enter for these types of elements.
- clickElement = true;
- break;
- }
- } else if (charCode == ' ') {
- switch (deprecatedInputType()) {
- case BUTTON:
- case CHECKBOX:
- case FILE:
- case IMAGE:
- case RESET:
- case SUBMIT:
- case RADIO:
- // Prevent scrolling down the page.
- evt->setDefaultHandled();
- return;
- default:
- break;
- }
- }
-
- if (clickElement) {
- dispatchSimulatedClick(evt);
- evt->setDefaultHandled();
- return;
- }
- }
-
- if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent()) {
- const String& key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
-
- if (key == "U+0020") {
- switch (deprecatedInputType()) {
- case BUTTON:
- case CHECKBOX:
- case FILE:
- case IMAGE:
- case RESET:
- case SUBMIT:
- case RADIO:
- setActive(true, true);
- // No setDefaultHandled(), because IE dispatches a keypress in this case
- // and the caller will only dispatch a keypress if we don't call setDefaultHandled.
- return;
- default:
- break;
- }
- }
-
- if (deprecatedInputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
- // Left and up mean "previous radio button".
- // Right and down mean "next radio button".
- // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves
- // to the right). Seems strange, but we'll match it.
- // However, when using Spatial Navigation, we need to be able to navigate without changing the selection.
- if (!document()->frame() || !document()->frame()->settings() || !document()->frame()->settings()->isSpatialNavigationEnabled()) {
- bool forward = (key == "Down" || key == "Right");
-
- // We can only stay within the form's children if the form hasn't been demoted to a leaf because
- // of malformed HTML.
- Node* n = this;
- while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
- // Once we encounter a form element, we know we're through.
- if (n->hasTagName(formTag))
- break;
-
- // Look for more radio buttons.
- if (n->hasTagName(inputTag)) {
- HTMLInputElement* elt = static_cast<HTMLInputElement*>(n);
- if (elt->form() != form())
- break;
- if (n->hasTagName(inputTag)) {
- HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n);
- if (inputElt->deprecatedInputType() == RADIO && inputElt->name() == name() && inputElt->isFocusable()) {
- inputElt->setChecked(true);
- document()->setFocusedNode(inputElt);
- inputElt->dispatchSimulatedClick(evt, false, false);
- evt->setDefaultHandled();
- break;
- }
- }
- }
- }
- }
- }
- }
-
- if (evt->type() == eventNames().keyupEvent && evt->isKeyboardEvent()) {
- bool clickElement = false;
-
- const String& key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
-
- if (key == "U+0020") {
- switch (deprecatedInputType()) {
- case BUTTON:
- case CHECKBOX:
- case FILE:
- case IMAGE:
- case RESET:
- case SUBMIT:
- // Simulate mouse click for spacebar for these types of elements.
- // The AppKit already does this for some, but not all, of them.
- clickElement = true;
- break;
- case RADIO:
- // If an unselected radio is tabbed into (because the entire group has nothing
- // checked, or because of some explicit .focus() call), then allow space to check it.
- if (!checked())
- clickElement = true;
- break;
- case COLOR:
- case DATE:
- case DATETIME:
- case DATETIMELOCAL:
- case EMAIL:
- case HIDDEN:
- case ISINDEX:
- case MONTH:
- case NUMBER:
- case PASSWORD:
- case RANGE:
- case SEARCH:
- case TELEPHONE:
- case TEXT:
- case TIME:
- case URL:
- case WEEK:
- break;
- }
- }
+ if (evt->isKeyboardEvent() && evt->type() == eventNames().keypressEvent && m_inputType->handleKeypressEvent(static_cast<KeyboardEvent*>(evt)))
+ return;
- if (clickElement) {
- if (active())
- dispatchSimulatedClick(evt);
- evt->setDefaultHandled();
- return;
- }
- }
+ if (evt->isKeyboardEvent() && evt->type() == eventNames().keyupEvent && m_inputType->handleKeyupEvent(static_cast<KeyboardEvent*>(evt)))
+ return;
- if (implicitSubmission) {
+ if (m_inputType->shouldSubmitImplicitly(evt)) {
if (isSearchField()) {
addSearchResult();
onSearch();
@@ -1860,37 +1697,113 @@ HTMLOptionElement* HTMLInputElement::selectedOption() const
void HTMLInputElement::stepUpFromRenderer(int n)
{
// The differences from stepUp()/stepDown():
- // If the current value is not a number, the value will be
- // - The value should be the minimum value if n > 0
- // - The value should be the maximum value if n < 0
+ //
+ // Difference 1: the current value
+ // If the current value is not a number, including empty, the current value is assumed as 0.
+ // * If 0 is in-range, and matches to step value
+ // - The value should be the +step if n > 0
+ // - The value should be the -step if n < 0
+ // If -step or +step is out of range, new value should be 0.
+ // * If 0 is smaller than the minimum value
+ // - The value should be the minimum value for any n
+ // * If 0 is larger than the maximum value
+ // - The value should be the maximum value for any n
+ // * If 0 is in-range, but not matched to step value
+ // - The value should be the larger matched value nearest to 0 if n > 0
+ // e.g. <input type=number min=-100 step=3> -> 2
+ // - The value should be the smaler matched value nearest to 0 if n < 0
+ // e.g. <input type=number min=-100 step=3> -> -1
+ // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time".
+ // As for datetime type, the current value is assumed as "the current date/time in UTC".
// If the current value is smaller than the minimum value:
// - The value should be the minimum value if n > 0
// - Nothing should happen if n < 0
// If the current value is larger than the maximum value:
// - The value should be the maximum value if n < 0
// - Nothing should happen if n > 0
-
- ASSERT(hasSpinButton());
- if (!hasSpinButton())
+ //
+ // Difference 2: clamping steps
+ // If the current value is not matched to step value:
+ // - The value should be the larger matched value nearest to 0 if n > 0
+ // e.g. <input type=number value=3 min=-100 step=3> -> 5
+ // - The value should be the smaler matched value nearest to 0 if n < 0
+ // e.g. <input type=number value=3 min=-100 step=3> -> 2
+ //
+ // n is assumed as -n if step < 0.
+
+ ASSERT(hasSpinButton() || m_inputType->isRangeControl());
+ if (!hasSpinButton() && !m_inputType->isRangeControl())
return;
ASSERT(n);
if (!n)
return;
+ unsigned stepDecimalPlaces, baseDecimalPlaces;
+ double step, base;
+ // The value will be the default value after stepping for <input value=(empty/invalid) step="any" />
+ // FIXME: Not any changes after stepping, even if it is an invalid value, may be better.
+ // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo")
+ if (equalIgnoringCase(getAttribute(stepAttr), "any"))
+ step = 0;
+ else if (!getAllowedValueStepWithDecimalPlaces(&step, &stepDecimalPlaces))
+ return;
+ base = m_inputType->stepBaseWithDecimalPlaces(&baseDecimalPlaces);
+ baseDecimalPlaces = min(baseDecimalPlaces, 16u);
+
+ int sign;
+ if (step > 0)
+ sign = n;
+ else if (step < 0)
+ sign = -n;
+ else
+ sign = 0;
+
const double nan = numeric_limits<double>::quiet_NaN();
String currentStringValue = value();
double current = m_inputType->parseToDouble(currentStringValue, nan);
- if (!isfinite(current) || (n > 0 && current < m_inputType->minimum()) || (n < 0 && current > m_inputType->maximum()))
- setValue(m_inputType->serialize(n > 0 ? m_inputType->minimum() : m_inputType->maximum()));
+ if (!isfinite(current)) {
+ ExceptionCode ec;
+ current = m_inputType->defaultValueForStepUp();
+ setValueAsNumber(current, ec);
+ }
+ if ((sign > 0 && current < m_inputType->minimum()) || (sign < 0 && current > m_inputType->maximum()))
+ setValue(m_inputType->serialize(sign > 0 ? m_inputType->minimum() : m_inputType->maximum()));
else {
ExceptionCode ec;
- stepUp(n, ec);
+ if (stepMismatch(currentStringValue)) {
+ ASSERT(step);
+ double newValue;
+ double scale = pow(10.0, static_cast<double>(max(stepDecimalPlaces, baseDecimalPlaces)));
+
+ if (sign < 0)
+ newValue = round((base + floor((current - base) / step) * step) * scale) / scale;
+ else if (sign > 0)
+ newValue = round((base + ceil((current - base) / step) * step) * scale) / scale;
+ else
+ newValue = current;
+
+ if (newValue < m_inputType->minimum())
+ newValue = m_inputType->minimum();
+ if (newValue > m_inputType->maximum())
+ newValue = m_inputType->maximum();
+
+ setValueAsNumber(newValue, ec);
+ current = newValue;
+ if (n > 1)
+ applyStep(n - 1, ec);
+ else if (n < -1)
+ applyStep(n + 1, ec);
+ } else
+ applyStep(n, ec);
}
if (currentStringValue != value()) {
if (renderer() && renderer()->isTextField())
toRenderTextControl(renderer())->setChangedSinceLastChangeEvent(true);
- dispatchEvent(Event::create(eventNames().inputEvent, true, false));
+ if (m_inputType->isRangeControl())
+ dispatchFormControlChangeEvent();
+ else
+ dispatchEvent(Event::create(eventNames().inputEvent, true, false));
}
}
@@ -1938,11 +1851,6 @@ bool HTMLInputElement::isSpeechEnabled() const
return false;
}
-void HTMLInputElement::dispatchWebkitSpeechChangeEvent()
-{
- ASSERT(isSpeechEnabled());
- dispatchEvent(Event::create(eventNames().webkitspeechchangeEvent, true, false));
-}
#endif
} // namespace