Bug 635553 (2/2) - <input type='number'> can suffer from a range underflow when @min is set. r=sicking

--HG--
extra : rebase_source : 27045996c62489b28c799d11b6cee5f5df3a2359
This commit is contained in:
Mounir Lamouri 2012-06-22 11:38:20 +02:00
parent 1891be27db
commit 535904ed42
6 changed files with 212 additions and 0 deletions

View File

@ -815,6 +815,8 @@ nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
UpdateTypeMismatchValidityState(); UpdateTypeMismatchValidityState();
} else if (aName == nsGkAtoms::max) { } else if (aName == nsGkAtoms::max) {
UpdateRangeOverflowValidityState(); UpdateRangeOverflowValidityState();
} else if (aName == nsGkAtoms::min) {
UpdateRangeUnderflowValidityState();
} }
UpdateState(aNotify); UpdateState(aNotify);
@ -3800,6 +3802,30 @@ nsHTMLInputElement::IsRangeOverflow() const
return value > max; return value > max;
} }
bool
nsHTMLInputElement::IsRangeUnderflow() const
{
nsAutoString minStr;
if (!DoesMinMaxApply() ||
!GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr)) {
return false;
}
PRInt32 ec;
double min = minStr.ToDouble(&ec);
if (NS_FAILED(ec)) {
return false;
}
double value = GetValueAsDouble();
// value can be NaN when value="".
if (value != value) {
return false;
}
return value < min;
}
void void
nsHTMLInputElement::UpdateTooLongValidityState() nsHTMLInputElement::UpdateTooLongValidityState()
{ {
@ -3884,6 +3910,12 @@ nsHTMLInputElement::UpdateRangeOverflowValidityState()
SetValidityState(VALIDITY_STATE_RANGE_OVERFLOW, IsRangeOverflow()); SetValidityState(VALIDITY_STATE_RANGE_OVERFLOW, IsRangeOverflow());
} }
void
nsHTMLInputElement::UpdateRangeUnderflowValidityState()
{
SetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW, IsRangeUnderflow());
}
void void
nsHTMLInputElement::UpdateAllValidityStates(bool aNotify) nsHTMLInputElement::UpdateAllValidityStates(bool aNotify)
{ {
@ -3893,6 +3925,7 @@ nsHTMLInputElement::UpdateAllValidityStates(bool aNotify)
UpdateTypeMismatchValidityState(); UpdateTypeMismatchValidityState();
UpdatePatternMismatchValidityState(); UpdatePatternMismatchValidityState();
UpdateRangeOverflowValidityState(); UpdateRangeOverflowValidityState();
UpdateRangeUnderflowValidityState();
if (validBefore != IsValid()) { if (validBefore != IsValid()) {
UpdateState(aNotify); UpdateState(aNotify);
@ -4020,6 +4053,26 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
aValidationMessage = message; aValidationMessage = message;
break; break;
} }
case VALIDITY_STATE_RANGE_UNDERFLOW:
{
nsXPIDLString message;
nsAutoString minStr;
GetAttr(kNameSpaceID_None, nsGkAtoms::min, minStr);
// We want to show the double as parsed so we parse it and change minStr.
PRInt32 ec;
double min = minStr.ToDouble(&ec);
NS_ASSERTION(NS_SUCCEEDED(ec), "min must be a number at this point!");
minStr.Truncate();
minStr.AppendFloat(min);
const PRUnichar* params[] = { minStr.get() };
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"FormValidationRangeUnderflow",
params, message);
aValidationMessage = message;
break;
}
default: default:
rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType); rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
} }

View File

@ -217,11 +217,13 @@ public:
bool HasTypeMismatch() const; bool HasTypeMismatch() const;
bool HasPatternMismatch() const; bool HasPatternMismatch() const;
bool IsRangeOverflow() const; bool IsRangeOverflow() const;
bool IsRangeUnderflow() const;
void UpdateTooLongValidityState(); void UpdateTooLongValidityState();
void UpdateValueMissingValidityState(); void UpdateValueMissingValidityState();
void UpdateTypeMismatchValidityState(); void UpdateTypeMismatchValidityState();
void UpdatePatternMismatchValidityState(); void UpdatePatternMismatchValidityState();
void UpdateRangeOverflowValidityState(); void UpdateRangeOverflowValidityState();
void UpdateRangeUnderflowValidityState();
void UpdateAllValidityStates(bool aNotify); void UpdateAllValidityStates(bool aNotify);
void UpdateBarredFromConstraintValidation(); void UpdateBarredFromConstraintValidation();
nsresult GetValidationMessage(nsAString& aValidationMessage, nsresult GetValidationMessage(nsAString& aValidationMessage,

View File

@ -74,6 +74,8 @@ nsIConstraintValidation::GetValidationMessage(nsAString& aValidationMessage)
GetValidationMessage(aValidationMessage, VALIDITY_STATE_PATTERN_MISMATCH); GetValidationMessage(aValidationMessage, VALIDITY_STATE_PATTERN_MISMATCH);
} else if (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW)) { } else if (GetValidityState(VALIDITY_STATE_RANGE_OVERFLOW)) {
GetValidationMessage(aValidationMessage, VALIDITY_STATE_RANGE_OVERFLOW); GetValidationMessage(aValidationMessage, VALIDITY_STATE_RANGE_OVERFLOW);
} else if (GetValidityState(VALIDITY_STATE_RANGE_UNDERFLOW)) {
GetValidationMessage(aValidationMessage, VALIDITY_STATE_RANGE_UNDERFLOW);
} else { } else {
// TODO: The other messages have not been written // TODO: The other messages have not been written
// because related constraint validation are not implemented yet. // because related constraint validation are not implemented yet.

View File

@ -43,6 +43,7 @@ _TEST_FILES = \
test_meter_element.html \ test_meter_element.html \
test_meter_pseudo-classes.html \ test_meter_pseudo-classes.html \
test_max_attribute.html \ test_max_attribute.html \
test_min_attribute.html \
$(NULL) $(NULL)
libs:: $(_TEST_FILES) libs:: $(_TEST_FILES)

View File

@ -0,0 +1,152 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=635553
-->
<head>
<title>Test for Bug 635553</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635499">Mozilla Bug 635499</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 635553 **/
var types = [
[ 'hidden', false ],
[ 'text', false ],
[ 'search', false ],
[ 'tel', false ],
[ 'url', false ],
[ 'email', false ],
[ 'password', false ],
[ 'datetime', true, true ],
[ 'date', true, true ],
[ 'month', true, true ],
[ 'week', true, true ],
[ 'time', true, true ],
[ 'datetime-local', true, true ],
[ 'number', true ],
[ 'range', true, true ],
[ 'color', false, true ],
[ 'checkbox', false ],
[ 'radio', false ],
[ 'file', false ],
[ 'submit', false ],
[ 'image', false ],
[ 'reset', false ],
[ 'button', false ],
];
var input = document.createElement("input");
document.getElementById('content').appendChild(input);
function checkValidity(aElement, aValidity)
{
is(aElement.validity.valid, aValidity,
"element validity should be " + aValidity);
is(aElement.validity.rangeUnderflow, !aValidity,
"element underflow status should be " + !aValidity);
is(aElement.validationMessage, aValidity
? "" : "Please select a value that is higher than " + aElement.min + ".",
"validation message");
is(aElement.mozMatchesSelector(":valid"), aElement.willValidate && aValidity,
(aElement.willValidate && aValidity) ? ":valid should apply" : "valid shouldn't apply");
is(aElement.mozMatchesSelector(":invalid"), aElement.willValidate && !aValidity,
(aElement.wil && aValidity) ? ":invalid shouldn't apply" : "valid should apply");
// TODO: Add tests for out-of-range / in-range selectors, see bug 635554.
}
for each (var data in types) {
input.type = data[0];
var apply = data[1];
if (data[2]) {
todo_is(input.type, data[0], data[0] + " isn't implemented yet");
continue;
}
checkValidity(input, true);
input.min = '0';
checkValidity(input, true);
if (input.type == 'url') {
input.value = 'http://mozilla.org';
checkValidity(input, true);
} else if (input.type == 'email') {
input.value = 'foo@bar.com';
checkValidity(input, true);
} else if (input.type == 'file') {
// Need privileges to set a filename with .value.
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties);
var file = dirSvc.get("ProfD", Components.interfaces.nsIFile);
file.append('635499_file');
var outStream = Components.
classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
outStream.init(file, 0x02 | 0x08 | 0x20, // write, create, truncate
0666, 0);
outStream.write("foo", 3);
outStream.close();
input.value = file.path;
checkValidity(input, true);
file.remove(false);
} else {
input.value = '1';
checkValidity(input, true);
input.value = '0';
checkValidity(input, true);
input.value = 'foo';
checkValidity(input, true);
input.value = '-0.1';
checkValidity(input, !apply);
input.min = '-1';
checkValidity(input, true);
input.value = '-42';
checkValidity(input, !apply);
}
input.min = '';
checkValidity(input, true);
input.min = 'foo';
checkValidity(input, true);
// Check that we correctly convert input.min to a double in validationMessage.
if (input.type == 'number') {
input.min = "4.333333333333333333333333333333333331";
input.value = "2";
is(input.validationMessage,
"Please select a value that is higher than 4.33333333333333.",
"validation message");
}
// Cleaning up,
input.removeAttribute('min');
input.value = '';
}
</script>
</pre>
</body>
</html>

View File

@ -42,6 +42,8 @@ FormValidationPatternMismatch=Please match the requested format.
FormValidationPatternMismatchWithTitle=Please match the requested format: %S. FormValidationPatternMismatchWithTitle=Please match the requested format: %S.
# LOCALIZATION NOTE (FormValidationRangeOverflow): %S can be a number or a date. # LOCALIZATION NOTE (FormValidationRangeOverflow): %S can be a number or a date.
FormValidationRangeOverflow=Please select a value that is lower than %S. FormValidationRangeOverflow=Please select a value that is lower than %S.
# LOCALIZATION NOTE (FormValidationRangeUnderflow): %S can be a number or a date.
FormValidationRangeUnderflow=Please select a value that is higher than %S.
GetAttributeNodeWarning=Use of getAttributeNode() is deprecated. Use getAttribute() instead. GetAttributeNodeWarning=Use of getAttributeNode() is deprecated. Use getAttribute() instead.
SetAttributeNodeWarning=Use of setAttributeNode() is deprecated. Use setAttribute() instead. SetAttributeNodeWarning=Use of setAttributeNode() is deprecated. Use setAttribute() instead.
GetAttributeNodeNSWarning=Use of getAttributeNodeNS() is deprecated. Use getAttributeNS() instead. GetAttributeNodeNSWarning=Use of getAttributeNodeNS() is deprecated. Use getAttributeNS() instead.