Bug 885359 - Support :indeterminate pseudo-class for radio groups. r=smaug

This commit is contained in:
Jessica Jong 2016-08-24 18:39:00 -04:00
Родитель 17c45acab3
Коммит 1b328436b6
9 изменённых файлов: 159 добавлений и 16 удалений

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

@ -3289,7 +3289,7 @@ HTMLInputElement::GetRadioGroupContainer() const
}
already_AddRefed<nsIDOMHTMLInputElement>
HTMLInputElement::GetSelectedRadioButton()
HTMLInputElement::GetSelectedRadioButton() const
{
nsIRadioGroupContainer* container = GetRadioGroupContainer();
if (!container) {
@ -3360,6 +3360,13 @@ HTMLInputElement::SetCheckedInternal(bool aChecked, bool aNotify)
// Notify the document that the CSS :checked pseudoclass for this element
// has changed state.
UpdateState(aNotify);
// Notify all radios in the group that value has changed, this is to let
// radios to have the chance to update its states, e.g., :indeterminate.
if (mType == NS_FORM_INPUT_RADIO) {
nsCOMPtr<nsIRadioVisitor> visitor = new nsRadioUpdateStateVisitor(this);
VisitGroup(visitor, aNotify);
}
}
void
@ -6420,6 +6427,15 @@ HTMLInputElement::IntrinsicState() const
state |= NS_EVENT_STATE_INDETERMINATE;
}
if (mType == NS_FORM_INPUT_RADIO) {
nsCOMPtr<nsIDOMHTMLInputElement> selected = GetSelectedRadioButton();
bool indeterminate = !selected && !mChecked;
if (indeterminate) {
state |= NS_EVENT_STATE_INDETERMINATE;
}
}
// Check whether we are the default checked element (:default)
if (DefaultChecked()) {
state |= NS_EVENT_STATE_DEFAULT;
@ -6643,6 +6659,9 @@ HTMLInputElement::WillRemoveFromRadioGroup()
// longer a selected radio button
if (mChecked) {
container->SetCurrentRadioButton(name, nullptr);
nsCOMPtr<nsIRadioVisitor> visitor = new nsRadioUpdateStateVisitor(this);
VisitGroup(visitor, true);
}
// Remove this radio from its group in the container.

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

@ -264,7 +264,7 @@ public:
*
* @return the selected button (or null).
*/
already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton();
already_AddRefed<nsIDOMHTMLInputElement> GetSelectedRadioButton() const;
virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;

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

@ -55,3 +55,15 @@ nsRadioSetValueMissingState::Visit(nsIFormControl* aRadio)
return true;
}
bool
nsRadioUpdateStateVisitor::Visit(nsIFormControl* aRadio)
{
if (aRadio == mExcludeElement) {
return true;
}
HTMLInputElement* input = static_cast<HTMLInputElement*>(aRadio);
input->UpdateState(true);
return true;
}

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

@ -94,5 +94,18 @@ protected:
bool mNotify;
};
class nsRadioUpdateStateVisitor : public nsRadioVisitor
{
public:
explicit nsRadioUpdateStateVisitor(nsIFormControl* aExcludeElement)
: mExcludeElement(aExcludeElement)
{ }
virtual bool Visit(nsIFormControl* aRadio) override;
protected:
nsIFormControl* mExcludeElement;
};
#endif // _nsRadioVisitor_h__

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

@ -9,6 +9,7 @@ support-files =
[test_bug1286509.html]
skip-if = os == "android" || appname == "b2g" # up/down arrow keys not supported on android/b2g
[test_button_attributes_reflection.html]
[test_input_radio_indeterminate.html]
[test_input_radio_radiogroup.html]
[test_input_radio_required.html]
[test_change_event.html]

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

@ -0,0 +1,109 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=885359
-->
<head>
<title>Test for Bug 885359</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=885359">Mozilla Bug 343444</a>
<p id="display"></p>
<form>
<input type="radio" id='radio1'/><br/>
<input type="radio" id="g1radio1" name="group1"/>
<input type="radio" id="g1radio2" name="group1"/></br>
<input type="radio" id="g1radio3" name="group1"/></br>
<input type="radio" id="g2radio1" name="group2"/>
<input type="radio" id="g2radio2" name="group2" checked/></br>
</form>
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
var radio1 = document.getElementById("radio1");
var g1radio1 = document.getElementById("g1radio1");
var g1radio2 = document.getElementById("g1radio2");
var g1radio3 = document.getElementById("g1radio3");
var g2radio1 = document.getElementById("g2radio1");
var g2radio2 = document.getElementById("g2radio2");
SimpleTest.waitForFocus(function() {
test();
SimpleTest.finish();
});
function verifyIndeterminateState(aElement, aIsIndeterminate, aMessage) {
is(aElement.mozMatchesSelector(':indeterminate'), aIsIndeterminate, aMessage);
}
function test() {
// Initial State.
verifyIndeterminateState(radio1, true,
"Unchecked radio in its own group (no name attribute)");
verifyIndeterminateState(g1radio1, true, "No selected radio in its group");
verifyIndeterminateState(g1radio2, true, "No selected radio in its group");
verifyIndeterminateState(g1radio3, true, "No selected radio in its group");
verifyIndeterminateState(g2radio1, false, "Selected radio in its group");
verifyIndeterminateState(g2radio2, false, "Selected radio in its group");
// Selecting radio buttion.
g1radio1.checked = true;
verifyIndeterminateState(g1radio1, false,
"Selecting a radio should affect all radios in the group");
verifyIndeterminateState(g1radio2, false,
"Selecting a radio should affect all radios in the group");
verifyIndeterminateState(g1radio3, false,
"Selecting a radio should affect all radios in the group");
// Changing the selected radio button.
g1radio3.checked = true;
verifyIndeterminateState(g1radio1, false,
"Selecting a radio should affect all radios in the group");
verifyIndeterminateState(g1radio2, false,
"Selecting a radio should affect all radios in the group");
verifyIndeterminateState(g1radio3, false,
"Selecting a radio should affect all radios in the group");
// Deselecting radio button.
g2radio2.checked = false;
verifyIndeterminateState(g2radio1, true,
"Deselecting a radio should affect all radios in the group");
verifyIndeterminateState(g2radio2, true,
"Deselecting a radio should affect all radios in the group");
// Move a selected radio button to another group.
g1radio3.name = "group2";
// The radios' state in the original group becomes indeterminated.
verifyIndeterminateState(g1radio1, true,
"Removing a radio from a group should affect all radios in the group");
verifyIndeterminateState(g1radio2, true,
"Removing a radio from a group should affect all radios in the group");
// The radios' state in the new group becomes determinated.
verifyIndeterminateState(g1radio3, false,
"Adding a radio from a group should affect all radios in the group");
verifyIndeterminateState(g2radio1, false,
"Adding a radio from a group should affect all radios in the group");
verifyIndeterminateState(g2radio2, false,
"Adding a radio from a group should affect all radios in the group");
// Change input type to 'text'.
g1radio3.type = "text";
verifyIndeterminateState(g1radio3, false,
"Input type text does not have an indeterminate state");
verifyIndeterminateState(g2radio1, true,
"Changing input type should affect all radios in the group");
verifyIndeterminateState(g2radio2, true,
"Changing input type should affect all radios in the group");
}
</script>
</pre>
</body>
</html>

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

@ -43,8 +43,8 @@ is(document.defaultView.getComputedStyle($("s"), null).getPropertyValue("margin-
$("s").setAttribute("type", "radio");
is(document.defaultView.getComputedStyle($("s"), null).getPropertyValue("margin-left"),
"10px",
"Only checkboxes should have indeterminate styles applied to them");
"30px",
"Setting an indeterminate element to type radio should give it indeterminate styles");
$("s").setAttribute("type", "checkbox");

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

@ -1,11 +0,0 @@
[indeterminate.html]
type: testharness
[':progress' matches <input>s radio buttons whose radio button group contains no checked input and <progress> elements without value attribute]
expected: FAIL
[dynamically check a radio input in a radio button group]
expected: FAIL
[click on radio4 which is in the indeterminate state]
expected: FAIL

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

@ -24,7 +24,7 @@
testSelectorIdsMatch(":indeterminate", ["radio4", "radio5", "progress1"], "dynamically check a radio input in a radio button group");
document.getElementById("radio4").click();
testSelectorIdsMatch(":indeterminate", ["checkbox1", "progress2"], "click on radio4 which is in the indeterminate state");
testSelectorIdsMatch(":indeterminate", ["progress1"], "click on radio4 which is in the indeterminate state");
document.getElementById("progress1").setAttribute("value", "20");
testSelectorIdsMatch(":indeterminate", [], "adding a value to progress1 should put it in a determinate state");