зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
2893258b45
Коммит
c658ba85fe
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
Загрузка…
Ссылка в новой задаче