зеркало из https://github.com/mozilla/pjs.git
Bug 635008 (1/4) - Keep track of the required state of radio groups. r=bz a=hardblocker
This commit is contained in:
Родитель
24d039501e
Коммит
25a40b0eab
|
@ -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;
|
||||
|
|
Загрузка…
Ссылка в новой задаче