зеркало из https://github.com/mozilla/pjs.git
Bug 345822 - Implement required attribute. f=ajvincent r=smaug sr=sicking a2.0=blocking
This commit is contained in:
Родитель
d30156cc14
Коммит
da54ee6c6d
|
@ -798,6 +798,7 @@ GK_ATOM(removeelement, "removeelement")
|
|||
GK_ATOM(renderingobserverlist, "renderingobserverlist")
|
||||
GK_ATOM(repeat, "repeat")
|
||||
GK_ATOM(replace, "replace")
|
||||
GK_ATOM(required, "required")
|
||||
GK_ATOM(reset, "reset")
|
||||
GK_ATOM(resizeafter, "resizeafter")
|
||||
GK_ATOM(resizebefore, "resizebefore")
|
||||
|
|
|
@ -79,6 +79,8 @@ nsConstraintValidation::GetValidationMessage(nsAString & aValidationMessage,
|
|||
aValidationMessage.Assign(mCustomValidity);
|
||||
} else if (IsTooLong()) {
|
||||
GetValidationMessage(aValidationMessage, VALIDATION_MESSAGE_TOO_LONG);
|
||||
} else if (IsValueMissing()) {
|
||||
GetValidationMessage(aValidationMessage, VALIDATION_MESSAGE_VALUE_MISSING);
|
||||
} else {
|
||||
// TODO: The other messages have not been written
|
||||
// because related constraint validation are not implemented yet.
|
||||
|
|
|
@ -538,6 +538,7 @@ NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Multiple, multiple)
|
|||
NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLInputElement, MaxLength, maxlength)
|
||||
NS_IMPL_STRING_ATTR(nsHTMLInputElement, Name, name)
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLInputElement, ReadOnly, readonly)
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Required, required)
|
||||
NS_IMPL_URI_ATTR(nsHTMLInputElement, Src, src)
|
||||
NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLInputElement, TabIndex, tabindex, 0)
|
||||
NS_IMPL_STRING_ATTR(nsHTMLInputElement, UseMap, usemap)
|
||||
|
@ -1084,17 +1085,7 @@ nsHTMLInputElement::RadioSetChecked(PRBool aNotify)
|
|||
//
|
||||
// Find the selected radio button so we can deselect it
|
||||
//
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> currentlySelected;
|
||||
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
|
||||
// This is ONLY INITIALIZED IF container EXISTS
|
||||
nsAutoString name;
|
||||
PRBool nameExists = PR_FALSE;
|
||||
if (container) {
|
||||
nameExists = GetNameIfExists(name);
|
||||
if (nameExists) {
|
||||
container->GetCurrentRadioButton(name, getter_AddRefs(currentlySelected));
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> currentlySelected = GetSelectedRadioButton();
|
||||
|
||||
//
|
||||
// Deselect the currently selected radio button
|
||||
|
@ -1117,7 +1108,9 @@ nsHTMLInputElement::RadioSetChecked(PRBool aNotify)
|
|||
// Let the group know that we are now the One True Radio Button
|
||||
//
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (container && nameExists) {
|
||||
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
|
||||
nsAutoString name;
|
||||
if (container && GetNameIfExists(name)) {
|
||||
rv = container->SetCurrentRadioButton(name, this);
|
||||
}
|
||||
|
||||
|
@ -1139,6 +1132,25 @@ nsHTMLInputElement::GetRadioGroupContainer()
|
|||
return retval;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIDOMHTMLInputElement>
|
||||
nsHTMLInputElement::GetSelectedRadioButton()
|
||||
{
|
||||
nsIDOMHTMLInputElement* selected;
|
||||
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
|
||||
|
||||
if (!container) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsAutoString name;
|
||||
if (!GetNameIfExists(name)) {
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
container->GetCurrentRadioButton(name, &selected);
|
||||
return selected;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLInputElement::MaybeSubmitForm(nsPresContext* aPresContext)
|
||||
{
|
||||
|
@ -1502,16 +1514,8 @@ nsHTMLInputElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
|
|||
|
||||
case NS_FORM_INPUT_RADIO:
|
||||
{
|
||||
nsCOMPtr<nsIRadioGroupContainer> container = GetRadioGroupContainer();
|
||||
if (container) {
|
||||
nsAutoString name;
|
||||
if (GetNameIfExists(name)) {
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> selectedRadioButton;
|
||||
container->GetCurrentRadioButton(name,
|
||||
getter_AddRefs(selectedRadioButton));
|
||||
aVisitor.mItemData = selectedRadioButton;
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> selectedRadioButton = GetSelectedRadioButton();
|
||||
aVisitor.mItemData = selectedRadioButton;
|
||||
|
||||
originalCheckedValue = GetChecked();
|
||||
if (!originalCheckedValue) {
|
||||
|
@ -2958,6 +2962,113 @@ nsHTMLInputElement::VisitGroup(nsIRadioVisitor* aVisitor, PRBool aFlushContent)
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsHTMLInputElement::ValueModeType
|
||||
nsHTMLInputElement::GetValueMode() const
|
||||
{
|
||||
switch (mType)
|
||||
{
|
||||
case NS_FORM_INPUT_HIDDEN:
|
||||
case NS_FORM_INPUT_SUBMIT:
|
||||
case NS_FORM_INPUT_BUTTON:
|
||||
case NS_FORM_INPUT_RESET:
|
||||
case NS_FORM_INPUT_IMAGE:
|
||||
return VALUE_MODE_DEFAULT;
|
||||
case NS_FORM_INPUT_CHECKBOX:
|
||||
case NS_FORM_INPUT_RADIO:
|
||||
return VALUE_MODE_DEFAULT_ON;
|
||||
case NS_FORM_INPUT_FILE:
|
||||
return VALUE_MODE_FILENAME;
|
||||
#ifdef DEBUG
|
||||
case NS_FORM_INPUT_TEXT:
|
||||
case NS_FORM_INPUT_PASSWORD:
|
||||
case NS_FORM_INPUT_SEARCH:
|
||||
case NS_FORM_INPUT_TEL:
|
||||
return VALUE_MODE_VALUE;
|
||||
default:
|
||||
NS_NOTYETIMPLEMENTED("Unexpected input type in GetValueMode()");
|
||||
return VALUE_MODE_VALUE;
|
||||
#else // DEBUG
|
||||
default:
|
||||
return VALUE_MODE_VALUE;
|
||||
#endif // DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLInputElement::IsMutable() const
|
||||
{
|
||||
return !HasAttr(kNameSpaceID_None, nsGkAtoms::disabled) &&
|
||||
GetCurrentDoc() &&
|
||||
!(DoesReadOnlyApply() &&
|
||||
HasAttr(kNameSpaceID_None, nsGkAtoms::readonly));
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLInputElement::DoesReadOnlyApply() const
|
||||
{
|
||||
switch (mType)
|
||||
{
|
||||
case NS_FORM_INPUT_HIDDEN:
|
||||
case NS_FORM_INPUT_BUTTON:
|
||||
case NS_FORM_INPUT_IMAGE:
|
||||
case NS_FORM_INPUT_RESET:
|
||||
case NS_FORM_INPUT_SUBMIT:
|
||||
case NS_FORM_INPUT_RADIO:
|
||||
case NS_FORM_INPUT_FILE:
|
||||
case NS_FORM_INPUT_CHECKBOX:
|
||||
// TODO:
|
||||
// case NS_FORM_INPUT_COLOR:
|
||||
// case NS_FORM_INPUT_RANGE:
|
||||
return PR_FALSE;
|
||||
#ifdef DEBUG
|
||||
case NS_FORM_INPUT_TEXT:
|
||||
case NS_FORM_INPUT_PASSWORD:
|
||||
case NS_FORM_INPUT_SEARCH:
|
||||
case NS_FORM_INPUT_TEL:
|
||||
return PR_TRUE;
|
||||
default:
|
||||
NS_NOTYETIMPLEMENTED("Unexpected input type in DoesReadOnlyApply()");
|
||||
return PR_TRUE;
|
||||
#else // DEBUG
|
||||
default:
|
||||
return PR_TRUE;
|
||||
#endif // DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLInputElement::DoesRequiredApply() const
|
||||
{
|
||||
switch (mType)
|
||||
{
|
||||
case NS_FORM_INPUT_HIDDEN:
|
||||
case NS_FORM_INPUT_BUTTON:
|
||||
case NS_FORM_INPUT_IMAGE:
|
||||
case NS_FORM_INPUT_RESET:
|
||||
case NS_FORM_INPUT_SUBMIT:
|
||||
// TODO:
|
||||
// case NS_FORM_INPUT_COLOR:
|
||||
// case NS_FORM_INPUT_RANGE:
|
||||
return PR_FALSE;
|
||||
#ifdef DEBUG
|
||||
case NS_FORM_INPUT_RADIO:
|
||||
case NS_FORM_INPUT_CHECKBOX:
|
||||
case NS_FORM_INPUT_FILE:
|
||||
case NS_FORM_INPUT_TEXT:
|
||||
case NS_FORM_INPUT_PASSWORD:
|
||||
case NS_FORM_INPUT_SEARCH:
|
||||
case NS_FORM_INPUT_TEL:
|
||||
return PR_TRUE;
|
||||
default:
|
||||
NS_NOTYETIMPLEMENTED("Unexpected input type in DoesRequiredApply()");
|
||||
return PR_TRUE;
|
||||
#else // DEBUG
|
||||
default:
|
||||
return PR_TRUE;
|
||||
#endif // DEBUG
|
||||
}
|
||||
}
|
||||
|
||||
// nsConstraintValidation
|
||||
|
||||
PRBool
|
||||
|
@ -2976,6 +3087,44 @@ nsHTMLInputElement::IsTooLong()
|
|||
return (maxLength >= 0) && (textLength > maxLength);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLInputElement::IsValueMissing()
|
||||
{
|
||||
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) ||
|
||||
!DoesRequiredApply()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
if (GetValueMode() == VALUE_MODE_VALUE) {
|
||||
if (!IsMutable()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsAutoString value;
|
||||
nsresult rv = GetValue(value);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
return value.IsEmpty();
|
||||
}
|
||||
|
||||
if (mType == NS_FORM_INPUT_CHECKBOX) {
|
||||
return !GetChecked();
|
||||
}
|
||||
|
||||
if (mType == NS_FORM_INPUT_RADIO) {
|
||||
nsCOMPtr<nsIDOMHTMLInputElement> selected = GetSelectedRadioButton();
|
||||
return !selected;
|
||||
}
|
||||
|
||||
if (mType == NS_FORM_INPUT_FILE) {
|
||||
nsCOMArray<nsIFile> files;
|
||||
GetFileArray(files);
|
||||
return !files.Count();
|
||||
}
|
||||
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLInputElement::IsBarredFromConstraintValidation()
|
||||
{
|
||||
|
@ -3014,6 +3163,29 @@ nsHTMLInputElement::GetValidationMessage(nsAString& aValidationMessage,
|
|||
aValidationMessage = message;
|
||||
break;
|
||||
}
|
||||
case VALIDATION_MESSAGE_VALUE_MISSING:
|
||||
{
|
||||
nsXPIDLString message;
|
||||
nsCAutoString key;
|
||||
switch (mType)
|
||||
{
|
||||
case NS_FORM_INPUT_FILE:
|
||||
key.Assign("FileElementSuffersFromBeingMissing");
|
||||
break;
|
||||
case NS_FORM_INPUT_CHECKBOX:
|
||||
key.Assign("CheckboxElementSuffersFromBeingMissing");
|
||||
break;
|
||||
case NS_FORM_INPUT_RADIO:
|
||||
key.Assign("RadioElementSuffersFromBeingMissing");
|
||||
break;
|
||||
default:
|
||||
key.Assign("TextElementSuffersFromBeingMissing");
|
||||
}
|
||||
rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
||||
key.get(), message);
|
||||
aValidationMessage = message;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
rv = nsConstraintValidation::GetValidationMessage(aValidationMessage, aType);
|
||||
}
|
||||
|
|
|
@ -188,6 +188,15 @@ public:
|
|||
*/
|
||||
virtual already_AddRefed<nsIRadioGroupContainer> GetRadioGroupContainer();
|
||||
|
||||
/**
|
||||
* Helper function returning the currently selected button in the radio group.
|
||||
* Returning null if the element is not a button or if there is no selectied
|
||||
* button in the group.
|
||||
*
|
||||
* @return the selected button (or null).
|
||||
*/
|
||||
already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton();
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
|
||||
virtual void UpdateEditableState()
|
||||
|
@ -204,6 +213,7 @@ public:
|
|||
|
||||
// nsConstraintValidation
|
||||
PRBool IsTooLong();
|
||||
PRBool IsValueMissing();
|
||||
PRBool IsBarredFromConstraintValidation();
|
||||
nsresult GetValidationMessage(nsAString& aValidationMessage,
|
||||
ValidationMessageType aType);
|
||||
|
@ -213,6 +223,29 @@ protected:
|
|||
// by the nsITextControlElement version.
|
||||
using nsGenericHTMLFormElement::IsSingleLineTextControl;
|
||||
|
||||
/**
|
||||
* The ValueModeType specifies how the value IDL attribute should behave.
|
||||
*
|
||||
* See: http://dev.w3.org/html5/spec/forms.html#dom-input-value
|
||||
*/
|
||||
enum ValueModeType
|
||||
{
|
||||
// On getting, returns the value.
|
||||
// On setting, sets value.
|
||||
VALUE_MODE_VALUE,
|
||||
// On getting, returns the value if present or the empty string.
|
||||
// On setting, sets the value.
|
||||
VALUE_MODE_DEFAULT,
|
||||
// On getting, returns the value if present or "on".
|
||||
// On setting, sets the value.
|
||||
VALUE_MODE_DEFAULT_ON,
|
||||
// On getting, returns "C:\fakepath\" followed by the file name of the
|
||||
// first file of the selected files if any.
|
||||
// On setting the empty string, empties the selected files list, otherwise
|
||||
// throw the INVALID_STATE_ERR exception.
|
||||
VALUE_MODE_FILENAME
|
||||
};
|
||||
|
||||
// Helper method
|
||||
nsresult SetValueInternal(const nsAString& aValue,
|
||||
PRBool aUserInput,
|
||||
|
@ -330,6 +363,30 @@ protected:
|
|||
*/
|
||||
PRBool NeedToInitializeEditorForEvent(nsEventChainPreVisitor& aVisitor) const;
|
||||
|
||||
/**
|
||||
* Get the value mode of the element, depending of the type.
|
||||
*/
|
||||
ValueModeType GetValueMode() const;
|
||||
|
||||
/**
|
||||
* Get the mutable state of the element.
|
||||
* When the element isn't mutable (immutable), the value or checkedness
|
||||
* should not be changed by the user.
|
||||
*
|
||||
* See: http://dev.w3.org/html5/spec/forms.html#concept-input-mutable
|
||||
*/
|
||||
PRBool IsMutable() const;
|
||||
|
||||
/**
|
||||
* Returns if the readonly attribute applies for the current type.
|
||||
*/
|
||||
PRBool DoesReadOnlyApply() const;
|
||||
|
||||
/**
|
||||
* Returns if the required attribute applies for the current type.
|
||||
*/
|
||||
PRBool DoesRequiredApply() const;
|
||||
|
||||
void FreeData();
|
||||
nsTextEditorState *GetEditorState() const;
|
||||
|
||||
|
|
|
@ -201,6 +201,7 @@ public:
|
|||
|
||||
// nsConstraintValidation
|
||||
PRBool IsTooLong();
|
||||
PRBool IsValueMissing();
|
||||
PRBool IsBarredFromConstraintValidation();
|
||||
nsresult GetValidationMessage(nsAString& aValidationMessage,
|
||||
ValidationMessageType aType);
|
||||
|
@ -251,6 +252,11 @@ protected:
|
|||
|
||||
virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName,
|
||||
const nsAString* aValue, PRBool aNotify);
|
||||
|
||||
/**
|
||||
* Get the mutable state of the element.
|
||||
*/
|
||||
PRBool IsMutable() const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -409,6 +415,7 @@ NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, Disabled, disabled)
|
|||
NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLTextAreaElement, MaxLength, maxlength)
|
||||
NS_IMPL_STRING_ATTR(nsHTMLTextAreaElement, Name, name)
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, ReadOnly, readonly)
|
||||
NS_IMPL_BOOL_ATTR(nsHTMLTextAreaElement, Required, required)
|
||||
NS_IMPL_INT_ATTR(nsHTMLTextAreaElement, Rows, rows)
|
||||
NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLTextAreaElement, TabIndex, tabindex, 0)
|
||||
NS_IMPL_STRING_ATTR(nsHTMLTextAreaElement, Wrap, wrap)
|
||||
|
@ -1044,6 +1051,13 @@ nsHTMLTextAreaElement::CopyInnerTo(nsGenericElement* aDest) const
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLTextAreaElement::IsMutable() const
|
||||
{
|
||||
return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) &&
|
||||
!HasAttr(kNameSpaceID_None, nsGkAtoms::disabled));
|
||||
}
|
||||
|
||||
// nsConstraintValidation
|
||||
|
||||
PRBool
|
||||
|
@ -1062,6 +1076,20 @@ nsHTMLTextAreaElement::IsTooLong()
|
|||
return (maxLength >= 0) && (textLength > maxLength);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLTextAreaElement::IsValueMissing()
|
||||
{
|
||||
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required) || !IsMutable()) {
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
||||
nsAutoString value;
|
||||
nsresult rv = GetValue(value);
|
||||
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
||||
|
||||
return value.IsEmpty();
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsHTMLTextAreaElement::IsBarredFromConstraintValidation()
|
||||
{
|
||||
|
@ -1097,6 +1125,15 @@ nsHTMLTextAreaElement::GetValidationMessage(nsAString& aValidationMessage,
|
|||
aValidationMessage = message;
|
||||
}
|
||||
break;
|
||||
case VALIDATION_MESSAGE_VALUE_MISSING:
|
||||
{
|
||||
nsXPIDLString message;
|
||||
rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
||||
"TextElementSuffersFromBeingMissing",
|
||||
message);
|
||||
aValidationMessage = message;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rv = nsConstraintValidation::GetValidationMessage(aValidationMessage, aType);
|
||||
}
|
||||
|
|
|
@ -194,6 +194,7 @@ _TEST_FILES = \
|
|||
test_bug345624-1.html \
|
||||
test_bug345624-2.html \
|
||||
test_bug561640.html \
|
||||
test_bug345822.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
|
|
@ -0,0 +1,313 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=345822
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 345822</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=345822">Mozilla Bug 345822</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<form>
|
||||
<input name="input" id='i'>
|
||||
<input name="input" id='r' type='radio'>
|
||||
<textarea name="textarea" id='t'></textarea>
|
||||
</form>
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 345822 **/
|
||||
|
||||
function checkRequiredAttribute(element)
|
||||
{
|
||||
ok('required' in element, "Element should have the required attribute");
|
||||
|
||||
ok(!element.required, "Element required attribute should be disabled");
|
||||
is(element.getAttribute('required'), null,
|
||||
"Element required attribute should be disabled");
|
||||
|
||||
element.required = true;
|
||||
ok(element.required, "Element required attribute should be enabled");
|
||||
isnot(element.getAttribute('required'), null,
|
||||
"Element required attribute should be enabled");
|
||||
|
||||
element.removeAttribute('required');
|
||||
element.setAttribute('required', '');
|
||||
ok(element.required, "Element required attribute should be enabled");
|
||||
isnot(element.getAttribute('required'), null,
|
||||
"Element required attribute should be enabled");
|
||||
|
||||
element.removeAttribute('required');
|
||||
ok(!element.required, "Element required attribute should be disabled");
|
||||
is(element.getAttribute('required'), null,
|
||||
"Element required attribute should be disabled");
|
||||
}
|
||||
|
||||
function checkNotSufferingFromBeingMissing(element)
|
||||
{
|
||||
ok(!element.validity.valueMissing,
|
||||
"Element should not suffer from value missing");
|
||||
ok(element.validity.valid, "Element should be valid");
|
||||
ok(element.checkValidity(), "Element should be valid");
|
||||
is(element.validationMessage, "",
|
||||
"Validation message should be the empty string");
|
||||
}
|
||||
|
||||
function checkSufferingFromBeingMissing(element)
|
||||
{
|
||||
ok(element.validity.valueMissing, "Element should suffer from value missing");
|
||||
ok(!element.validity.valid, "Element should not be valid");
|
||||
ok(!element.checkValidity(), "Element should not be valid");
|
||||
|
||||
if (element.type == 'checkbox')
|
||||
{
|
||||
is(element.validationMessage,
|
||||
"This checkbox is mandatory, you have to check it.",
|
||||
"Validation message is wrong");
|
||||
}
|
||||
else if (element.type == 'radio')
|
||||
{
|
||||
is(element.validationMessage,
|
||||
"You have to select one of these options.",
|
||||
"Validation message is wrong");
|
||||
}
|
||||
else if (input .type == 'file')
|
||||
{
|
||||
is(element.validationMessage,
|
||||
"You have to select a file.",
|
||||
"Validation message is wrong");
|
||||
}
|
||||
else // text fields
|
||||
{
|
||||
is(element.validationMessage,
|
||||
"This field is mandatory, you have to fill it.",
|
||||
"Validation message is wrong");
|
||||
}
|
||||
}
|
||||
|
||||
function checkTextareaRequiredValidity(element)
|
||||
{
|
||||
element.value = '';
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = true;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.readOnly = true;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.readOnly = false;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.value = 'foo';
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.value = '';
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
}
|
||||
|
||||
function checkInputRequiredNotApply(element)
|
||||
{
|
||||
element.value = '';
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = true;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = false;
|
||||
}
|
||||
|
||||
function checkInputRequiredValidity(element)
|
||||
{
|
||||
element.value = '';
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = true;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.readOnly = true;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.readOnly = false;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.value = 'foo';
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.value = '';
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
}
|
||||
|
||||
function checkInputRequiredValidityForCheckbox(element)
|
||||
{
|
||||
element.checked = false;
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = true;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.checked = true;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.checked = false;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
}
|
||||
|
||||
function checkInputRequiredValidityForRadio(element)
|
||||
{
|
||||
element.checked = false;
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = true;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.checked = true;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.checked = false;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
// A required radio button should not suffer from value missing if another
|
||||
// radio button from the same group is checked.
|
||||
var element2 = document.createElement('input');//document.getElementById('r');
|
||||
document.forms[0].appendChild(element2);
|
||||
element2.name = 'input';
|
||||
element2.type = 'radio';
|
||||
element2.checked = false;
|
||||
element2.required = false;
|
||||
|
||||
element.checked = false;
|
||||
element.required = true;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element2.checked = true;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
// The other radio button should not be disabled.
|
||||
// A disabled checked radio button in the radio group
|
||||
// is enough to not suffer from value missing.
|
||||
element2.disabled = true;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
// If a radio button is not required but another radio button is required in
|
||||
// the same group, the not required radio button should not suffer from value
|
||||
// missing.
|
||||
element2.disabled = false;
|
||||
element2.checked = false;
|
||||
element.required = false;
|
||||
element2.required = true;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
checkSufferingFromBeingMissing(element2);
|
||||
}
|
||||
|
||||
function checkInputRequiredValidityForFile(element)
|
||||
{
|
||||
function createFileWithData(fileName, fileData) {
|
||||
var dirSvc = Components.classes["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Components.interfaces.nsIProperties);
|
||||
var testFile = dirSvc.get("ProfD", Components.interfaces.nsIFile);
|
||||
testFile.append(fileName);
|
||||
var outStream = Components.
|
||||
classes["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Components.interfaces.nsIFileOutputStream);
|
||||
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
|
||||
0666, 0);
|
||||
outStream.write(fileData, fileData.length);
|
||||
outStream.close();
|
||||
|
||||
return testFile;
|
||||
}
|
||||
|
||||
// Need privileges to set a filename with .value.
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
var file = createFileWithData("345822_file", "file content");
|
||||
|
||||
element.value = "";
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = true;
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.value = file.path;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
element.value = "";
|
||||
checkSufferingFromBeingMissing(element);
|
||||
|
||||
element.required = false;
|
||||
checkNotSufferingFromBeingMissing(element);
|
||||
|
||||
file.remove(false);
|
||||
}
|
||||
|
||||
var textarea = document.getElementById('t');
|
||||
var input = document.getElementById('i');
|
||||
|
||||
checkRequiredAttribute(textarea);
|
||||
checkTextareaRequiredValidity(textarea);
|
||||
|
||||
// Every input element should have the required attribute.
|
||||
checkRequiredAttribute(input);
|
||||
|
||||
// The required attribute behavior depend of the input type.
|
||||
// First of all, checks for the types which do not use the required attribute.
|
||||
// TODO: check 'color' and 'range' when they will be implemented.
|
||||
input.type = 'hidden';
|
||||
checkInputRequiredNotApply(input);
|
||||
input.type = 'submit';
|
||||
checkInputRequiredNotApply(input);
|
||||
input.type = 'button';
|
||||
checkInputRequiredNotApply(input);
|
||||
input.type = 'reset';
|
||||
checkInputRequiredNotApply(input);
|
||||
input.type = 'image';
|
||||
checkInputRequiredNotApply(input);
|
||||
|
||||
// Now, checking for all types which accept the required attribute.
|
||||
// TODO: check 'url', 'email', 'datetime', 'date',
|
||||
// 'month', 'week', 'time', 'datetime-local' and 'number'
|
||||
// when they will be implemented.
|
||||
input.type = 'text';
|
||||
checkInputRequiredValidity(input);
|
||||
input.type = 'password';
|
||||
checkInputRequiredValidity(input);
|
||||
input.type = 'checkbox';
|
||||
checkInputRequiredValidityForCheckbox(input);
|
||||
input.type = 'radio';
|
||||
checkInputRequiredValidityForRadio(input);
|
||||
input.type = 'file';
|
||||
checkInputRequiredValidityForFile(input);
|
||||
input.type = 'search';
|
||||
checkInputRequiredValidity(input);
|
||||
input.type = 'tel';
|
||||
checkInputRequiredValidity(input);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -51,7 +51,7 @@ interface nsIDOMValidityState;
|
|||
* http://www.w3.org/TR/DOM-Level-2-HTML/
|
||||
*/
|
||||
|
||||
[scriptable, uuid(0aac61a3-6cce-4cae-b73c-b0bacd5d3692)]
|
||||
[scriptable, uuid(ed3f8a4a-9894-4120-a7e6-4a18f3d8effd)]
|
||||
interface nsIDOMHTMLInputElement : nsIDOMHTMLElement
|
||||
{
|
||||
attribute DOMString accept;
|
||||
|
@ -74,6 +74,7 @@ interface nsIDOMHTMLInputElement : nsIDOMHTMLElement
|
|||
|
||||
attribute DOMString placeholder;
|
||||
attribute boolean readOnly;
|
||||
attribute boolean required;
|
||||
|
||||
attribute DOMString accessKey;
|
||||
attribute DOMString align;
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
interface nsIControllers;
|
||||
interface nsIDOMValidityState;
|
||||
|
||||
[scriptable, uuid(2a7243d5-7958-4394-a063-64c68efa4a32)]
|
||||
[scriptable, uuid(df5dc23d-cf90-443e-bf56-cd3af021ebc7)]
|
||||
interface nsIDOMNSHTMLTextAreaElement : nsISupports
|
||||
{
|
||||
attribute boolean autofocus;
|
||||
|
@ -53,6 +53,7 @@ interface nsIDOMNSHTMLTextAreaElement : nsISupports
|
|||
attribute long selectionEnd;
|
||||
attribute long maxLength;
|
||||
attribute DOMString placeholder;
|
||||
attribute boolean required;
|
||||
|
||||
/**
|
||||
* Reflects the wrap content attribute. Possible values are "soft, "hard" and
|
||||
|
|
|
@ -64,4 +64,8 @@ WrongEventPropertyAccessWarning=The '%S' property of a %S event should not be us
|
|||
SpeculationFailed=An unbalanced tree was written using document.write() causing data from the network to be reparsed. For more information https://developer.mozilla.org/en/Optimizing_Your_Pages_for_Speculative_Parsing
|
||||
DocumentWriteIgnored=A call to document.write() from an asynchronously-loaded external script was ignored.
|
||||
ElementSuffersFromBeingTooLong=The text is too long. It is %S characters long and the limit is %S.
|
||||
TextElementSuffersFromBeingMissing=This field is mandatory, you have to fill it.
|
||||
CheckboxElementSuffersFromBeingMissing=This checkbox is mandatory, you have to check it.
|
||||
RadioElementSuffersFromBeingMissing=You have to select one of these options.
|
||||
FileElementSuffersFromBeingMissing=You have to select a file.
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче