зеркало из https://github.com/mozilla/gecko-dev.git
Bug 827161, part 2 - Implement HTML 5's ValidityState.badInput and implement the state for HTMLInputElement's number type. r=smaug
This commit is contained in:
Родитель
2a8a0145a1
Коммит
dc7188dcc2
|
@ -19,8 +19,8 @@ class ValidityState;
|
|||
}
|
||||
|
||||
#define NS_ICONSTRAINTVALIDATION_IID \
|
||||
{ 0xca3824dc, 0x4f5c, 0x4878, \
|
||||
{ 0xa6, 0x8a, 0x95, 0x54, 0x5f, 0xfa, 0x4b, 0xf9 } }
|
||||
{ 0x983829da, 0x1aaf, 0x449c, \
|
||||
{ 0xa3, 0x06, 0x85, 0xd4, 0xf0, 0x31, 0x1c, 0xf6 } }
|
||||
|
||||
/**
|
||||
* This interface is for form elements implementing the validity constraint API.
|
||||
|
@ -51,14 +51,16 @@ public:
|
|||
|
||||
enum ValidityStateType
|
||||
{
|
||||
VALIDITY_STATE_VALUE_MISSING = 0x01, // 0b00000001
|
||||
VALIDITY_STATE_TYPE_MISMATCH = 0x02, // 0b00000010
|
||||
VALIDITY_STATE_PATTERN_MISMATCH = 0x04, // 0b00000100
|
||||
VALIDITY_STATE_TOO_LONG = 0x08, // 0b00001000
|
||||
VALIDITY_STATE_RANGE_UNDERFLOW = 0x10, // 0b00010000
|
||||
VALIDITY_STATE_RANGE_OVERFLOW = 0x20, // 0b00100000
|
||||
VALIDITY_STATE_STEP_MISMATCH = 0x40, // 0b01000000
|
||||
VALIDITY_STATE_CUSTOM_ERROR = 0x80 // 0b10000000
|
||||
VALIDITY_STATE_VALUE_MISSING = 0x1 << 0,
|
||||
VALIDITY_STATE_TYPE_MISMATCH = 0x1 << 1,
|
||||
VALIDITY_STATE_PATTERN_MISMATCH = 0x1 << 2,
|
||||
VALIDITY_STATE_TOO_LONG = 0x1 << 3,
|
||||
//VALIDITY_STATE_TOO_SHORT = 0x1 << 4,
|
||||
VALIDITY_STATE_RANGE_UNDERFLOW = 0x1 << 5,
|
||||
VALIDITY_STATE_RANGE_OVERFLOW = 0x1 << 6,
|
||||
VALIDITY_STATE_STEP_MISMATCH = 0x1 << 7,
|
||||
VALIDITY_STATE_BAD_INPUT = 0x1 << 8,
|
||||
VALIDITY_STATE_CUSTOM_ERROR = 0x1 << 9,
|
||||
};
|
||||
|
||||
void SetValidityState(ValidityStateType mState,
|
||||
|
@ -103,7 +105,7 @@ private:
|
|||
* A bitfield representing the current validity state of the element.
|
||||
* Each bit represent an error. All bits to zero means the element is valid.
|
||||
*/
|
||||
int8_t mValidityBitField;
|
||||
int16_t mValidityBitField;
|
||||
|
||||
/**
|
||||
* Keeps track whether the element is barred from constraint validation.
|
||||
|
|
|
@ -2767,6 +2767,7 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue,
|
|||
if (!mParserCreating) {
|
||||
SanitizeValue(value);
|
||||
}
|
||||
// else SanitizeValue will be called by DoneCreatingElement
|
||||
|
||||
if (aSetValueChanged) {
|
||||
SetValueChanged(true);
|
||||
|
@ -2774,6 +2775,9 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue,
|
|||
|
||||
if (IsSingleLineTextControl(false)) {
|
||||
mInputData.mState->SetValue(value, aUserInput, aSetValueChanged);
|
||||
if (mType == NS_FORM_INPUT_EMAIL) {
|
||||
UpdateAllValidityStates(mParserCreating);
|
||||
}
|
||||
} else {
|
||||
mInputData.mValue = ToNewUnicode(value);
|
||||
if (aSetValueChanged) {
|
||||
|
@ -3471,7 +3475,7 @@ HTMLInputElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
|
|||
numberControlFrame->GetValueOfAnonTextControl(value);
|
||||
numberControlFrame->HandlingInputEvent(true);
|
||||
nsWeakFrame weakNumberControlFrame(numberControlFrame);
|
||||
SetValueInternal(value, false, true);
|
||||
SetValueInternal(value, true, true);
|
||||
if (weakNumberControlFrame.IsAlive()) {
|
||||
numberControlFrame->HandlingInputEvent(false);
|
||||
}
|
||||
|
@ -6422,6 +6426,103 @@ HTMLInputElement::HasStepMismatch() const
|
|||
return NS_floorModulo(value - GetStepBase(), step) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits the string on the first "@" character and punycode encodes the first
|
||||
* and second parts separately before rejoining them with an "@" and returning
|
||||
* the result via the aEncodedEmail out-param. Returns false if there is no
|
||||
* "@" caracter, if the "@" character is at the start or end, or if the
|
||||
* conversion to punycode fails.
|
||||
*
|
||||
* This function exists because ConvertUTF8toACE() treats 'username@domain' as
|
||||
* a single label, but we need to encode the username and domain parts
|
||||
* separately.
|
||||
*/
|
||||
static bool PunycodeEncodeEmailAddress(const nsAString& aEmail,
|
||||
nsAutoCString& aEncodedEmail,
|
||||
uint32_t* aIndexOfAt)
|
||||
{
|
||||
nsAutoCString value = NS_ConvertUTF16toUTF8(aEmail);
|
||||
uint32_t length = value.Length();
|
||||
|
||||
uint32_t atPos = (uint32_t)value.FindChar('@');
|
||||
// Email addresses must contain a '@', but can't begin or end with it.
|
||||
if (atPos == (uint32_t)kNotFound || atPos == 0 || atPos == length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID);
|
||||
if (!idnSrv) {
|
||||
NS_ERROR("nsIIDNService isn't present!");
|
||||
return false;
|
||||
}
|
||||
|
||||
const nsDependentCSubstring username = Substring(value, 0, atPos);
|
||||
bool ace;
|
||||
if (NS_SUCCEEDED(idnSrv->IsACE(username, &ace)) && !ace) {
|
||||
nsAutoCString usernameACE;
|
||||
// TODO: Bug 901347: Usernames longer than 63 chars are not converted by
|
||||
// ConvertUTF8toACE(). For now, continue on even if the conversion fails.
|
||||
if (NS_SUCCEEDED(idnSrv->ConvertUTF8toACE(username, usernameACE))) {
|
||||
value.Replace(0, atPos, usernameACE);
|
||||
atPos = usernameACE.Length();
|
||||
}
|
||||
}
|
||||
|
||||
const nsDependentCSubstring domain = Substring(value, atPos + 1);
|
||||
if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) {
|
||||
nsAutoCString domainACE;
|
||||
if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) {
|
||||
return false;
|
||||
}
|
||||
value.Replace(atPos + 1, domain.Length(), domainACE);
|
||||
}
|
||||
|
||||
aEncodedEmail = value;
|
||||
*aIndexOfAt = atPos;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLInputElement::HasBadInput() const
|
||||
{
|
||||
if (mType == NS_FORM_INPUT_NUMBER) {
|
||||
nsAutoString value;
|
||||
GetValueInternal(value);
|
||||
if (!value.IsEmpty()) {
|
||||
// The input can't be bad, otherwise it would have been sanitized to the
|
||||
// empty string.
|
||||
NS_ASSERTION(!GetValueAsDecimal().isNaN(), "Should have sanitized");
|
||||
return false;
|
||||
}
|
||||
nsNumberControlFrame* numberControlFrame =
|
||||
do_QueryFrame(GetPrimaryFrame());
|
||||
if (numberControlFrame &&
|
||||
!numberControlFrame->AnonTextControlIsEmpty()) {
|
||||
// The input the user entered failed to parse as a number.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (mType == NS_FORM_INPUT_EMAIL) {
|
||||
// With regards to suffering from bad input the spec says that only the
|
||||
// punycode conversion works, so we don't care whether the email address is
|
||||
// valid or not here. (If the email address is invalid then we will be
|
||||
// suffering from a type mismatch.)
|
||||
nsAutoString value;
|
||||
nsAutoCString unused;
|
||||
uint32_t unused2;
|
||||
NS_ENSURE_SUCCESS(GetValueInternal(value), false);
|
||||
HTMLSplitOnSpacesTokenizer tokenizer(value, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
if (!PunycodeEncodeEmailAddress(tokenizer.nextToken(), unused, &unused2)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::UpdateTooLongValidityState()
|
||||
{
|
||||
|
@ -6518,6 +6619,12 @@ HTMLInputElement::UpdateStepMismatchValidityState()
|
|||
SetValidityState(VALIDITY_STATE_STEP_MISMATCH, HasStepMismatch());
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::UpdateBadInputValidityState()
|
||||
{
|
||||
SetValidityState(VALIDITY_STATE_BAD_INPUT, HasBadInput());
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::UpdateAllValidityStates(bool aNotify)
|
||||
{
|
||||
|
@ -6529,6 +6636,7 @@ HTMLInputElement::UpdateAllValidityStates(bool aNotify)
|
|||
UpdateRangeOverflowValidityState();
|
||||
UpdateRangeUnderflowValidityState();
|
||||
UpdateStepMismatchValidityState();
|
||||
UpdateBadInputValidityState();
|
||||
|
||||
if (validBefore != IsValid()) {
|
||||
UpdateState(aNotify);
|
||||
|
@ -6753,6 +6861,22 @@ HTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
|
|||
aValidationMessage = message;
|
||||
break;
|
||||
}
|
||||
case VALIDITY_STATE_BAD_INPUT:
|
||||
{
|
||||
nsXPIDLString message;
|
||||
nsAutoCString key;
|
||||
if (mType == NS_FORM_INPUT_NUMBER) {
|
||||
key.AssignLiteral("FormValidationBadInputNumber");
|
||||
} else if (mType == NS_FORM_INPUT_EMAIL) {
|
||||
key.AssignLiteral("FormValidationInvalidEmail");
|
||||
} else {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
||||
key.get(), message);
|
||||
aValidationMessage = message;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
|
||||
}
|
||||
|
@ -6779,51 +6903,25 @@ HTMLInputElement::IsValidEmailAddressList(const nsAString& aValue)
|
|||
bool
|
||||
HTMLInputElement::IsValidEmailAddress(const nsAString& aValue)
|
||||
{
|
||||
nsAutoCString value = NS_ConvertUTF16toUTF8(aValue);
|
||||
uint32_t i = 0;
|
||||
uint32_t length = value.Length();
|
||||
|
||||
// Email addresses can't be empty and can't end with a '.' or '-'.
|
||||
if (length == 0 || value[length - 1] == '.' || value[length - 1] == '-') {
|
||||
if (aValue.IsEmpty() || aValue.Last() == '.' || aValue.Last() == '-') {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t atPos = (uint32_t)value.FindChar('@');
|
||||
uint32_t atPos;
|
||||
nsAutoCString value;
|
||||
if (!PunycodeEncodeEmailAddress(aValue, value, &atPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t length = value.Length();
|
||||
|
||||
// Email addresses must contain a '@', but can't begin or end with it.
|
||||
if (atPos == (uint32_t)kNotFound || atPos == 0 || atPos == length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Puny-encode the string if needed before running the validation algorithm.
|
||||
nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID);
|
||||
if (idnSrv) {
|
||||
// ConvertUTF8toACE() treats 'username@domain' as a single label so we need
|
||||
// to puny-encode the username and domain parts separately.
|
||||
const nsDependentCSubstring username = Substring(value, 0, atPos);
|
||||
bool ace;
|
||||
if (NS_SUCCEEDED(idnSrv->IsACE(username, &ace)) && !ace) {
|
||||
nsAutoCString usernameACE;
|
||||
// TODO: Bug 901347: Usernames longer than 63 chars are not converted by
|
||||
// ConvertUTF8toACE(). For now, continue on even if the conversion fails.
|
||||
if (NS_SUCCEEDED(idnSrv->ConvertUTF8toACE(username, usernameACE))) {
|
||||
value.Replace(0, atPos, usernameACE);
|
||||
atPos = usernameACE.Length();
|
||||
}
|
||||
}
|
||||
|
||||
const nsDependentCSubstring domain = Substring(value, atPos + 1);
|
||||
if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) {
|
||||
nsAutoCString domainACE;
|
||||
if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) {
|
||||
return false;
|
||||
}
|
||||
value.Replace(atPos + 1, domain.Length(), domainACE);
|
||||
}
|
||||
|
||||
length = value.Length();
|
||||
} else {
|
||||
NS_ERROR("nsIIDNService isn't present!");
|
||||
}
|
||||
uint32_t i = 0;
|
||||
|
||||
// Parsing the username.
|
||||
for (; i < atPos; ++i) {
|
||||
|
|
|
@ -257,6 +257,7 @@ public:
|
|||
bool IsRangeOverflow() const;
|
||||
bool IsRangeUnderflow() const;
|
||||
bool HasStepMismatch() const;
|
||||
bool HasBadInput() const;
|
||||
void UpdateTooLongValidityState();
|
||||
void UpdateValueMissingValidityState();
|
||||
void UpdateTypeMismatchValidityState();
|
||||
|
@ -264,6 +265,7 @@ public:
|
|||
void UpdateRangeOverflowValidityState();
|
||||
void UpdateRangeUnderflowValidityState();
|
||||
void UpdateStepMismatchValidityState();
|
||||
void UpdateBadInputValidityState();
|
||||
void UpdateAllValidityStates(bool aNotify);
|
||||
void UpdateBarredFromConstraintValidation();
|
||||
nsresult GetValidationMessage(nsAString& aValidationMessage,
|
||||
|
|
|
@ -76,6 +76,13 @@ ValidityState::GetStepMismatch(bool* aStepMismatch)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ValidityState::GetBadInput(bool* aBadInput)
|
||||
{
|
||||
*aBadInput = BadInput();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ValidityState::GetCustomError(bool* aCustomError)
|
||||
{
|
||||
|
|
|
@ -60,6 +60,10 @@ public:
|
|||
{
|
||||
return GetValidityState(nsIConstraintValidation::VALIDITY_STATE_STEP_MISMATCH);
|
||||
}
|
||||
bool BadInput() const
|
||||
{
|
||||
return GetValidityState(nsIConstraintValidation::VALIDITY_STATE_BAD_INPUT);
|
||||
}
|
||||
bool CustomError() const
|
||||
{
|
||||
return GetValidityState(nsIConstraintValidation::VALIDITY_STATE_CUSTOM_ERROR);
|
||||
|
|
|
@ -86,6 +86,8 @@ nsIConstraintValidation::GetValidationMessage(nsAString& aValidationMessage)
|
|||
GetValidationMessage(aValidationMessage, VALIDITY_STATE_RANGE_UNDERFLOW);
|
||||
} else if (GetValidityState(VALIDITY_STATE_STEP_MISMATCH)) {
|
||||
GetValidationMessage(aValidationMessage, VALIDITY_STATE_STEP_MISMATCH);
|
||||
} else if (GetValidityState(VALIDITY_STATE_BAD_INPUT)) {
|
||||
GetValidationMessage(aValidationMessage, VALIDITY_STATE_BAD_INPUT);
|
||||
} else {
|
||||
// There should not be other validity states.
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
support-files =
|
||||
save_restore_radio_groups.sjs
|
||||
submit_invalid_file.sjs
|
||||
test_input_number_data.js
|
||||
|
||||
[test_button_attributes_reflection.html]
|
||||
[test_change_event.html]
|
||||
|
@ -32,6 +33,9 @@ skip-if = os == "android" || appname == "b2g"
|
|||
skip-if = os == "android" || appname == "b2g"
|
||||
[test_input_number_rounding.html]
|
||||
skip-if = os == "android"
|
||||
[test_input_number_validation.html]
|
||||
# We don't build ICU for Firefox for Android or Firefox OS:
|
||||
skip-if = os == "android" || appname == "b2g"
|
||||
[test_input_range_attr_order.html]
|
||||
[test_input_range_key_events.html]
|
||||
[test_input_range_mouse_and_touch_events.html]
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
|
||||
var tests = [
|
||||
{ desc: "British English",
|
||||
langTag: "en-GB", inputWithGrouping: "123,456.78",
|
||||
inputWithoutGrouping: "123456.78", value: 123456.78
|
||||
},
|
||||
{ desc: "Farsi",
|
||||
langTag: "fa", inputWithGrouping: "۱۲۳٬۴۵۶٫۷۸",
|
||||
inputWithoutGrouping: "۱۲۳۴۵۶٫۷۸", value: 123456.78
|
||||
},
|
||||
{ desc: "French",
|
||||
langTag: "fr-FR", inputWithGrouping: "123 456,78",
|
||||
inputWithoutGrouping: "123456,78", value: 123456.78
|
||||
},
|
||||
{ desc: "German",
|
||||
langTag: "de", inputWithGrouping: "123.456,78",
|
||||
inputWithoutGrouping: "123456,78", value: 123456.78
|
||||
},
|
||||
{ desc: "Hebrew",
|
||||
langTag: "he", inputWithGrouping: "123,456.78",
|
||||
inputWithoutGrouping: "123456.78", value: 123456.78
|
||||
},
|
||||
];
|
|
@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=844744
|
|||
<title>Test localization of number control input</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="test_input_number_data.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
|
@ -31,29 +32,6 @@ SimpleTest.waitForFocus(function() {
|
|||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
var tests = [
|
||||
{ desc: "British English",
|
||||
langTag: "en-GB", inputWithGrouping: "123,456.78",
|
||||
inputWithoutGrouping: "123456.78", value: 123456.78
|
||||
},
|
||||
{ desc: "Farsi",
|
||||
langTag: "fa", inputWithGrouping: "۱۲۳٬۴۵۶٫۷۸",
|
||||
inputWithoutGrouping: "۱۲۳۴۵۶٫۷۸", value: 123456.78
|
||||
},
|
||||
{ desc: "French",
|
||||
langTag: "fr-FR", inputWithGrouping: "123 456,78",
|
||||
inputWithoutGrouping: "123456,78", value: 123456.78
|
||||
},
|
||||
{ desc: "German",
|
||||
langTag: "de", inputWithGrouping: "123.456,78",
|
||||
inputWithoutGrouping: "123456,78", value: 123456.78
|
||||
},
|
||||
{ desc: "Hebrew",
|
||||
langTag: "he", inputWithGrouping: "123,456.78",
|
||||
inputWithoutGrouping: "123456.78", value: 123456.78
|
||||
},
|
||||
];
|
||||
|
||||
var elem;
|
||||
|
||||
function runTest(test) {
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=827161
|
||||
-->
|
||||
<head>
|
||||
<title>Test validation of number control input</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="test_input_number_data.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=827161">Mozilla Bug 827161</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<input id="input" type="number" step="0.01" oninvalid="invalidEventHandler(event);">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/**
|
||||
* Test for Bug 827161.
|
||||
* This test checks that validation works correctly for <input type=number>.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
startTests();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
var elem;
|
||||
|
||||
function runTest(test) {
|
||||
elem.lang = test.langTag;
|
||||
|
||||
gInvalid = false; // reset
|
||||
elem.value = 0;
|
||||
elem.focus();
|
||||
elem.select();
|
||||
sendString(test.inputWithGrouping);
|
||||
checkIsValid(elem, test.desc + " ('" + test.langTag + "') with grouping separator");
|
||||
sendChar("a");
|
||||
checkIsInvalid(elem, test.desc + " ('" + test.langTag + "') with grouping separator");
|
||||
|
||||
gInvalid = false; // reset
|
||||
elem.value = 0;
|
||||
elem.select();
|
||||
sendString(test.inputWithoutGrouping);
|
||||
checkIsValid(elem, test.desc + " ('" + test.langTag + "') without grouping separator");
|
||||
sendChar("a");
|
||||
checkIsInvalid(elem, test.desc + " ('" + test.langTag + "') without grouping separator");
|
||||
}
|
||||
|
||||
function startTests() {
|
||||
elem = document.getElementById("input");
|
||||
for (var test of tests) {
|
||||
runTest(test);
|
||||
}
|
||||
}
|
||||
|
||||
var gInvalid = false;
|
||||
|
||||
function invalidEventHandler(e)
|
||||
{
|
||||
is(e.type, "invalid", "Invalid event type should be 'invalid'");
|
||||
gInvalid = true;
|
||||
}
|
||||
|
||||
function checkIsValid(element, infoStr)
|
||||
{
|
||||
ok(!element.validity.badInput,
|
||||
"Element should not suffer from bad input for " + infoStr);
|
||||
ok(element.validity.valid, "Element should be valid for " + infoStr);
|
||||
ok(element.checkValidity(), "checkValidity() should return true for " + infoStr);
|
||||
ok(!gInvalid, "The invalid event should not have been thrown for " + infoStr);
|
||||
is(element.validationMessage, '',
|
||||
"Validation message should be the empty string for " + infoStr);
|
||||
ok(element.mozMatchesSelector(":valid"), ":valid pseudo-class should apply for " + infoStr);
|
||||
}
|
||||
|
||||
function checkIsInvalid(element, infoStr)
|
||||
{
|
||||
ok(element.validity.badInput,
|
||||
"Element should suffer from bad input for " + infoStr);
|
||||
ok(!element.validity.valid, "Element should not be valid for " + infoStr);
|
||||
ok(!element.checkValidity(), "checkValidity() should return false for " + infoStr);
|
||||
ok(gInvalid, "The invalid event should have been thrown for " + infoStr);
|
||||
is(element.validationMessage, "Please enter a number.",
|
||||
"Validation message is not the expected message for " + infoStr);
|
||||
ok(element.mozMatchesSelector(":invalid"), ":invalid pseudo-class should apply for " + infoStr);
|
||||
}
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -31,6 +31,7 @@ var input = document.getElementById('i');
|
|||
var form = document.getElementById('f');
|
||||
var submitFrame = document.getElementsByTagName('iframe')[0];
|
||||
var testData = [];
|
||||
var gCurrentTest = null;
|
||||
var gValidData = [];
|
||||
var gInvalidData = [];
|
||||
|
||||
|
@ -46,6 +47,19 @@ function urlify(aStr) {
|
|||
return aStr.replace(':', '%3A', 'g');
|
||||
}
|
||||
|
||||
function runTestsForNextInputType()
|
||||
{
|
||||
try {
|
||||
testRunner.next();
|
||||
} catch (e) {
|
||||
if (e.toString() == '[object StopIteration]') {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
throw StopIteration;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkValueSubmittedIsValid()
|
||||
{
|
||||
is(frames['submit_frame'].location.href,
|
||||
|
@ -56,6 +70,12 @@ function checkValueSubmittedIsValid()
|
|||
input.value = "";
|
||||
|
||||
if (valueIndex >= gValidData.length) {
|
||||
if (gCurrentTest.canHaveBadInputValidityState) {
|
||||
// Don't run the submission tests on the invalid input if submission
|
||||
// will be blocked by invalid input.
|
||||
runTestsForNextInputType();
|
||||
return;
|
||||
}
|
||||
valueIndex = 0;
|
||||
submitFrame.onload = checkValueSubmittedIsInvalid;
|
||||
testData = gInvalidData;
|
||||
|
@ -74,15 +94,7 @@ function checkValueSubmittedIsInvalid()
|
|||
|
||||
if (valueIndex >= gInvalidData.length) {
|
||||
if (submitMethod == sendKeyEventToSubmitForm) {
|
||||
try {
|
||||
testRunner.next();
|
||||
} catch (e) {
|
||||
if (e.toString() == '[object StopIteration]') {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
throw StopIteration;
|
||||
}
|
||||
}
|
||||
runTestsForNextInputType();
|
||||
return;
|
||||
}
|
||||
valueIndex = 0;
|
||||
|
@ -109,6 +121,7 @@ function runTest()
|
|||
var data = [
|
||||
{
|
||||
type: 'number',
|
||||
canHaveBadInputValidityState: true,
|
||||
validData: [
|
||||
"42",
|
||||
"-42", // should work for negative values
|
||||
|
@ -173,6 +186,8 @@ function runTest()
|
|||
];
|
||||
|
||||
for (test of data) {
|
||||
gCurrentTest = test;
|
||||
|
||||
if (test.todo) {
|
||||
input.type = test.type;
|
||||
todo_is(input.type, test.type, test.type + " is not implemented");
|
||||
|
|
|
@ -57,6 +57,7 @@ function checkConstraintValidationAPIExist(element)
|
|||
validity = element.validity;
|
||||
ok('valueMissing' in validity, "validity.valueMissing is not available in the DOM");
|
||||
ok('typeMismatch' in validity, "validity.typeMismatch is not available in the DOM");
|
||||
ok('badInput' in validity, "validity.badInput is not available in the DOM");
|
||||
ok('patternMismatch' in validity, "validity.patternMismatch is not available in the DOM");
|
||||
ok('tooLong' in validity, "validity.tooLong is not available in the DOM");
|
||||
ok('rangeUnderflow' in validity, "validity.rangeUnderflow is not available in the DOM");
|
||||
|
@ -75,6 +76,7 @@ function checkConstraintValidationAPIDefaultValues(element)
|
|||
|
||||
ok(!element.validity.valueMissing, "The element should not suffer from a constraint validation");
|
||||
ok(!element.validity.typeMismatch, "The element should not suffer from a constraint validation");
|
||||
ok(!element.validity.badInput, "The element should not suffer from a constraint validation");
|
||||
ok(!element.validity.patternMismatch, "The element should not suffer from a constraint validation");
|
||||
ok(!element.validity.tooLong, "The element should not suffer from a constraint validation");
|
||||
ok(!element.validity.rangeUnderflow, "The element should not suffer from a constraint validation");
|
||||
|
@ -152,6 +154,10 @@ function checkSpecificWillValidate()
|
|||
ok(!i.willValidate, "Submit state input should be barred from constraint validation");
|
||||
is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
|
||||
"rgb(0, 0, 0)", "Nor :valid and :invalid should apply");
|
||||
i.type = "number";
|
||||
ok(i.willValidate, "Number state input should not be barred from constraint validation");
|
||||
is(window.getComputedStyle(i, null).getPropertyValue('background-color'),
|
||||
"rgb(0, 255, 0)", ":valid pseudo-class should apply");
|
||||
i.type = "";
|
||||
i.readOnly = 'true';
|
||||
ok(!i.willValidate, "Readonly input should be barred from constraint validation");
|
||||
|
@ -275,6 +281,8 @@ function checkValidityStateObjectAliveWithoutElement(element)
|
|||
"When the element is not alive, it shouldn't suffer from constraint validation");
|
||||
ok(!v.typeMismatch,
|
||||
"When the element is not alive, it shouldn't suffer from constraint validation");
|
||||
ok(!v.badInput,
|
||||
"When the element is not alive, it shouldn't suffer from constraint validation");
|
||||
ok(!v.patternMismatch,
|
||||
"When the element is not alive, it shouldn't suffer from constraint validation");
|
||||
ok(!v.tooLong,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* http://www.whatwg.org/specs/web-apps/current-work/#validitystate
|
||||
*/
|
||||
|
||||
[scriptable, uuid(5e62197a-9b74-4812-b5a2-ca102e886f7a)]
|
||||
[scriptable, uuid(00bed276-f1f7-492f-a039-dbd9b9efc10b)]
|
||||
interface nsIDOMValidityState : nsISupports
|
||||
{
|
||||
readonly attribute boolean valueMissing;
|
||||
|
@ -23,6 +23,7 @@ interface nsIDOMValidityState : nsISupports
|
|||
readonly attribute boolean rangeUnderflow;
|
||||
readonly attribute boolean rangeOverflow;
|
||||
readonly attribute boolean stepMismatch;
|
||||
readonly attribute boolean badInput;
|
||||
readonly attribute boolean customError;
|
||||
readonly attribute boolean valid;
|
||||
};
|
||||
|
|
|
@ -41,6 +41,7 @@ FormValidationRangeUnderflow=Please select a value that is higher than %S.
|
|||
FormValidationStepMismatch=Please select a valid value. The two nearest valid values are %S and %S.
|
||||
# LOCALIZATION NOTE (FormValidationStepMismatchOneValue): %S can be a number, a date or a time. This is called instead of FormValidationStepMismatch when the second value is the same as the first.
|
||||
FormValidationStepMismatchOneValue=Please select a valid value. The nearest valid value is %S.
|
||||
FormValidationBadInputNumber=Please enter a number.
|
||||
GetAttributeNodeWarning=Use of getAttributeNode() is deprecated. Use getAttribute() instead.
|
||||
SetAttributeNodeWarning=Use of setAttributeNode() is deprecated. Use setAttribute() instead.
|
||||
GetAttributeNodeNSWarning=Use of getAttributeNodeNS() is deprecated. Use getAttributeNS() instead.
|
||||
|
|
|
@ -18,7 +18,7 @@ interface ValidityState {
|
|||
readonly attribute boolean rangeUnderflow;
|
||||
readonly attribute boolean rangeOverflow;
|
||||
readonly attribute boolean stepMismatch;
|
||||
// readonly attribute boolean badInput;
|
||||
readonly attribute boolean badInput;
|
||||
readonly attribute boolean customError;
|
||||
readonly attribute boolean valid;
|
||||
};
|
||||
|
|
|
@ -595,6 +595,17 @@ nsNumberControlFrame::GetValueOfAnonTextControl(nsAString& aValue)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
nsNumberControlFrame::AnonTextControlIsEmpty()
|
||||
{
|
||||
if (!mTextField) {
|
||||
return true;
|
||||
}
|
||||
nsAutoString value;
|
||||
HTMLInputElement::FromContent(mTextField)->GetValue(value);
|
||||
return value.IsEmpty();
|
||||
}
|
||||
|
||||
Element*
|
||||
nsNumberControlFrame::GetPseudoElement(nsCSSPseudoElements::Type aType)
|
||||
{
|
||||
|
|
|
@ -95,6 +95,8 @@ public:
|
|||
*/
|
||||
void GetValueOfAnonTextControl(nsAString& aValue);
|
||||
|
||||
bool AnonTextControlIsEmpty();
|
||||
|
||||
/**
|
||||
* Called to notify this frame that its HTMLInputElement is currently
|
||||
* processing a DOM 'input' event.
|
||||
|
|
|
@ -41,6 +41,7 @@ FormValidationRangeUnderflow=Please select a value that is higher than %S.
|
|||
FormValidationStepMismatch=Please select a valid value. The two nearest valid values are %S and %S.
|
||||
# LOCALIZATION NOTE (FormValidationStepMismatchOneValue): %S can be a number, a date or a time. This is called instead of FormValidationStepMismatch when the second value is the same as the first.
|
||||
FormValidationStepMismatchOneValue=Please select a valid value. The nearest valid value is %S.
|
||||
FormValidationBadInputNumber=Please enter a number.
|
||||
GetAttributeNodeWarning=Use of getAttributeNode() is deprecated. Use getAttribute() instead.
|
||||
SetAttributeNodeWarning=Use of setAttributeNode() is deprecated. Use setAttribute() instead.
|
||||
GetAttributeNodeNSWarning=Use of getAttributeNodeNS() is deprecated. Use getAttributeNS() instead.
|
||||
|
|
Загрузка…
Ссылка в новой задаче