Bug 596511 (3/3 - <select> can be invalid if no option is selected or all of them have empty value. r=sicking a=blocking-betaN

This commit is contained in:
Mounir Lamouri 2010-11-17 00:41:19 +01:00
parent c4a2f1de10
commit 4db1d8847f
19 changed files with 474 additions and 45 deletions

View File

@ -48,7 +48,7 @@ interface nsIDOMHTMLOptionElement;
* OPTIONs within a SELECT element.
*/
[scriptable, uuid(35bd8ed5-5f34-4126-8c4f-38ba01681836)]
[scriptable, uuid(aa73a61a-8ef2-402d-b86c-3a5c5f2a6027)]
interface nsISelectElement : nsISupports
{
@ -64,7 +64,8 @@ interface nsISelectElement : nsISupports
*/
[noscript] void willAddOptions(in nsIContent aOptions,
in nsIContent aParent,
in long aContentIndex);
in long aContentIndex,
in boolean aNotify);
/**
* To be called when stuff is removed under a child of the select--but
@ -75,7 +76,8 @@ interface nsISelectElement : nsISupports
* parent is an optgroup, the index within the optgroup)
*/
[noscript] void willRemoveOptions(in nsIContent aParent,
in long aContentIndex);
in long aContentIndex,
in boolean aNotify);
/**
* Checks whether an option is disabled (even if it's part of an optgroup)

View File

@ -175,7 +175,7 @@ nsHTMLOptGroupElement::InsertChildAt(nsIContent* aKid,
PRUint32 aIndex,
PRBool aNotify)
{
nsSafeOptionListMutation safeMutation(GetSelect(), this, aKid, aIndex);
nsSafeOptionListMutation safeMutation(GetSelect(), this, aKid, aIndex, aNotify);
nsresult rv = nsGenericHTMLElement::InsertChildAt(aKid, aIndex, aNotify);
if (NS_FAILED(rv)) {
safeMutation.MutationFailed();
@ -186,7 +186,7 @@ nsHTMLOptGroupElement::InsertChildAt(nsIContent* aKid,
nsresult
nsHTMLOptGroupElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationEvent)
{
nsSafeOptionListMutation safeMutation(GetSelect(), this, nsnull, aIndex);
nsSafeOptionListMutation safeMutation(GetSelect(), this, nsnull, aIndex, aNotify);
nsresult rv = nsGenericHTMLElement::RemoveChildAt(aIndex, aNotify, aMutationEvent);
if (NS_FAILED(rv)) {
safeMutation.MutationFailed();

View File

@ -86,7 +86,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsSelectState, NS_SELECT_STATE_IID)
nsSafeOptionListMutation::nsSafeOptionListMutation(nsIContent* aSelect,
nsIContent* aParent,
nsIContent* aKid,
PRUint32 aIndex)
PRUint32 aIndex,
PRBool aNotify)
: mSelect(do_QueryInterface(aSelect)), mTopLevelMutation(PR_FALSE),
mNeedsRebuild(PR_FALSE)
{
@ -104,9 +105,9 @@ nsSafeOptionListMutation::nsSafeOptionListMutation(nsIContent* aSelect,
}
nsresult rv;
if (aKid) {
rv = mSelect->WillAddOptions(aKid, aParent, aIndex);
rv = mSelect->WillAddOptions(aKid, aParent, aIndex, aNotify);
} else {
rv = mSelect->WillRemoveOptions(aParent, aIndex);
rv = mSelect->WillRemoveOptions(aParent, aIndex, aNotify);
}
mNeedsRebuild = NS_FAILED(rv);
}
@ -226,7 +227,7 @@ nsHTMLSelectElement::InsertChildAt(nsIContent* aKid,
PRUint32 aIndex,
PRBool aNotify)
{
nsSafeOptionListMutation safeMutation(this, this, aKid, aIndex);
nsSafeOptionListMutation safeMutation(this, this, aKid, aIndex, aNotify);
nsresult rv = nsGenericHTMLFormElement::InsertChildAt(aKid, aIndex, aNotify);
if (NS_FAILED(rv)) {
safeMutation.MutationFailed();
@ -238,7 +239,7 @@ nsresult
nsHTMLSelectElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMutationEvent)
{
NS_ASSERTION(aMutationEvent, "Someone tried to inhibit mutations on select child removal.");
nsSafeOptionListMutation safeMutation(this, this, nsnull, aIndex);
nsSafeOptionListMutation safeMutation(this, this, nsnull, aIndex, aNotify);
nsresult rv = nsGenericHTMLFormElement::RemoveChildAt(aIndex, aNotify, aMutationEvent);
if (NS_FAILED(rv)) {
safeMutation.MutationFailed();
@ -252,7 +253,8 @@ nsHTMLSelectElement::RemoveChildAt(PRUint32 aIndex, PRBool aNotify, PRBool aMuta
nsresult
nsHTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
PRInt32 aListIndex,
PRInt32 aDepth)
PRInt32 aDepth,
PRBool aNotify)
{
PRInt32 insertIndex = aListIndex;
nsresult rv = InsertOptionsIntoListRecurse(aOptions, &insertIndex, aDepth);
@ -306,7 +308,7 @@ nsHTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
}
}
CheckSelectSomething();
CheckSelectSomething(aNotify);
}
return NS_OK;
@ -315,7 +317,8 @@ nsHTMLSelectElement::InsertOptionsIntoList(nsIContent* aOptions,
nsresult
nsHTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
PRInt32 aListIndex,
PRInt32 aDepth)
PRInt32 aDepth,
PRBool aNotify)
{
PRInt32 numRemoved = 0;
nsresult rv = RemoveOptionsFromListRecurse(aOptions, aListIndex, &numRemoved,
@ -347,7 +350,20 @@ nsHTMLSelectElement::RemoveOptionsFromList(nsIContent* aOptions,
// Select something in case we removed the selected option on a
// single select
CheckSelectSomething();
if (!CheckSelectSomething(aNotify) && mSelectedIndex == -1) {
// Update the validity state in case of we've just removed the last
// option.
UpdateValueMissingValidityState();
if (aNotify) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
NS_EVENT_STATE_INVALID);
}
}
}
}
return NS_OK;
@ -455,7 +471,8 @@ nsHTMLSelectElement::RemoveOptionsFromListRecurse(nsIContent* aOptions,
NS_IMETHODIMP
nsHTMLSelectElement::WillAddOptions(nsIContent* aOptions,
nsIContent* aParent,
PRInt32 aContentIndex)
PRInt32 aContentIndex,
PRBool aNotify)
{
PRInt32 level = GetContentDepth(aParent);
if (level == -1) {
@ -490,12 +507,13 @@ nsHTMLSelectElement::WillAddOptions(nsIContent* aOptions,
}
}
return InsertOptionsIntoList(aOptions, ind, level);
return InsertOptionsIntoList(aOptions, ind, level, aNotify);
}
NS_IMETHODIMP
nsHTMLSelectElement::WillRemoveOptions(nsIContent* aParent,
PRInt32 aContentIndex)
PRInt32 aContentIndex,
PRBool aNotify)
{
PRInt32 level = GetContentDepth(aParent);
NS_ASSERTION(level >= 0, "getting notified by unexpected content");
@ -516,7 +534,7 @@ nsHTMLSelectElement::WillRemoveOptions(nsIContent* aParent,
ind = GetFirstOptionIndex(currentKid);
}
if (ind != -1) {
nsresult rv = RemoveOptionsFromList(currentKid, ind, level);
nsresult rv = RemoveOptionsFromList(currentKid, ind, level, aNotify);
NS_ENSURE_SUCCESS(rv, rv);
}
}
@ -857,6 +875,16 @@ nsHTMLSelectElement::OnOptionSelected(nsISelectControlFrame* aSelectFrame,
if (aSelectFrame) {
aSelectFrame->OnOptionSelected(aIndex, aSelected);
}
UpdateValueMissingValidityState();
if (aNotify) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
NS_EVENT_STATE_INVALID);
}
}
}
void
@ -1083,7 +1111,7 @@ nsHTMLSelectElement::SetOptionsSelectedByIndex(PRInt32 aStartIndex,
// Make sure something is selected unless we were set to -1 (none)
if (optionsDeselected && aStartIndex != -1) {
optionsSelected = CheckSelectSomething() || optionsSelected;
optionsSelected = CheckSelectSomething(aNotify) || optionsSelected;
}
// Let the caller know whether anything was changed
@ -1261,18 +1289,18 @@ nsHTMLSelectElement::NamedItem(const nsAString& aName,
}
PRBool
nsHTMLSelectElement::CheckSelectSomething()
nsHTMLSelectElement::CheckSelectSomething(PRBool aNotify)
{
if (mIsDoneAddingChildren) {
if (mSelectedIndex < 0 && IsCombobox()) {
return SelectSomething();
return SelectSomething(aNotify);
}
}
return PR_FALSE;
}
PRBool
nsHTMLSelectElement::SelectSomething()
nsHTMLSelectElement::SelectSomething(PRBool aNotify)
{
// If we're not done building the select, don't play with this yet.
if (!mIsDoneAddingChildren) {
@ -1288,6 +1316,17 @@ nsHTMLSelectElement::SelectSomething()
if (NS_FAILED(rv) || !disabled) {
rv = SetSelectedIndex(i);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
UpdateValueMissingValidityState();
if (aNotify) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
NS_EVENT_STATE_INVALID);
}
}
return PR_TRUE;
}
}
@ -1329,15 +1368,23 @@ nsresult
nsHTMLSelectElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAString* aValue, PRBool aNotify)
{
if (aName == nsGkAtoms::disabled && aNameSpaceID == kNameSpaceID_None) {
UpdateBarredFromConstraintValidation();
if (aNotify) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
doc->ContentStatesChanged(this, nsnull, NS_EVENT_STATE_VALID |
NS_EVENT_STATE_INVALID);
}
nsEventStates states;
if (aNameSpaceID == kNameSpaceID_None) {
if (aName == nsGkAtoms::disabled) {
UpdateBarredFromConstraintValidation();
states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
} else if (aName == nsGkAtoms::required) {
UpdateValueMissingValidityState();
states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID;
}
}
if (aNotify && !states.IsEmpty()) {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
MOZ_AUTO_DOC_UPDATE(doc, UPDATE_CONTENT_STATE, PR_TRUE);
doc->ContentStatesChanged(this, nsnull, states);
}
}
@ -1370,7 +1417,7 @@ nsHTMLSelectElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
aAttribute == nsGkAtoms::multiple) {
// We might have become a combobox; make sure _something_ gets
// selected in that case
CheckSelectSomething();
CheckSelectSomething(aNotify);
}
return rv;
@ -1408,7 +1455,12 @@ nsHTMLSelectElement::DoneAddingChildren(PRBool aHaveNotified)
// Now that we're done, select something (if it's a single select something
// must be selected)
CheckSelectSomething();
if (!CheckSelectSomething(PR_FALSE)) {
// If an option has @selected set, it will be selected during parsing but
// with an empty value. We have to make sure the select element updates it's
// validity state to take this into account.
UpdateValueMissingValidityState();
}
return NS_OK;
}
@ -1639,7 +1691,7 @@ nsHTMLSelectElement::Reset()
// If nothing was selected and it's not multiple, select something
//
if (numSelected == 0 && IsCombobox()) {
SelectSomething();
SelectSomething(PR_TRUE);
}
//
@ -1776,6 +1828,75 @@ nsHTMLSelectElement::RebuildOptionsArray()
FindSelectedIndex(0);
}
bool
nsHTMLSelectElement::IsValueMissing()
{
if (!HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
return false;
}
PRUint32 length;
nsIDOMHTMLOptionElement* option = nsnull;
PRBool disabled;
PRBool selected;
mOptions->GetLength(&length);
for (PRUint32 i=0; i<length; ++i) {
option = mOptions->ItemAsOption(i);
NS_ENSURE_SUCCESS(option->GetSelected(&selected), false);
if (!selected) {
continue;
}
IsOptionDisabled(i, &disabled);
if (disabled) {
continue;
}
nsAutoString value;
NS_ENSURE_SUCCESS(option->GetValue(value), false);
if (!value.IsEmpty()) {
return false;
}
}
return true;
}
void
nsHTMLSelectElement::UpdateValueMissingValidityState()
{
SetValidityState(VALIDITY_STATE_VALUE_MISSING, IsValueMissing());
}
nsresult
nsHTMLSelectElement::GetValidationMessage(nsAString& aValidationMessage,
ValidityStateType aType)
{
nsresult rv = NS_OK;
switch (aType)
{
case VALIDITY_STATE_VALUE_MISSING:
{
nsXPIDLString message;
rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
"FormValidationSelectMissing",
message);
aValidationMessage = message;
}
break;
default:
rv = nsIConstraintValidation::GetValidationMessage(aValidationMessage, aType);
}
return rv;
}
#ifdef DEBUG
static void

View File

@ -215,7 +215,7 @@ public:
* @param aIndex The index of the content object in the parent.
*/
nsSafeOptionListMutation(nsIContent* aSelect, nsIContent* aParent,
nsIContent* aKid, PRUint32 aIndex);
nsIContent* aKid, PRUint32 aIndex, PRBool aNotify);
~nsSafeOptionListMutation();
void MutationFailed() { mNeedsRebuild = PR_TRUE; }
private:
@ -330,7 +330,8 @@ public:
virtual nsXPCClassInfo* GetClassInfo();
// nsIConstraintValidation
void UpdateBarredFromConstraintValidation();
nsresult GetValidationMessage(nsAString& aValidationMessage,
ValidityStateType aType);
protected:
friend class nsSafeOptionListMutation;
@ -352,13 +353,13 @@ protected:
* Select some option if possible (generally the first non-disabled option).
* @return true if something was selected, false otherwise
*/
PRBool SelectSomething();
PRBool SelectSomething(PRBool aNotify);
/**
* Call SelectSomething(), but only if nothing is selected
* @see SelectSomething()
* @return true if something was selected, false otherwise
*/
PRBool CheckSelectSomething();
PRBool CheckSelectSomething(PRBool aNotify);
/**
* Called to trigger notifications of frames and fixing selected index
*
@ -390,7 +391,8 @@ protected:
*/
nsresult InsertOptionsIntoList(nsIContent* aOptions,
PRInt32 aListIndex,
PRInt32 aDepth);
PRInt32 aDepth,
PRBool aNotify);
/**
* Remove option(s) from the options[] array
* @param aOptions the option or optgroup being added
@ -399,7 +401,8 @@ protected:
*/
nsresult RemoveOptionsFromList(nsIContent* aOptions,
PRInt32 aListIndex,
PRInt32 aDepth);
PRInt32 aDepth,
PRBool aNotify);
/**
* Insert option(s) into the options[] array (called by InsertOptionsIntoList)
* @param aOptions the option or optgroup being added
@ -420,6 +423,12 @@ protected:
PRInt32 aRemoveIndex,
PRInt32* aNumRemoved,
PRInt32 aDepth);
// nsIConstraintValidation
void UpdateBarredFromConstraintValidation();
bool IsValueMissing();
void UpdateValueMissingValidityState();
/**
* Find out how deep this content is from the select (1=direct child)
* @param aContent the content to check

View File

@ -8,18 +8,68 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=596511
<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"/>
<style>
select:valid { background-color: green; }
select:invalid { background-color: red; }
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=596511">Mozilla Bug 596511</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 596511 **/
function checkNotSufferingFromBeingMissing(element, aTodo)
{
if (aTodo) {
ok = todo;
is = todo_is;
}
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");
is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
"rgb(0, 128, 0)", ":valid pseudo-class should apply");
if (aTodo) {
ok = SimpleTest.ok;
is = SimpleTest.is;
}
}
function checkSufferingFromBeingMissing(element, aTodo)
{
if (aTodo) {
ok = todo;
is = todo_is;
}
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");
is(element.validationMessage, "Please select an item in the list.",
"Validation message is wrong");
is(window.getComputedStyle(element, null).getPropertyValue('background-color'),
"rgb(255, 0, 0)", ":invalid pseudo-class should apply");
if (aTodo) {
ok = SimpleTest.ok;
is = SimpleTest.is;
}
}
function checkRequiredAttribute(element)
{
ok('required' in element, "select should have a required attribute");
@ -47,23 +97,132 @@ function checkRequiredAttribute(element)
function checkRequiredAndOptionalSelectors(element)
{
is(document.querySelector("select:optional"), element, "select should be optional");
is(document.querySelector("select:required"), null, "select shouldn't be required");
is(document.querySelector("select:optional"), element,
"select should be optional");
is(document.querySelector("select:required"), null,
"select shouldn't be required");
element.required = true;
is(document.querySelector("select:optional"), null, "select shouldn't be optional");
is(document.querySelector("select:required"), element, "select should be required");
is(document.querySelector("select:optional"), null,
"select shouldn't be optional");
is(document.querySelector("select:required"), element,
"select should be required");
element.required = false;
}
function checkInvalidWhenValueMissing(element)
{
checkNotSufferingFromBeingMissing(select);
element.required = true;
checkSufferingFromBeingMissing(select);
/**
* Non-multiple and size=1.
*/
select.appendChild(new Option());
checkSufferingFromBeingMissing(select);
// When removing the required attribute, element should not be invalid.
element.required = false;
checkNotSufferingFromBeingMissing(select);
element.required = true;
select.options[0].textContent = "foo";
// TODO: having that working would require us to add a mutation observer on
// the select element.
checkNotSufferingFromBeingMissing(select, true);
select.remove(0);
checkSufferingFromBeingMissing(select);
select.add(new Option("foo", "foo"), null);
checkNotSufferingFromBeingMissing(select);
select.add(new Option(), null);
checkNotSufferingFromBeingMissing(select);
select.options[1].selected = true;
checkSufferingFromBeingMissing(select);
select.selectedIndex = 0;
checkNotSufferingFromBeingMissing(select);
select.selectedIndex = 1;
checkSufferingFromBeingMissing(select);
select.remove(1);
checkNotSufferingFromBeingMissing(select);
select.options[0].disabled = true;
// TODO: having that working would require us to add a mutation observer on
// the select element.
checkSufferingFromBeingMissing(select, true);
select.options[0].disabled = false
select.remove(0);
checkSufferingFromBeingMissing(select);
var option = new Option("foo", "foo");
option.disabled = true;
select.add(option, null);
select.add(new Option("bar"), null);
option.selected = true;
checkSufferingFromBeingMissing(select);
select.remove(0);
select.remove(0);
/**
* Non-multiple and size > 1.
* Everything should be the same except moving the selection.
*/
select.multiple = false;
select.size = 4;
checkSufferingFromBeingMissing(select);
select.add(new Option("", "", true), null);
checkSufferingFromBeingMissing(select);
select.add(new Option("foo", "foo"), null);
select.remove(0);
checkSufferingFromBeingMissing(select);
select.options[0].selected = true;
checkNotSufferingFromBeingMissing(select);
select.remove(0);
/**
* Multiple, any size.
* We can select more than one element and at least needs a value.
*/
select.multiple = true;
select.size = 4;
checkSufferingFromBeingMissing(select);
select.add(new Option("", "", true), null);
checkSufferingFromBeingMissing(select);
select.add(new Option("", "", true), null);
checkSufferingFromBeingMissing(select);
select.add(new Option("foo"), null);
checkSufferingFromBeingMissing(select);
select.options[2].selected = true;
checkNotSufferingFromBeingMissing(select);
}
var select = document.createElement("select");
var content = document.getElementById('content');
content.appendChild(select);
checkRequiredAttribute(select);
checkRequiredAndOptionalSelectors(select);
checkInvalidWhenValueMissing(select);
</script>
</pre>

View File

@ -3,6 +3,10 @@
== select-disabled.html select-disabled-ref.html
== select-dyn-disabled.html select-disabled-ref.html
== select-dyn-not-disabled.html select-ref.html
== select-required-invalid.html select-required-ref.html
== select-required-valid.html select-required-ref.html
== select-required-multiple-invalid.html select-required-multiple-ref.html
== select-required-multiple-valid.html select-required-multiple-ref.html
== select-disabled-fieldset-1.html select-fieldset-ref.html
== select-disabled-fieldset-2.html select-fieldset-ref.html
== select-fieldset-legend.html select-fieldset-legend-ref.html

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<!-- Test: if select is required and has a select option which has an empty
string value, :invalid should apply. -->
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select class='invalid' required>
<option selected value="">foo</option>
</select>
</body>
</html></html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<!-- Test: if select is required and has all selected option value set to the
string string, :invalid should apply. -->
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select class='invalid' required multiple>
<option selected></option>
<option selected value="">foo</option>
</select>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select multiple style="background-color: green;">
<option selected></option>
<option selected value="">foo</option>
</select>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<!-- Test: if select is required and has a selected option which has value
different from the empty string, :invalid should not apply. -->
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select class='notinvalid' required multiple>
<option selected></option>
<option selected>foo</option>
</select>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select style="background-color: green;">
<option selected value="">foo</option>
</selecT>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<!-- Test: if select is required and has a select option which has value
different from the empty string, :invalid should not apply. -->
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select class='notinvalid' required>
<option selected>foo</option>
</select>
</body>
</html>

View File

@ -3,6 +3,10 @@
== select-disabled.html select-disabled-ref.html
== select-dyn-disabled.html select-disabled-ref.html
== select-dyn-not-disabled.html select-ref.html
== select-required-invalid.html select-required-ref.html
== select-required-valid.html select-required-ref.html
== select-required-multiple-invalid.html select-required-multiple-ref.html
== select-required-multiple-valid.html select-required-multiple-ref.html
== select-disabled-fieldset-1.html select-fieldset-ref.html
== select-disabled-fieldset-2.html select-fieldset-ref.html
== select-fieldset-legend.html select-fieldset-legend-ref.html

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<!-- Test: if select is required and has a select option which has an empty
string value, :valid should not apply. -->
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select class='notvalid' required>
<option selected value="">foo</option>
</select>
</body>
</html></html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<!-- Test: if select is required and has all selected option value set to the
string string, :valid should not apply. -->
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select class='notvalid' required multiple>
<option selected></option>
<option selected value="">foo</option>
</select>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select multiple style="background-color: green;">
<option selected></option>
<option selected value="">foo</option>
</select>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<!-- Test: if select is required and has a selected option which has value
different from the empty string, :valid should apply. -->
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select class='valid' required multiple>
<option selected></option>
<option selected>foo</option>
</select>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select style="background-color: green;">
<option selected value="">foo</option>
</selecT>
</body>
</html>

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<!-- Test: if select is required and has a select option which has value
different from the empty string, :valid should apply. -->
<link rel='stylesheet' type='text/css' href='style.css'>
<body>
<select class='valid' required>
<option selected>foo</option>
</select>
</body>
</html>