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

This commit is contained in:
Mounir Lamouri 2011-02-25 19:12:47 +01:00
Родитель 24d039501e
Коммит 25a40b0eab
6 изменённых файлов: 164 добавлений и 9 удалений

Просмотреть файл

@ -500,11 +500,16 @@ struct FindContentData
*/
struct nsRadioGroupStruct
{
nsRadioGroupStruct()
: mRequiredRadioCount(0)
{}
/**
* A strong pointer to the currently selected radio button.
*/
nsCOMPtr<nsIDOMHTMLInputElement> mSelectedRadioButton;
nsCOMArray<nsIFormControl> mRadioButtons;
PRUint32 mRequiredRadioCount;
};
@ -1725,6 +1730,7 @@ NS_INTERFACE_TABLE_HEAD(nsDocument)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsPIDOMEventTarget)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsISupportsWeakReference)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIRadioGroupContainer_MOZILLA_2_0_BRANCH)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIMutationObserver)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIApplicationCacheContainer)
NS_INTERFACE_TABLE_ENTRY(nsDocument, nsIDOMNSDocument_MOZILLA_2_0_BRANCH)
@ -6710,6 +6716,12 @@ nsDocument::AddToRadioGroup(const nsAString& aName,
GetRadioGroup(aName, &radioGroup);
if (radioGroup) {
radioGroup->mRadioButtons.AppendObject(aRadio);
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
radioGroup->mRequiredRadioCount++;
}
}
return NS_OK;
@ -6723,6 +6735,14 @@ nsDocument::RemoveFromRadioGroup(const nsAString& aName,
GetRadioGroup(aName, &radioGroup);
if (radioGroup) {
radioGroup->mRadioButtons.RemoveObject(aRadio);
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
radioGroup->mRequiredRadioCount--;
NS_ASSERTION(radioGroup->mRequiredRadioCount >= 0,
"mRequiredRadioCount shouldn't be negative!");
}
}
return NS_OK;
@ -6750,6 +6770,42 @@ nsDocument::WalkRadioGroup(const nsAString& aName,
return NS_OK;
}
PRUint32
nsDocument::GetRequiredRadioCount(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->mRequiredRadioCount : 0;
}
void
nsDocument::RadioRequiredChanged(const nsAString& aName, nsIFormControl* aRadio)
{
nsRadioGroupStruct* radioGroup = nsnull;
GetRadioGroup(aName, &radioGroup);
if (!radioGroup) {
return;
}
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
radioGroup->mRequiredRadioCount++;
} else {
radioGroup->mRequiredRadioCount--;
NS_ASSERTION(radioGroup->mRequiredRadioCount >= 0,
"mRequiredRadioCount shouldn't be negative!");
}
}
void
nsDocument::RetrieveRelevantHeaders(nsIChannel *aChannel)
{

Просмотреть файл

@ -515,7 +515,7 @@ class nsDocument : public nsIDocument,
public nsIDOM3EventTarget,
public nsIDOMNSEventTarget,
public nsIScriptObjectPrincipal,
public nsIRadioGroupContainer,
public nsIRadioGroupContainer_MOZILLA_2_0_BRANCH,
public nsIApplicationCacheContainer,
public nsStubMutationObserver,
public nsIDOMNSDocument_MOZILLA_2_0_BRANCH
@ -790,6 +790,9 @@ public:
nsIFormControl* aRadio);
NS_IMETHOD RemoveFromRadioGroup(const nsAString& aName,
nsIFormControl* aRadio);
virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const;
virtual void RadioRequiredChanged(const nsAString& aName,
nsIFormControl* aRadio);
// for radio group
nsresult GetRadioGroup(const nsAString& aName,

Просмотреть файл

@ -137,4 +137,21 @@ public:
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 } }
class nsIRadioGroupContainer_MOZILLA_2_0_BRANCH : public nsIRadioGroupContainer
{
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IRADIOGROUPCONTAINER_MOZILLA_2_0_IID)
virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const = 0;
virtual void RadioRequiredChanged(const nsAString& aName,
nsIFormControl* aRadio) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIRadioGroupContainer_MOZILLA_2_0_BRANCH,
NS_IRADIOGROUPCONTAINER_MOZILLA_2_0_BRANCH_IID)
#endif /* nsIRadioGroupContainer_h__ */

Просмотреть файл

@ -295,6 +295,8 @@ nsHTMLFormElement::Init()
NS_ENSURE_TRUE(mSelectedRadioButtons.Init(4),
NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mRequiredRadioButtonCounts.Init(4),
NS_ERROR_OUT_OF_MEMORY);
return NS_OK;
}
@ -329,12 +331,13 @@ DOMCI_NODE_DATA(HTMLFormElement, nsHTMLFormElement)
// QueryInterface implementation for nsHTMLFormElement
NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLFormElement)
NS_HTML_CONTENT_INTERFACE_TABLE5(nsHTMLFormElement,
NS_HTML_CONTENT_INTERFACE_TABLE6(nsHTMLFormElement,
nsIDOMHTMLFormElement,
nsIDOMNSHTMLFormElement,
nsIForm,
nsIWebProgressListener,
nsIRadioGroupContainer)
nsIRadioGroupContainer,
nsIRadioGroupContainer_MOZILLA_2_0_BRANCH)
NS_HTML_CONTENT_INTERFACE_TABLE_TO_MAP_SEGUE(nsHTMLFormElement,
nsGenericHTMLElement)
NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLASSINFO(HTMLFormElement)
@ -2121,6 +2124,14 @@ NS_IMETHODIMP
nsHTMLFormElement::AddToRadioGroup(const nsAString& aName,
nsIFormControl* aRadio)
{
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements!");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
mRequiredRadioButtonCounts.Put(aName,
mRequiredRadioButtonCounts.Get(aName)+1);
}
return NS_OK;
}
@ -2128,9 +2139,53 @@ NS_IMETHODIMP
nsHTMLFormElement::RemoveFromRadioGroup(const nsAString& aName,
nsIFormControl* aRadio)
{
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements!");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
PRUint32 requiredNb = mRequiredRadioButtonCounts.Get(aName);
NS_ASSERTION(requiredNb >= 1,
"At least one radio button has to be required!");
if (requiredNb == 1) {
mRequiredRadioButtonCounts.Remove(aName);
} else {
mRequiredRadioButtonCounts.Put(aName, requiredNb-1);
}
}
return NS_OK;
}
PRUint32
nsHTMLFormElement::GetRequiredRadioCount(const nsAString& aName) const
{
return mRequiredRadioButtonCounts.Get(aName);
}
void
nsHTMLFormElement::RadioRequiredChanged(const nsAString& aName,
nsIFormControl* aRadio)
{
nsCOMPtr<nsIContent> element = do_QueryInterface(aRadio);
NS_ASSERTION(element, "radio controls have to be content elements!");
if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
mRequiredRadioButtonCounts.Put(aName,
mRequiredRadioButtonCounts.Get(aName)+1);
} else {
PRUint32 requiredNb = mRequiredRadioButtonCounts.Get(aName);
NS_ASSERTION(requiredNb >= 1,
"At least one radio button has to be required!");
if (requiredNb == 1) {
mRequiredRadioButtonCounts.Remove(aName);
} else {
mRequiredRadioButtonCounts.Put(aName, requiredNb-1);
}
}
}
//----------------------------------------------------------------------
// nsFormControlList implementation, this could go away if there were
// a lightweight collection implementation somewhere

Просмотреть файл

@ -53,6 +53,7 @@
#include "nsUnicharUtils.h"
#include "nsThreadUtils.h"
#include "nsInterfaceHashtable.h"
#include "nsDataHashtable.h"
class nsFormControlList;
class nsIMutableArray;
@ -95,7 +96,7 @@ class nsHTMLFormElement : public nsGenericHTMLElement,
public nsIDOMNSHTMLFormElement,
public nsIWebProgressListener,
public nsIForm,
public nsIRadioGroupContainer
public nsIRadioGroupContainer_MOZILLA_2_0_BRANCH
{
public:
nsHTMLFormElement(already_AddRefed<nsINodeInfo> aNodeInfo);
@ -149,6 +150,9 @@ public:
nsIFormControl* aRadio);
NS_IMETHOD RemoveFromRadioGroup(const nsAString& aName,
nsIFormControl* aRadio);
virtual PRUint32 GetRequiredRadioCount(const nsAString& aName) const;
virtual void RadioRequiredChanged(const nsAString& aName,
nsIFormControl* aRadio);
// nsIContent
virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
@ -412,6 +416,8 @@ protected:
nsRefPtr<nsFormControlList> mControls;
/** The currently selected radio button of each group */
nsInterfaceHashtable<nsStringCaseInsensitiveHashKey,nsIDOMHTMLInputElement> mSelectedRadioButtons;
/** The number of required radio button of each group */
nsDataHashtable<nsStringCaseInsensitiveHashKey,PRUint32> mRequiredRadioButtonCounts;
/** Whether we are currently processing a submit event or not */
PRPackedBool mGeneratingSubmit;
/** Whether we are currently processing a reset event or not */

Просмотреть файл

@ -906,6 +906,17 @@ nsHTMLInputElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
NS_EVENT_STATE_MOZ_SUBMITINVALID;
}
if (mType == NS_FORM_INPUT_RADIO && aName == nsGkAtoms::required) {
nsCOMPtr<nsIRadioGroupContainer> c = GetRadioGroupContainer();
nsCOMPtr<nsIRadioGroupContainer_MOZILLA_2_0_BRANCH> container =
do_QueryInterface(c);
nsAutoString name;
if (container && GetNameIfExists(name)) {
container->RadioRequiredChanged(name, this);
}
}
if (aName == nsGkAtoms::required || aName == nsGkAtoms::disabled ||
aName == nsGkAtoms::readonly) {
UpdateValueMissingValidityState();
@ -3903,6 +3914,7 @@ nsHTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
{
PRBool notify = !GET_BOOLBIT(mBitField, BF_PARSER_CREATING);
nsCOMPtr<nsIDOMHTMLInputElement> selection = GetSelectedRadioButton();
// If there is no selection, that might mean the radio is not in a group.
// In that case, we can look for the checked state of the radio.
bool selected = selection ? true
@ -3911,11 +3923,17 @@ nsHTMLInputElement::UpdateValueMissingValidityStateForRadio(bool aIgnoreSelf)
: HasAttr(kNameSpaceID_None, nsGkAtoms::required);
bool valueMissing = false;
// If the current radio is required, don't check the entire group.
if (!required) {
nsCOMPtr<nsIRadioVisitor> visitor =
NS_GetRadioGroupRequiredVisitor(this, &required);
VisitGroup(visitor, notify);
nsCOMPtr<nsIRadioGroupContainer> c = GetRadioGroupContainer();
nsCOMPtr<nsIRadioGroupContainer_MOZILLA_2_0_BRANCH> container =
do_QueryInterface(c);
nsAutoString name;
// If the current radio is required and not ignored, we can assume the entire
// group is required.
if (!required && container && GetNameIfExists(name)) {
required = (aIgnoreSelf && HasAttr(kNameSpaceID_None, nsGkAtoms::required))
? container->GetRequiredRadioCount(name) - 1
: container->GetRequiredRadioCount(name);
}
valueMissing = required && !selected;