diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 3d385b31695..75cb3070a02 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -502,6 +502,7 @@ struct nsRadioGroupStruct { nsRadioGroupStruct() : mRequiredRadioCount(0) + , mGroupSuffersFromValueMissing(false) {} /** @@ -510,6 +511,7 @@ struct nsRadioGroupStruct nsCOMPtr mSelectedRadioButton; nsCOMArray mRadioButtons; 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 nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel) { diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index f160b9790a8..1b1726d8384 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -793,6 +793,8 @@ public: virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const; virtual void RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio); + virtual bool GetValueMissingState(const nsAString& aName) const; + virtual void SetValueMissingState(const nsAString& aName, bool aValue); // for radio group nsresult GetRadioGroup(const nsAString& aName, diff --git a/content/html/content/public/nsIRadioGroupContainer.h b/content/html/content/public/nsIRadioGroupContainer.h index e3611ffdd04..2f519b50e7f 100644 --- a/content/html/content/public/nsIRadioGroupContainer.h +++ b/content/html/content/public/nsIRadioGroupContainer.h @@ -138,8 +138,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIRadioGroupContainer, NS_IRADIOGROUPCONTAINER_IID) #define NS_IRADIOGROUPCONTAINER_MOZILLA_2_0_BRANCH_IID \ -{ 0xdb6419eb, 0xff3d, 0x4bce, \ - { 0x9e, 0x4d, 0x47, 0x8c, 0x43, 0xb8, 0x1a, 0x10 } } +{ 0xaa9ec446, 0xcdc7, 0x4030, \ + { 0xab, 0x02, 0xda, 0x44, 0xee, 0xb1, 0x80, 0x0a } } class nsIRadioGroupContainer_MOZILLA_2_0_BRANCH : public nsIRadioGroupContainer { @@ -149,6 +149,8 @@ public: virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const = 0; virtual void RadioRequiredChanged(const nsAString& aName, 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, diff --git a/content/html/content/src/nsHTMLFormElement.cpp b/content/html/content/src/nsHTMLFormElement.cpp index fc3b43ed5e1..390928ed80e 100644 --- a/content/html/content/src/nsHTMLFormElement.cpp +++ b/content/html/content/src/nsHTMLFormElement.cpp @@ -297,6 +297,8 @@ nsHTMLFormElement::Init() NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_TRUE(mRequiredRadioButtonCounts.Init(4), NS_ERROR_OUT_OF_MEMORY); + NS_ENSURE_TRUE(mValueMissingRadioGroups.Init(4), + NS_ERROR_OUT_OF_MEMORY); return NS_OK; } @@ -1178,16 +1180,8 @@ nsHTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild, #ifdef DEBUG AssertDocumentOrder(controlList, this); #endif - - // - // Notify the radio button it's been added to a group - // + PRInt32 type = aChild->GetType(); - if (type == NS_FORM_INPUT_RADIO) { - nsRefPtr radio = - static_cast(aChild); - radio->AddedToRadioGroup(); - } // // 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 radio = + static_cast(aChild); + radio->AddedToRadioGroup(); + } + 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 diff --git a/content/html/content/src/nsHTMLFormElement.h b/content/html/content/src/nsHTMLFormElement.h index ea9d896d80c..f464463ada3 100644 --- a/content/html/content/src/nsHTMLFormElement.h +++ b/content/html/content/src/nsHTMLFormElement.h @@ -153,6 +153,8 @@ public: virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const; virtual void RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio); + virtual bool GetValueMissingState(const nsAString& aName) const; + virtual void SetValueMissingState(const nsAString& aName, bool aValue); // nsIContent virtual PRBool ParseAttribute(PRInt32 aNamespaceID, @@ -418,6 +420,8 @@ protected: nsInterfaceHashtable mSelectedRadioButtons; /** The number of required radio button of each group */ nsDataHashtable mRequiredRadioButtonCounts; + /** The value missing state of each group */ + nsDataHashtable mValueMissingRadioGroups; /** Whether we are currently processing a submit event or not */ PRPackedBool mGeneratingSubmit; /** Whether we are currently processing a reset event or not */ diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index 2a11c8b631a..6ba95978f02 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -814,6 +814,9 @@ nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, mType == NS_FORM_INPUT_RADIO && (mForm || !(GET_BOOLBIT(mBitField, BF_PARSER_CREATING)))) { 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 @@ -3494,6 +3497,13 @@ nsHTMLInputElement::AddedToRadioGroup() nsAutoString name; if (GetNameIfExists(name)) { container->AddToRadioGroup(name, static_cast(this)); + + // We initialize the validity of the element to the validity of the group + // because we assume UpdateValueMissingState() will be called after. + nsCOMPtr container2 = + do_QueryInterface(container); + SetValidityState(VALIDITY_STATE_VALUE_MISSING, + container2->GetValueMissingState(name)); } } } @@ -3927,10 +3937,11 @@ nsHTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf) nsCOMPtr container = do_QueryInterface(c); nsAutoString name; + GetNameIfExists(name); // If the current radio is required and not ignored, we can assume the entire // group is required. - if (!required && container && GetNameIfExists(name)) { + if (!required && container && !name.IsEmpty()) { required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required)) ? container->GetRequiredRadioCount(name) - 1 : container->GetRequiredRadioCount(name); @@ -3938,12 +3949,20 @@ nsHTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf) valueMissing = required && !selected; - SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing); + if (container && !name.IsEmpty()) { + if (container->GetValueMissingState(name) != valueMissing) { + container->SetValueMissingState(name, valueMissing); - nsCOMPtr visitor = - NS_SetRadioValueMissingState(this, GetCurrentDoc(), valueMissing, - notify); - VisitGroup(visitor, notify); + SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing); + + nsCOMPtr visitor = + NS_SetRadioValueMissingState(this, GetCurrentDoc(), valueMissing, + notify); + VisitGroup(visitor, notify); + } + } else { + SetValidityState(VALIDITY_STATE_VALUE_MISSING, valueMissing); + } } void