Bug 635008 (3/4) - Keep track of the validity state of radio groups. r=bz a=hardblocker

This commit is contained in:
Mounir Lamouri 2011-02-25 19:16:04 +01:00
parent 1c0e6ffecf
commit 729c57b0b5
6 changed files with 89 additions and 17 deletions

View File

@ -502,6 +502,7 @@ struct nsRadioGroupStruct
{ {
nsRadioGroupStruct() nsRadioGroupStruct()
: mRequiredRadioCount(0) : mRequiredRadioCount(0)
, mGroupSuffersFromValueMissing(false)
{} {}
/** /**
@ -510,6 +511,7 @@ struct nsRadioGroupStruct
nsCOMPtr<nsIDOMHTMLInputElement> mSelectedRadioButton; nsCOMPtr<nsIDOMHTMLInputElement> mSelectedRadioButton;
nsCOMArray<nsIFormControl> mRadioButtons; nsCOMArray<nsIFormControl> mRadioButtons;
PRUint32 mRequiredRadioCount; PRUint32 mRequiredRadioCount;
bool mGroupSuffersFromValueMissing;
}; };
@ -6806,6 +6808,34 @@ nsDocument::RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio)
} }
} }
bool
nsDocument::GetValueMissingState(const nsAString& aName) const
{
nsRadioGroupStruct* radioGroup = nsnull;
// TODO: we should call GetRadioGroup here (and make it const) but for that
// we would need to have an explicit CreateRadioGroup() instead of create
// one when GetRadioGroup is called. See bug 636123.
nsAutoString tmKey(aName);
if (IsHTML())
ToLowerCase(tmKey); //should case-insensitive.
mRadioGroups.Get(tmKey, &radioGroup);
return radioGroup && radioGroup->mGroupSuffersFromValueMissing;
}
void
nsDocument::SetValueMissingState(const nsAString& aName, bool aValue)
{
nsRadioGroupStruct* radioGroup = nsnull;
GetRadioGroup(aName, &radioGroup);
if (!radioGroup) {
return;
}
radioGroup->mGroupSuffersFromValueMissing = aValue;
}
void void
nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel) nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
{ {

View File

@ -793,6 +793,8 @@ public:
virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const; virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const;
virtual void RadioRequiredChanged(const nsAString& aName, virtual void RadioRequiredChanged(const nsAString& aName,
nsIFormControl* aRadio); nsIFormControl* aRadio);
virtual bool GetValueMissingState(const nsAString& aName) const;
virtual void SetValueMissingState(const nsAString& aName, bool aValue);
// for radio group // for radio group
nsresult GetRadioGroup(const nsAString& aName, nsresult GetRadioGroup(const nsAString& aName,

View File

@ -138,8 +138,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIRadioGroupContainer,
NS_IRADIOGROUPCONTAINER_IID) NS_IRADIOGROUPCONTAINER_IID)
#define NS_IRADIOGROUPCONTAINER_MOZILLA_2_0_BRANCH_IID \ #define NS_IRADIOGROUPCONTAINER_MOZILLA_2_0_BRANCH_IID \
{ 0xdb6419eb, 0xff3d, 0x4bce, \ { 0xaa9ec446, 0xcdc7, 0x4030, \
{ 0x9e, 0x4d, 0x47, 0x8c, 0x43, 0xb8, 0x1a, 0x10 } } { 0xab, 0x02, 0xda, 0x44, 0xee, 0xb1, 0x80, 0x0a } }
class nsIRadioGroupContainer_MOZILLA_2_0_BRANCH : public nsIRadioGroupContainer class nsIRadioGroupContainer_MOZILLA_2_0_BRANCH : public nsIRadioGroupContainer
{ {
@ -149,6 +149,8 @@ public:
virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const = 0; virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const = 0;
virtual void RadioRequiredChanged(const nsAString& aName, virtual void RadioRequiredChanged(const nsAString& aName,
nsIFormControl* aRadio) = 0; nsIFormControl* aRadio) = 0;
virtual bool GetValueMissingState(const nsAString& aName) const = 0;
virtual void SetValueMissingState(const nsAString& aName, bool aValue) = 0;
}; };
NS_DEFINE_STATIC_IID_ACCESSOR(nsIRadioGroupContainer_MOZILLA_2_0_BRANCH, NS_DEFINE_STATIC_IID_ACCESSOR(nsIRadioGroupContainer_MOZILLA_2_0_BRANCH,

View File

@ -297,6 +297,8 @@ nsHTMLFormElement::Init()
NS_ERROR_OUT_OF_MEMORY); NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mRequiredRadioButtonCounts.Init(4), NS_ENSURE_TRUE(mRequiredRadioButtonCounts.Init(4),
NS_ERROR_OUT_OF_MEMORY); NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mValueMissingRadioGroups.Init(4),
NS_ERROR_OUT_OF_MEMORY);
return NS_OK; return NS_OK;
} }
@ -1179,15 +1181,7 @@ nsHTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
AssertDocumentOrder(controlList, this); AssertDocumentOrder(controlList, this);
#endif #endif
//
// Notify the radio button it's been added to a group
//
PRInt32 type = aChild->GetType(); PRInt32 type = aChild->GetType();
if (type == NS_FORM_INPUT_RADIO) {
nsRefPtr<nsHTMLInputElement> radio =
static_cast<nsHTMLInputElement*>(aChild);
radio->AddedToRadioGroup();
}
// //
// If it is a password control, and the password manager has not yet been // If it is a password control, and the password manager has not yet been
@ -1265,6 +1259,15 @@ nsHTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
} }
} }
// Notify the radio button it's been added to a group
// This has to be done _after_ UpdateValidity() call to prevent the element
// being count twice.
if (type == NS_FORM_INPUT_RADIO) {
nsRefPtr<nsHTMLInputElement> radio =
static_cast<nsHTMLInputElement*>(aChild);
radio->AddedToRadioGroup();
}
return NS_OK; return NS_OK;
} }
@ -2185,6 +2188,18 @@ nsHTMLFormElement::RadioRequiredChanged(const nsAString& aName,
} }
} }
bool
nsHTMLFormElement::GetValueMissingState(const nsAString& aName) const
{
return mValueMissingRadioGroups.Get(aName);
}
void
nsHTMLFormElement::SetValueMissingState(const nsAString& aName, bool aValue)
{
mValueMissingRadioGroups.Put(aName, aValue);
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// nsFormControlList implementation, this could go away if there were // nsFormControlList implementation, this could go away if there were

View File

@ -153,6 +153,8 @@ public:
virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const; virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const;
virtual void RadioRequiredChanged(const nsAString& aName, virtual void RadioRequiredChanged(const nsAString& aName,
nsIFormControl* aRadio); nsIFormControl* aRadio);
virtual bool GetValueMissingState(const nsAString& aName) const;
virtual void SetValueMissingState(const nsAString& aName, bool aValue);
// nsIContent // nsIContent
virtual PRBool ParseAttribute(PRInt32 aNamespaceID, virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
@ -418,6 +420,8 @@ protected:
nsInterfaceHashtable<nsStringCaseInsensitiveHashKey,nsIDOMHTMLInputElement> mSelectedRadioButtons; nsInterfaceHashtable<nsStringCaseInsensitiveHashKey,nsIDOMHTMLInputElement> mSelectedRadioButtons;
/** The number of required radio button of each group */ /** The number of required radio button of each group */
nsDataHashtable<nsStringCaseInsensitiveHashKey,PRUint32> mRequiredRadioButtonCounts; nsDataHashtable<nsStringCaseInsensitiveHashKey,PRUint32> mRequiredRadioButtonCounts;
/** The value missing state of each group */
nsDataHashtable<nsStringCaseInsensitiveHashKey,bool> mValueMissingRadioGroups;
/** Whether we are currently processing a submit event or not */ /** Whether we are currently processing a submit event or not */
PRPackedBool mGeneratingSubmit; PRPackedBool mGeneratingSubmit;
/** Whether we are currently processing a reset event or not */ /** Whether we are currently processing a reset event or not */

View File

@ -814,6 +814,9 @@ nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
mType == NS_FORM_INPUT_RADIO && mType == NS_FORM_INPUT_RADIO &&
(mForm || !(GET_BOOLBIT(mBitField, BF_PARSER_CREATING)))) { (mForm || !(GET_BOOLBIT(mBitField, BF_PARSER_CREATING)))) {
AddedToRadioGroup(); AddedToRadioGroup();
UpdateValueMissingValidityStateForRadio(false);
states |= NS_EVENT_STATE_VALID | NS_EVENT_STATE_INVALID |
NS_EVENT_STATE_REQUIRED | NS_EVENT_STATE_OPTIONAL;
} }
// If @value is changed and BF_VALUE_CHANGED is false, @value is the value // If @value is changed and BF_VALUE_CHANGED is false, @value is the value
@ -3494,6 +3497,13 @@ nsHTMLInputElement::AddedToRadioGroup()
nsAutoString name; nsAutoString name;
if (GetNameIfExists(name)) { if (GetNameIfExists(name)) {
container->AddToRadioGroup(name, static_cast<nsIFormControl*>(this)); container->AddToRadioGroup(name, static_cast<nsIFormControl*>(this));
// We initialize the validity of the element to the validity of the group
// because we assume UpdateValueMissingState() will be called after.
nsCOMPtr<nsIRadioGroupContainer_MOZILLA_2_0_BRANCH> container2 =
do_QueryInterface(container);
SetValidityState(VALIDITY_STATE_VALUE_MISSING,
container2->GetValueMissingState(name));
} }
} }
} }
@ -3927,10 +3937,11 @@ nsHTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
nsCOMPtr<nsIRadioGroupContainer_MOZILLA_2_0_BRANCH> container = nsCOMPtr<nsIRadioGroupContainer_MOZILLA_2_0_BRANCH> container =
do_QueryInterface(c); do_QueryInterface(c);
nsAutoString name; nsAutoString name;
GetNameIfExists(name);
// If the current radio is required and not ignored, we can assume the entire // If the current radio is required and not ignored, we can assume the entire
// group is required. // group is required.
if (!required && container && GetNameIfExists(name)) { if (!required && container && !name.IsEmpty()) {
required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required))
? container->GetRequiredRadioCount(name) - 1 ? container->GetRequiredRadioCount(name) - 1
: container->GetRequiredRadioCount(name); : container->GetRequiredRadioCount(name);
@ -3938,6 +3949,10 @@ nsHTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
valueMissing = required && !selected; valueMissing = required && !selected;
if (container && !name.IsEmpty()) {
if (container->GetValueMissingState(name) != valueMissing) {
container->SetValueMissingState(name, valueMissing);
SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing); SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing);
nsCOMPtr<nsIRadioVisitor> visitor = nsCOMPtr<nsIRadioVisitor> visitor =
@ -3945,6 +3960,10 @@ nsHTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
notify); notify);
VisitGroup(visitor, notify); VisitGroup(visitor, notify);
} }
} else {
SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing);
}
}
void void
nsHTMLInputElement::UpdateValueMissingValidityState() nsHTMLInputElement::UpdateValueMissingValidityState()