Bug 759666 - Make :disabled apply on <option> when their parent <optgroup> has @disabled set. r=bz

This commit is contained in:
Mounir Lamouri 2012-06-01 11:46:43 +02:00
Родитель efcd6ffbea
Коммит 1c3fc21ec0
5 изменённых файлов: 189 добавлений и 2 удалений

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

@ -54,6 +54,9 @@ public:
virtual nsXPCClassInfo* GetClassInfo();
virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify);
virtual nsIDOMNode* AsDOMNode() { return this; }
virtual bool IsDisabled() const {
@ -165,6 +168,26 @@ nsHTMLOptGroupElement::RemoveChildAt(PRUint32 aIndex, bool aNotify)
nsGenericHTMLElement::RemoveChildAt(aIndex, aNotify);
}
nsresult
nsHTMLOptGroupElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAttrValue* aValue, bool aNotify)
{
if (aNameSpaceID == kNameSpaceID_None && aName == nsGkAtoms::disabled) {
// All our children <option> have their :disabled state depending on our
// disabled attribute. We should make sure their state is updated.
for (nsIContent* child = nsINode::GetFirstChild(); child;
child = child->GetNextSibling()) {
if (child->IsHTML(nsGkAtoms::option)) {
// No need to call |IsElement()| because it's an HTML element.
child->AsElement()->UpdateState(true);
}
}
}
return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
aNotify);
}
nsEventStates
nsHTMLOptGroupElement::IntrinsicState() const
{

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

@ -277,6 +277,31 @@ nsHTMLOptionElement::SetText(const nsAString& aText)
return nsContentUtils::SetNodeTextContent(this, aText, true);
}
nsresult
nsHTMLOptionElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
// Our new parent might change :disabled/:enabled state.
UpdateState(false);
return NS_OK;
}
void
nsHTMLOptionElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
// Our previous parent could have been involved in :disabled/:enabled state.
UpdateState(false);
}
nsEventStates
nsHTMLOptionElement::IntrinsicState() const
{
@ -288,12 +313,21 @@ nsHTMLOptionElement::IntrinsicState() const
state |= NS_EVENT_STATE_DEFAULT;
}
// An <option> is disabled if it has @disabled set or if it's <optgroup> has
// @disabled set.
if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
state |= NS_EVENT_STATE_DISABLED;
state &= ~NS_EVENT_STATE_ENABLED;
} else {
state &= ~NS_EVENT_STATE_DISABLED;
state |= NS_EVENT_STATE_ENABLED;
nsIContent* parent = GetParent();
if (parent && parent->IsHTML(nsGkAtoms::optgroup) &&
parent->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
state |= NS_EVENT_STATE_DISABLED;
state &= ~NS_EVENT_STATE_ENABLED;
} else {
state &= ~NS_EVENT_STATE_DISABLED;
state |= NS_EVENT_STATE_ENABLED;
}
}
return state;

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

@ -62,6 +62,12 @@ public:
void SetSelectedInternal(bool aValue, bool aNotify);
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers);
virtual void UnbindFromTree(bool aDeep = true,
bool aNullParent = true);
// nsIContent
virtual nsEventStates IntrinsicState() const;

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

@ -39,6 +39,7 @@ _TEST_FILES = \
test_form_attribute-2.html \
test_form_attribute-3.html \
test_form_attribute-4.html \
test_option_disabled.html \
$(NULL)
libs:: $(_TEST_FILES)

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

@ -0,0 +1,123 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=759666
-->
<head>
<meta charset="utf-8">
<title>Test for HTMLOptionElement disabled attribute and pseudo-class</title>
<script type="application/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=759666">Mozilla Bug 759666</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for HTMLOptionElement disabled attribute and pseudo-class **/
var testCases = [
// Static checks.
{ html: "<option></option>",
result: { attr: null, idl: false, pseudo: false } },
{ html: "<option disabled></option>",
result: { attr: "", idl: true, pseudo: true } },
{ html: "<optgroup><option></option></otpgroup>",
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup><option disabled></option></optgroup>",
result: { attr: "", idl: true, pseudo: true } },
{ html: "<optgroup disabled><option disabled></option></optgroup>",
result: { attr: "", idl: true, pseudo: true } },
{ html: "<optgroup disabled><option></option></optgroup>",
result: { attr: null, idl: false, pseudo: true } },
{ html: "<optgroup><optgroup disabled><option></option></optgroup></optgroup>",
result: { attr: null, idl: false, pseudo: true } },
{ html: "<optgroup disabled><optgroup><option></option></optgroup></optgroup>",
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup disabled><optgroup><option disabled></option></optgroup></optgroup>",
result: { attr: "", idl: true, pseudo: true } },
// Dynamic checks: changing disable value.
{ html: "<option></option>",
modifier: function(c) { c.querySelector('option').disabled = true; },
result: { attr: "", idl: true, pseudo: true } },
{ html: "<option disabled></option>",
modifier: function(c) { c.querySelector('option').disabled = false; },
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup><option></option></otpgroup>",
modifier: function(c) { c.querySelector('optgroup').disabled = true; },
result: { attr: null, idl: false, pseudo: true } },
{ html: "<optgroup><option disabled></option></optgroup>",
modifier: function(c) { c.querySelector('option').disabled = false; },
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup disabled><option disabled></option></optgroup>",
modifier: function(c) { c.querySelector('optgroup').disabled = false; },
result: { attr: "", idl: true, pseudo: true } },
{ html: "<optgroup disabled><option disabled></option></optgroup>",
modifier: function(c) { c.querySelector('option').disabled = false; },
result: { attr: null, idl: false, pseudo: true } },
{ html: "<optgroup disabled><option disabled></option></optgroup>",
modifier: function(c) { c.querySelector('optgroup').disabled = c.querySelector('option').disabled = false; },
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup disabled><option></option></optgroup>",
modifier: function(c) { c.querySelector('optgroup').disabled = false; },
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup><optgroup disabled><option></option></optgroup></optgroup>",
modifier: function(c) { c.querySelector('optgroup[disabled]').disabled = false; },
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup disabled><optgroup><option></option></optgroup></optgroup>",
modifier: function(c) { c.querySelector('optgroup[disabled]').disabled = false; },
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup disabled><optgroup><option disabled></option></optgroup></optgroup>",
modifier: function(c) { c.querySelector('optgroup').disabled = false; },
result: { attr: "", idl: true, pseudo: true } },
{ html: "<optgroup disabled><optgroup><option disabled></option></optgroup></optgroup>",
modifier: function(c) { c.querySelector('option').disabled = false; },
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup disabled><optgroup><option disabled></option></optgroup></optgroup>",
modifier: function(c) { c.querySelector('option').disabled = c.querySelector('option').disabled = false; },
result: { attr: null, idl: false, pseudo: false } },
// Dynamic checks: moving option element.
{ html: "<optgroup id='a'><option></option></optgroup><optgroup id='b'></optgroup>",
modifier: function(c) { c.querySelector('#b').appendChild(c.querySelector('option')); },
result: { attr: null, idl: false, pseudo: false } },
{ html: "<optgroup id='a'><option disabled></option></optgroup><optgroup id='b'></optgroup>",
modifier: function(c) { c.querySelector('#b').appendChild(c.querySelector('option')); },
result: { attr: "", idl: true, pseudo: true } },
{ html: "<optgroup id='a'><option></option></optgroup><optgroup disabled id='b'></optgroup>",
modifier: function(c) { c.querySelector('#b').appendChild(c.querySelector('option')); },
result: { attr: null, idl: false, pseudo: true } },
{ html: "<optgroup disabled id='a'><option></option></optgroup><optgroup id='b'></optgroup>",
modifier: function(c) { c.querySelector('#b').appendChild(c.querySelector('option')); },
result: { attr: null, idl: false, pseudo: false } },
];
var content = document.getElementById('content');
testCases.forEach(function(testCase) {
var result = testCase.result;
content.innerHTML = testCase.html;
if (testCase.modifier !== undefined) {
testCase.modifier(content);
}
var option = content.querySelector('option');
is(option.getAttribute('disabled'), result.attr, "disabled content attribute value should be " + result.attr);
is(option.disabled, result.idl, "disabled idl attribute value should be " + result.idl);
is(option.mozMatchesSelector(":disabled"), result.pseudo, ":disabled state should be " + result.pseudo);
is(option.mozMatchesSelector(":enabled"), !result.pseudo, ":enabled state should be " + !result.pseudo);
content.innerHTML = "";
});
</script>
</pre>
</body>
</html>