author | Mounir Lamouri <mounir.lamouri@gmail.com> |
Fri, 01 Jun 2012 11:46:43 +0200 | |
changeset 95521 | 654fb530a96894be08e35b9eb713dea2b7d18be8 |
parent 95520 | 68f264782f5eaad4611915c0eec00be7ab6f246d |
child 95522 | 3e88a83dcb3743381c37a281be935fe9882b6a61 |
push id | 22819 |
push user | eakhgari@mozilla.com |
push date | Sat, 02 Jun 2012 18:40:08 +0000 |
treeherder | mozilla-central@f4a7c1a1f514 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bz |
bugs | 759666 |
milestone | 15.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/html/content/src/nsHTMLOptGroupElement.cpp +++ b/content/html/content/src/nsHTMLOptGroupElement.cpp @@ -49,16 +49,19 @@ public: virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor); virtual nsEventStates IntrinsicState() const; virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; virtual nsXPCClassInfo* GetClassInfo(); + virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, + const nsAttrValue* aValue, bool aNotify); + virtual nsIDOMNode* AsDOMNode() { return this; } virtual bool IsDisabled() const { return HasAttr(kNameSpaceID_None, nsGkAtoms::disabled); } protected: /** @@ -160,16 +163,36 @@ nsHTMLOptGroupElement::InsertChildAt(nsI void nsHTMLOptGroupElement::RemoveChildAt(PRUint32 aIndex, bool aNotify) { nsSafeOptionListMutation safeMutation(GetSelect(), this, nsnull, aIndex, 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 { nsEventStates state = nsGenericHTMLElement::IntrinsicState(); if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) { state |= NS_EVENT_STATE_DISABLED; state &= ~NS_EVENT_STATE_ENABLED;
--- a/content/html/content/src/nsHTMLOptionElement.cpp +++ b/content/html/content/src/nsHTMLOptionElement.cpp @@ -272,33 +272,67 @@ nsHTMLOptionElement::GetText(nsAString& } NS_IMETHODIMP 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 { nsEventStates state = nsGenericHTMLElement::IntrinsicState(); if (Selected()) { state |= NS_EVENT_STATE_CHECKED; } if (DefaultSelected()) { 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; } // Get the select content element that contains this option nsHTMLSelectElement* nsHTMLOptionElement::GetSelect()
--- a/content/html/content/src/nsHTMLOptionElement.h +++ b/content/html/content/src/nsHTMLOptionElement.h @@ -57,16 +57,22 @@ public: PRInt32 aModType) const; virtual nsresult BeforeSetAttr(PRInt32 aNamespaceID, nsIAtom* aName, const nsAttrValueOrString* aValue, bool aNotify); 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; virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const; nsresult CopyInnerTo(nsGenericElement* aDest) const; virtual nsXPCClassInfo* GetClassInfo();
--- a/content/html/content/test/forms/Makefile.in +++ b/content/html/content/test/forms/Makefile.in @@ -34,13 +34,14 @@ include $(topsrcdir)/config/rules.mk test_datalist_element.html \ test_form_attributes_reflection.html \ test_option_index_attribute.html \ test_progress_element.html \ test_form_attribute-1.html \ test_form_attribute-2.html \ test_form_attribute-3.html \ test_form_attribute-4.html \ + test_option_disabled.html \ $(NULL) libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/content/html/content/test/forms/test_option_disabled.html @@ -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>