Merge pull request #2907 from brunoabinader/css-selectors-disabled-enabled

Implement CSS selectors :enabled & :disabled (incl. HTML Elements support)
This commit is contained in:
Simon Sapin 2014-08-05 19:01:38 +01:00
commit b65ed02941
27 changed files with 1131 additions and 40 deletions

View file

@ -398,6 +398,18 @@ impl<'le> TElement for LayoutElement<'le> {
fn get_id(&self) -> Option<Atom> {
unsafe { self.element.get_attr_atom_for_layout(&namespace::Null, "id") }
}
fn get_disabled_state(&self) -> bool {
unsafe {
self.element.node.get_disabled_state_for_layout()
}
}
fn get_enabled_state(&self) -> bool {
unsafe {
self.element.node.get_enabled_state_for_layout()
}
}
}
fn get_content(content_list: &content::T) -> String {

View file

@ -257,6 +257,8 @@ pub trait AttributeHandlers {
fn set_atomic_attribute(&self, name: &str, value: DOMString);
// http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
fn has_attribute(&self, name: &str) -> bool;
fn set_bool_attribute(&self, name: &str, value: bool);
fn get_url_attribute(&self, name: &str) -> DOMString;
fn set_url_attribute(&self, name: &str, value: DOMString);
fn get_string_attribute(&self, name: &str) -> DOMString;
@ -385,6 +387,25 @@ impl<'a> AttributeHandlers for JSRef<'a, Element> {
self.set_attribute(name, value);
}
fn has_attribute(&self, name: &str) -> bool {
let name = match self.html_element_in_html_document() {
true => name.to_ascii_lower(),
false => name.to_string()
};
self.deref().attrs.borrow().iter().map(|attr| attr.root()).any(|attr| {
name == attr.local_name && attr.namespace == Null
})
}
fn set_bool_attribute(&self, name: &str, value: bool) {
if self.has_attribute(name) == value { return; }
if value {
self.set_string_attribute(name, String::new());
} else {
self.remove_attribute(Null, name);
}
}
fn get_url_attribute(&self, name: &str) -> DOMString {
// XXX Resolve URL.
self.get_string_attribute(name)
@ -664,7 +685,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
// http://dom.spec.whatwg.org/#dom-element-hasattribute
fn HasAttribute(&self,
name: DOMString) -> bool {
self.GetAttribute(name).is_some()
self.has_attribute(name.as_slice())
}
// http://dom.spec.whatwg.org/#dom-element-hasattributens
@ -925,4 +946,12 @@ impl<'a> style::TElement for JSRef<'a, Element> {
}
})
}
fn get_disabled_state(&self) -> bool {
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.get_disabled_state()
}
fn get_enabled_state(&self) -> bool {
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.get_enabled_state()
}
}

View file

@ -72,6 +72,32 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLAnchorElement> {
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"href" => node.set_enabled_state(true),
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"href" => node.set_enabled_state(false),
_ => ()
}
}
fn handle_event(&self, event: &JSRef<Event>) {
match self.super_type() {
Some(s) => {

View file

@ -4,13 +4,15 @@
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLAreaElementDerived;
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, NodeCast};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLAreaElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
#[deriving(Encodable)]
@ -37,6 +39,39 @@ impl HTMLAreaElement {
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLAreaElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"href" => node.set_enabled_state(true),
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"href" => node.set_enabled_state(false),
_ => ()
}
}
}
impl Reflectable for HTMLAreaElement {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector()

View file

@ -4,15 +4,17 @@
use dom::bindings::codegen::Bindings::HTMLButtonElementBinding;
use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
use dom::bindings::codegen::InheritTypes::HTMLButtonElementDerived;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLButtonElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLButtonElementTypeId;
use dom::element::{AttributeHandlers, Element, HTMLButtonElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, window_from_node};
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
#[deriving(Encodable)]
@ -44,6 +46,82 @@ impl<'a> HTMLButtonElementMethods for JSRef<'a, HTMLButtonElement> {
let window = window_from_node(self).root();
ValidityState::new(&*window)
}
// http://www.whatwg.org/html/#dom-fe-disabled
fn Disabled(&self) -> bool {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.has_attribute("disabled")
}
// http://www.whatwg.org/html/#dom-fe-disabled
fn SetDisabled(&self, disabled: bool) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_bool_attribute("disabled", disabled)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLButtonElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(true);
node.set_enabled_state(false);
},
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(false);
node.set_enabled_state(true);
node.check_ancestors_disabled_state_for_form_control();
},
_ => ()
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.bind_to_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.check_ancestors_disabled_state_for_form_control();
}
fn unbind_from_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.unbind_from_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
node.check_ancestors_disabled_state_for_form_control();
} else {
node.check_disabled_attribute();
}
}
}
impl Reflectable for HTMLButtonElement {

View file

@ -5,15 +5,18 @@
use dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding;
use dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding::HTMLFieldSetElementMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLFieldSetElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLLegendElementDerived};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::{Element, HTMLFieldSetElementTypeId};
use dom::element::{AttributeHandlers, Element, HTMLFieldSetElementTypeId, HTMLButtonElementTypeId};
use dom::element::{HTMLInputElementTypeId, HTMLSelectElementTypeId, HTMLTextAreaElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlcollection::{HTMLCollection, CollectionFilter};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, window_from_node};
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
use servo_util::str::{DOMString, StaticStringVec};
#[deriving(Encodable)]
@ -62,6 +65,89 @@ impl<'a> HTMLFieldSetElementMethods for JSRef<'a, HTMLFieldSetElement> {
let window = window_from_node(self).root();
ValidityState::new(&*window)
}
// http://www.whatwg.org/html/#dom-fieldset-disabled
fn Disabled(&self) -> bool {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.has_attribute("disabled")
}
// http://www.whatwg.org/html/#dom-fieldset-disabled
fn SetDisabled(&self, disabled: bool) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_bool_attribute("disabled", disabled)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLFieldSetElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(true);
node.set_enabled_state(false);
let maybe_legend = node.children().find(|node| node.is_htmllegendelement());
let filtered: Vec<JSRef<Node>> = node.children().filter(|child| {
maybe_legend.map_or(true, |legend| legend != *child)
}).collect();
for descendant in filtered.iter().flat_map(|child| child.traverse_preorder()) {
match descendant.type_id() {
ElementNodeTypeId(HTMLButtonElementTypeId) |
ElementNodeTypeId(HTMLInputElementTypeId) |
ElementNodeTypeId(HTMLSelectElementTypeId) |
ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
descendant.set_disabled_state(true);
descendant.set_enabled_state(false);
},
_ => ()
}
}
},
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(false);
node.set_enabled_state(true);
let maybe_legend = node.children().find(|node| node.is_htmllegendelement());
let filtered: Vec<JSRef<Node>> = node.children().filter(|child| {
maybe_legend.map_or(true, |legend| legend != *child)
}).collect();
for descendant in filtered.iter().flat_map(|child| child.traverse_preorder()) {
match descendant.type_id() {
ElementNodeTypeId(HTMLButtonElementTypeId) |
ElementNodeTypeId(HTMLInputElementTypeId) |
ElementNodeTypeId(HTMLSelectElementTypeId) |
ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
descendant.check_disabled_attribute();
descendant.check_ancestors_disabled_state_for_form_control();
},
_ => ()
}
}
},
_ => ()
}
}
}
impl Reflectable for HTMLFieldSetElement {

View file

@ -3,14 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::HTMLInputElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLInputElementDerived;
use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLInputElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLInputElementTypeId;
use dom::element::{AttributeHandlers, Element, HTMLInputElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
#[deriving(Encodable)]
@ -37,6 +40,84 @@ impl HTMLInputElement {
}
}
impl<'a> HTMLInputElementMethods for JSRef<'a, HTMLInputElement> {
// http://www.whatwg.org/html/#dom-fe-disabled
fn Disabled(&self) -> bool {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.has_attribute("disabled")
}
// http://www.whatwg.org/html/#dom-fe-disabled
fn SetDisabled(&self, disabled: bool) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_bool_attribute("disabled", disabled)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLInputElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(true);
node.set_enabled_state(false);
},
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(false);
node.set_enabled_state(true);
node.check_ancestors_disabled_state_for_form_control();
},
_ => ()
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.bind_to_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.check_ancestors_disabled_state_for_form_control();
}
fn unbind_from_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.unbind_from_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
node.check_ancestors_disabled_state_for_form_control();
} else {
node.check_disabled_attribute();
}
}
}
impl Reflectable for HTMLInputElement {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector()

View file

@ -4,13 +4,15 @@
use dom::bindings::codegen::Bindings::HTMLLinkElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLLinkElementDerived;
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, NodeCast};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLLinkElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
#[deriving(Encodable)]
@ -37,6 +39,39 @@ impl HTMLLinkElement {
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLLinkElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"href" => node.set_enabled_state(true),
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"href" => node.set_enabled_state(false),
_ => ()
}
}
}
impl Reflectable for HTMLLinkElement {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector()

View file

@ -3,14 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::HTMLOptGroupElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementDerived;
use dom::bindings::codegen::Bindings::HTMLOptGroupElementBinding::HTMLOptGroupElementMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLOptGroupElementDerived, HTMLOptionElementDerived};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLOptGroupElementTypeId;
use dom::element::{AttributeHandlers, Element, HTMLOptGroupElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
#[deriving(Encodable)]
@ -37,6 +40,66 @@ impl HTMLOptGroupElement {
}
}
impl<'a> HTMLOptGroupElementMethods for JSRef<'a, HTMLOptGroupElement> {
// http://www.whatwg.org/html#dom-optgroup-disabled
fn Disabled(&self) -> bool {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.has_attribute("disabled")
}
// http://www.whatwg.org/html#dom-optgroup-disabled
fn SetDisabled(&self, disabled: bool) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_bool_attribute("disabled", disabled)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLOptGroupElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(true);
node.set_enabled_state(false);
for child in node.children().filter(|child| child.is_htmloptionelement()) {
child.set_disabled_state(true);
child.set_enabled_state(false);
}
},
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(false);
node.set_enabled_state(true);
for child in node.children().filter(|child| child.is_htmloptionelement()) {
child.check_disabled_attribute();
}
},
_ => ()
}
}
}
impl Reflectable for HTMLOptGroupElement {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector()

View file

@ -3,14 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding;
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::HTMLOptionElementDerived;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLOptionElementTypeId;
use dom::element::{AttributeHandlers, Element, HTMLOptionElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
#[deriving(Encodable)]
@ -37,6 +40,84 @@ impl HTMLOptionElement {
}
}
impl<'a> HTMLOptionElementMethods for JSRef<'a, HTMLOptionElement> {
// http://www.whatwg.org/html/#dom-option-disabled
fn Disabled(&self) -> bool {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.has_attribute("disabled")
}
// http://www.whatwg.org/html/#dom-option-disabled
fn SetDisabled(&self, disabled: bool) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_bool_attribute("disabled", disabled)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLOptionElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(true);
node.set_enabled_state(false);
},
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(false);
node.set_enabled_state(true);
node.check_parent_disabled_state_for_option();
},
_ => ()
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.bind_to_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.check_parent_disabled_state_for_option();
}
fn unbind_from_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.unbind_from_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
if node.parent_node().is_some() {
node.check_parent_disabled_state_for_option();
} else {
node.check_disabled_attribute();
}
}
}
impl Reflectable for HTMLOptionElement {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector()

View file

@ -4,17 +4,19 @@
use dom::bindings::codegen::Bindings::HTMLSelectElementBinding;
use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods;
use dom::bindings::codegen::InheritTypes::HTMLSelectElementDerived;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLSelectElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::codegen::UnionTypes::HTMLElementOrLong::HTMLElementOrLong;
use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement::HTMLOptionElementOrHTMLOptGroupElement;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLSelectElementTypeId;
use dom::element::{AttributeHandlers, Element, HTMLSelectElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId, window_from_node};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId, window_from_node};
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
#[deriving(Encodable)]
@ -50,6 +52,82 @@ impl<'a> HTMLSelectElementMethods for JSRef<'a, HTMLSelectElement> {
// Note: this function currently only exists for test_union.html.
fn Add(&self, _element: HTMLOptionElementOrHTMLOptGroupElement, _before: Option<HTMLElementOrLong>) {
}
// http://www.whatwg.org/html/#dom-fe-disabled
fn Disabled(&self) -> bool {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.has_attribute("disabled")
}
// http://www.whatwg.org/html/#dom-fe-disabled
fn SetDisabled(&self, disabled: bool) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_bool_attribute("disabled", disabled)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLSelectElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(true);
node.set_enabled_state(false);
},
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(false);
node.set_enabled_state(true);
node.check_ancestors_disabled_state_for_form_control();
},
_ => ()
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.bind_to_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.check_ancestors_disabled_state_for_form_control();
}
fn unbind_from_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.unbind_from_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
node.check_ancestors_disabled_state_for_form_control();
} else {
node.check_disabled_attribute();
}
}
}
impl Reflectable for HTMLSelectElement {

View file

@ -3,14 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementDerived;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::HTMLTextAreaElementTypeId;
use dom::element::{AttributeHandlers, Element, HTMLTextAreaElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, ElementNodeTypeId};
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, ElementNodeTypeId};
use dom::virtualmethods::VirtualMethods;
use servo_util::str::DOMString;
#[deriving(Encodable)]
@ -37,6 +40,84 @@ impl HTMLTextAreaElement {
}
}
impl<'a> HTMLTextAreaElementMethods for JSRef<'a, HTMLTextAreaElement> {
// http://www.whatwg.org/html/#dom-fe-disabled
fn Disabled(&self) -> bool {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.has_attribute("disabled")
}
// http://www.whatwg.org/html/#dom-fe-disabled
fn SetDisabled(&self, disabled: bool) {
let elem: &JSRef<Element> = ElementCast::from_ref(self);
elem.set_bool_attribute("disabled", disabled)
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLTextAreaElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.after_set_attr(name.clone(), value.clone()),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(true);
node.set_enabled_state(false);
},
_ => ()
}
}
fn before_remove_attr(&self, name: DOMString, value: DOMString) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(name.clone(), value),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
match name.as_slice() {
"disabled" => {
node.set_disabled_state(false);
node.set_enabled_state(true);
node.check_ancestors_disabled_state_for_form_control();
},
_ => ()
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.bind_to_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.check_ancestors_disabled_state_for_form_control();
}
fn unbind_from_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.unbind_from_tree(tree_in_doc),
_ => (),
}
let node: &JSRef<Node> = NodeCast::from_ref(self);
if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
node.check_ancestors_disabled_state_for_form_control();
} else {
node.check_disabled_attribute();
}
}
}
impl Reflectable for HTMLTextAreaElement {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector()

View file

@ -16,6 +16,8 @@ use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTy
use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast, ElementDerived};
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived};
use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast};
use dom::bindings::codegen::InheritTypes::{HTMLLegendElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementDerived;
use dom::bindings::error::{ErrorResult, Fallible, NotFound, HierarchyRequest, Syntax};
use dom::bindings::global::{GlobalRef, Window};
use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, Root, OptionalUnrootable};
@ -30,7 +32,10 @@ use dom::document::{Document, DocumentHelpers, HTMLDocument, NonHTMLDocument};
use dom::documentfragment::DocumentFragment;
use dom::documenttype::DocumentType;
use dom::element::{AttributeHandlers, Element, ElementTypeId};
use dom::element::{HTMLAnchorElementTypeId, ElementHelpers};
use dom::element::{HTMLAnchorElementTypeId, HTMLButtonElementTypeId, ElementHelpers};
use dom::element::{HTMLInputElementTypeId, HTMLSelectElementTypeId};
use dom::element::{HTMLTextAreaElementTypeId, HTMLOptGroupElementTypeId};
use dom::element::{HTMLOptionElementTypeId, HTMLFieldSetElementTypeId};
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::nodelist::{NodeList};
use dom::processinginstruction::ProcessingInstruction;
@ -123,8 +128,12 @@ bitflags! {
flags NodeFlags: u8 {
#[doc = "Specifies whether this node is in a document."]
static IsInDoc = 0x01,
#[doc = "Specifies whether this node is hover state for this node"]
static InHoverState = 0x02
#[doc = "Specifies whether this node is in hover state."]
static InHoverState = 0x02,
#[doc = "Specifies whether this node is in disabled state."]
static InDisabledState = 0x04,
#[doc = "Specifies whether this node is in enabled state."]
static InEnabledState = 0x08
}
}
@ -383,6 +392,12 @@ pub trait NodeHelpers {
fn get_hover_state(&self) -> bool;
fn set_hover_state(&self, state: bool);
fn get_disabled_state(&self) -> bool;
fn set_disabled_state(&self, state: bool);
fn get_enabled_state(&self) -> bool;
fn set_enabled_state(&self, state: bool);
fn dump(&self);
fn dump_indent(&self, indent: uint);
fn debug_str(&self) -> String;
@ -500,6 +515,30 @@ impl<'a> NodeHelpers for JSRef<'a, Node> {
}
}
fn get_disabled_state(&self) -> bool {
self.flags.deref().borrow().contains(InDisabledState)
}
fn set_disabled_state(&self, state: bool) {
if state {
self.flags.deref().borrow_mut().insert(InDisabledState);
} else {
self.flags.deref().borrow_mut().remove(InDisabledState);
}
}
fn get_enabled_state(&self) -> bool {
self.flags.deref().borrow().contains(InEnabledState)
}
fn set_enabled_state(&self, state: bool) {
if state {
self.flags.deref().borrow_mut().insert(InEnabledState);
} else {
self.flags.deref().borrow_mut().remove(InEnabledState);
}
}
/// Iterates over this node and all its descendants, in preorder.
fn traverse_preorder<'a>(&'a self) -> TreeIterator<'a> {
let mut nodes = vec!();
@ -728,12 +767,20 @@ impl LayoutNodeHelpers for JS<Node> {
pub trait RawLayoutNodeHelpers {
unsafe fn get_hover_state_for_layout(&self) -> bool;
unsafe fn get_disabled_state_for_layout(&self) -> bool;
unsafe fn get_enabled_state_for_layout(&self) -> bool;
}
impl RawLayoutNodeHelpers for Node {
unsafe fn get_hover_state_for_layout(&self) -> bool {
self.flags.deref().borrow().contains(InHoverState)
}
unsafe fn get_disabled_state_for_layout(&self) -> bool {
self.flags.deref().borrow().contains(InDisabledState)
}
unsafe fn get_enabled_state_for_layout(&self) -> bool {
self.flags.deref().borrow().contains(InEnabledState)
}
}
@ -926,7 +973,7 @@ impl Node {
}
fn new_(type_id: NodeTypeId, doc: Option<JSRef<Document>>) -> Node {
Node {
let node = Node {
eventtarget: EventTarget::new_inherited(NodeTargetTypeId(type_id)),
type_id: type_id,
@ -941,7 +988,22 @@ impl Node {
flags: Traceable::new(RefCell::new(NodeFlags::new(type_id))),
layout_data: LayoutDataRef::new(),
};
match type_id {
// The following elements are enabled by default.
ElementNodeTypeId(HTMLButtonElementTypeId) |
ElementNodeTypeId(HTMLInputElementTypeId) |
ElementNodeTypeId(HTMLSelectElementTypeId) |
ElementNodeTypeId(HTMLTextAreaElementTypeId) |
ElementNodeTypeId(HTMLOptGroupElementTypeId) |
ElementNodeTypeId(HTMLOptionElementTypeId) |
//ElementNodeTypeId(HTMLMenuItemElementTypeId) |
ElementNodeTypeId(HTMLFieldSetElementTypeId) => {
node.flags.deref().borrow_mut().insert(InEnabledState);
},
_ => ()
}
node
}
// http://dom.spec.whatwg.org/#concept-node-adopt
@ -1966,3 +2028,51 @@ impl<'a> style::TNode<JSRef<'a, Element>> for JSRef<'a, Node> {
}
}
}
pub trait DisabledStateHelpers {
fn check_ancestors_disabled_state_for_form_control(&self);
fn check_parent_disabled_state_for_option(&self);
fn check_disabled_attribute(&self);
}
impl<'a> DisabledStateHelpers for JSRef<'a, Node> {
fn check_ancestors_disabled_state_for_form_control(&self) {
if self.get_disabled_state() { return; }
for ancestor in self.ancestors().filter(|ancestor| ancestor.is_htmlfieldsetelement()) {
if !ancestor.get_disabled_state() { continue; }
if ancestor.is_parent_of(self) {
self.set_disabled_state(true);
self.set_enabled_state(false);
return;
}
match ancestor.children().find(|child| child.is_htmllegendelement()) {
Some(ref legend) => {
// XXXabinader: should we save previous ancestor to avoid this iteration?
if self.ancestors().any(|ancestor| ancestor == *legend) { continue; }
},
None => ()
}
self.set_disabled_state(true);
self.set_enabled_state(false);
return;
}
}
fn check_parent_disabled_state_for_option(&self) {
if self.get_disabled_state() { return; }
match self.parent_node().root() {
Some(ref parent) if parent.is_htmloptgroupelement() && parent.get_disabled_state() => {
self.set_disabled_state(true);
self.set_enabled_state(false);
},
_ => ()
}
}
fn check_disabled_attribute(&self) {
let elem: &JSRef<'a, Element> = ElementCast::to_ref(self).unwrap();
let has_disabled_attrib = elem.has_attribute("disabled");
self.set_disabled_state(has_disabled_attrib);
self.set_enabled_state(!has_disabled_attrib);
}
}

View file

@ -5,28 +5,59 @@
use dom::attr::{AttrValue, StringAttrValue};
use dom::bindings::codegen::InheritTypes::ElementCast;
use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
use dom::bindings::codegen::InheritTypes::HTMLAreaElementCast;
use dom::bindings::codegen::InheritTypes::HTMLBodyElementCast;
use dom::bindings::codegen::InheritTypes::HTMLButtonElementCast;
use dom::bindings::codegen::InheritTypes::HTMLCanvasElementCast;
use dom::bindings::codegen::InheritTypes::HTMLElementCast;
use dom::bindings::codegen::InheritTypes::HTMLFieldSetElementCast;
use dom::bindings::codegen::InheritTypes::HTMLIFrameElementCast;
use dom::bindings::codegen::InheritTypes::HTMLImageElementCast;
use dom::bindings::codegen::InheritTypes::HTMLInputElementCast;
use dom::bindings::codegen::InheritTypes::HTMLLinkElementCast;
use dom::bindings::codegen::InheritTypes::HTMLObjectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementCast;
use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
use dom::bindings::js::JSRef;
use dom::element::Element;
use dom::element::{ElementTypeId, HTMLAnchorElementTypeId};
use dom::element::{HTMLBodyElementTypeId, HTMLCanvasElementTypeId};
use dom::element::{HTMLIFrameElementTypeId, HTMLImageElementTypeId};
use dom::element::{HTMLObjectElementTypeId, HTMLStyleElementTypeId};
use dom::element::ElementTypeId;
use dom::element::HTMLAnchorElementTypeId;
use dom::element::HTMLAreaElementTypeId;
use dom::element::HTMLBodyElementTypeId;
use dom::element::HTMLButtonElementTypeId;
use dom::element::HTMLCanvasElementTypeId;
use dom::element::HTMLFieldSetElementTypeId;
use dom::element::HTMLIFrameElementTypeId;
use dom::element::HTMLImageElementTypeId;
use dom::element::HTMLInputElementTypeId;
use dom::element::HTMLLinkElementTypeId;
use dom::element::HTMLObjectElementTypeId;
use dom::element::HTMLOptGroupElementTypeId;
use dom::element::HTMLOptionElementTypeId;
use dom::element::HTMLSelectElementTypeId;
use dom::element::HTMLStyleElementTypeId;
use dom::element::HTMLTextAreaElementTypeId;
use dom::event::Event;
use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmlareaelement::HTMLAreaElement;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlbuttonelement::HTMLButtonElement;
use dom::htmlcanvaselement::HTMLCanvasElement;
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmlimageelement::HTMLImageElement;
use dom::htmlinputelement::HTMLInputElement;
use dom::htmllinkelement::HTMLLinkElement;
use dom::htmlobjectelement::HTMLObjectElement;
use dom::htmloptgroupelement::HTMLOptGroupElement;
use dom::htmloptionelement::HTMLOptionElement;
use dom::htmlselectelement::HTMLSelectElement;
use dom::htmlstyleelement::HTMLStyleElement;
use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
use servo_util::str::DOMString;
@ -111,14 +142,26 @@ pub fn vtable_for<'a>(node: &'a JSRef<Node>) -> &'a VirtualMethods {
let element: &JSRef<HTMLAnchorElement> = HTMLAnchorElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLAreaElementTypeId) => {
let element: &JSRef<HTMLAreaElement> = HTMLAreaElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLBodyElementTypeId) => {
let element: &JSRef<HTMLBodyElement> = HTMLBodyElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLButtonElementTypeId) => {
let element: &JSRef<HTMLButtonElement> = HTMLButtonElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLCanvasElementTypeId) => {
let element: &JSRef<HTMLCanvasElement> = HTMLCanvasElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLFieldSetElementTypeId) => {
let element: &JSRef<HTMLFieldSetElement> = HTMLFieldSetElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLImageElementTypeId) => {
let element: &JSRef<HTMLImageElement> = HTMLImageElementCast::to_ref(node).unwrap();
element as &VirtualMethods
@ -127,14 +170,38 @@ pub fn vtable_for<'a>(node: &'a JSRef<Node>) -> &'a VirtualMethods {
let element: &JSRef<HTMLIFrameElement> = HTMLIFrameElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLInputElementTypeId) => {
let element: &JSRef<HTMLInputElement> = HTMLInputElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLLinkElementTypeId) => {
let element: &JSRef<HTMLLinkElement> = HTMLLinkElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLObjectElementTypeId) => {
let element: &JSRef<HTMLObjectElement> = HTMLObjectElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLOptGroupElementTypeId) => {
let element: &JSRef<HTMLOptGroupElement> = HTMLOptGroupElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLOptionElementTypeId) => {
let element: &JSRef<HTMLOptionElement> = HTMLOptionElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLSelectElementTypeId) => {
let element: &JSRef<HTMLSelectElement> = HTMLSelectElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLStyleElementTypeId) => {
let element: &JSRef<HTMLStyleElement> = HTMLStyleElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
let element: &JSRef<HTMLTextAreaElement> = HTMLTextAreaElementCast::to_ref(node).unwrap();
element as &VirtualMethods
}
ElementNodeTypeId(ElementTypeId) => {
let element: &JSRef<Element> = ElementCast::to_ref(node).unwrap();
element as &VirtualMethods

View file

@ -6,7 +6,7 @@
// http://www.whatwg.org/html/#htmlbuttonelement
interface HTMLButtonElement : HTMLElement {
// attribute boolean autofocus;
// attribute boolean disabled;
attribute boolean disabled;
//readonly attribute HTMLFormElement? form;
// attribute DOMString formAction;
// attribute DOMString formEnctype;

View file

@ -5,7 +5,7 @@
// http://www.whatwg.org/html/#htmlfieldsetelement
interface HTMLFieldSetElement : HTMLElement {
// attribute boolean disabled;
attribute boolean disabled;
//readonly attribute HTMLFormElement? form;
// attribute DOMString name;

View file

@ -12,7 +12,7 @@ interface HTMLInputElement : HTMLElement {
// attribute boolean defaultChecked;
// attribute boolean checked;
// attribute DOMString dirName;
// attribute boolean disabled;
attribute boolean disabled;
//readonly attribute HTMLFormElement? form;
//readonly attribute FileList? files;
// attribute DOMString formAction;

View file

@ -5,6 +5,6 @@
// http://www.whatwg.org/html/#htmloptgroupelement
interface HTMLOptGroupElement : HTMLElement {
// attribute boolean disabled;
attribute boolean disabled;
// attribute DOMString label;
};

View file

@ -6,7 +6,7 @@
// http://www.whatwg.org/html/#htmloptionelement
//[NamedConstructor=Option(optional DOMString text = "", optional DOMString value, optional boolean defaultSelected = false, optional boolean selected = false)]
interface HTMLOptionElement : HTMLElement {
// attribute boolean disabled;
attribute boolean disabled;
//readonly attribute HTMLFormElement? form;
// attribute DOMString label;
// attribute boolean defaultSelected;

View file

@ -6,7 +6,7 @@
// http://www.whatwg.org/html/#htmlselectelement
interface HTMLSelectElement : HTMLElement {
// attribute boolean autofocus;
// attribute boolean disabled;
attribute boolean disabled;
//readonly attribute HTMLFormElement? form;
// attribute boolean multiple;
// attribute DOMString name;

View file

@ -9,7 +9,7 @@ interface HTMLTextAreaElement : HTMLElement {
// attribute boolean autofocus;
// attribute unsigned long cols;
// attribute DOMString dirName;
// attribute boolean disabled;
attribute boolean disabled;
//readonly attribute HTMLFormElement? form;
// attribute DOMString inputMode;
// attribute long maxLength;

View file

@ -12,13 +12,14 @@ use dom::bindings::js::OptionalRootable;
use dom::bindings::utils::Reflectable;
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
use dom::document::{Document, HTMLDocument, DocumentHelpers};
use dom::element::{Element};
use dom::element::{Element, HTMLButtonElementTypeId, HTMLInputElementTypeId};
use dom::element::{HTMLSelectElementTypeId, HTMLTextAreaElementTypeId, HTMLOptionElementTypeId};
use dom::event::{Event_, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
use dom::event::Event;
use dom::uievent::UIEvent;
use dom::eventtarget::{EventTarget, EventTargetHelpers};
use dom::node;
use dom::node::{Node, NodeHelpers};
use dom::node::{ElementNodeTypeId, Node, NodeHelpers};
use dom::window::{TimerId, Window, WindowHelpers};
use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress};
use html::hubbub_html_parser::HtmlParserResult;
@ -193,6 +194,24 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> {
}
}
trait PrivateScriptTaskHelpers {
fn click_event_filter_by_disabled_state(&self) -> bool;
}
impl<'a> PrivateScriptTaskHelpers for JSRef<'a, Node> {
fn click_event_filter_by_disabled_state(&self) -> bool {
match self.type_id {
ElementNodeTypeId(HTMLButtonElementTypeId) |
ElementNodeTypeId(HTMLInputElementTypeId) |
// ElementNodeTypeId(HTMLKeygenElementTypeId) |
ElementNodeTypeId(HTMLOptionElementTypeId) |
ElementNodeTypeId(HTMLSelectElementTypeId) |
ElementNodeTypeId(HTMLTextAreaElementTypeId) if self.get_disabled_state() => true,
_ => false
}
}
}
impl ScriptTask {
/// Creates a new script task.
pub fn new(id: PipelineId,
@ -691,6 +710,8 @@ impl ScriptTask {
match maybe_node {
Some(node) => {
debug!("clicked on {:s}", node.debug_str());
// Prevent click event if form control element is disabled.
if node.click_event_filter_by_disabled_state() { return; }
match *page.frame() {
Some(ref frame) => {
let window = frame.window.root();

View file

@ -27,5 +27,7 @@ pub trait TElement {
fn get_namespace<'a>(&'a self) -> &'a Namespace;
fn get_hover_state(&self) -> bool;
fn get_id(&self) -> Option<Atom>;
fn get_disabled_state(&self) -> bool;
fn get_enabled_state(&self) -> bool;
}

View file

@ -778,6 +778,18 @@ fn matches_simple_selector<E:TElement,
let elem = element.as_element();
elem.get_hover_state()
},
// http://www.whatwg.org/html/#selector-disabled
Disabled => {
*shareable = false;
let elem = element.as_element();
elem.get_disabled_state()
},
// http://www.whatwg.org/html/#selector-enabled
Enabled => {
*shareable = false;
let elem = element.as_element();
elem.get_enabled_state()
},
FirstChild => {
*shareable = false;
matches_first_child(element)

View file

@ -77,6 +77,8 @@ pub enum SimpleSelector {
Link,
Visited,
Hover,
Disabled,
Enabled,
FirstChild, LastChild, OnlyChild,
// Empty,
Root,
@ -218,7 +220,7 @@ fn compute_specificity(mut selector: &CompoundSelector,
&ClassSelector(..)
| &AttrExists(..) | &AttrEqual(..) | &AttrIncludes(..) | &AttrDashMatch(..)
| &AttrPrefixMatch(..) | &AttrSubstringMatch(..) | &AttrSuffixMatch(..)
| &AnyLink | &Link | &Visited | &Hover
| &AnyLink | &Link | &Visited | &Hover | &Disabled | &Enabled
| &FirstChild | &LastChild | &OnlyChild | &Root
// | &Empty | &Lang(*)
| &NthChild(..) | &NthLastChild(..)
@ -479,6 +481,8 @@ fn parse_simple_pseudo_class(name: &str) -> Option<SimpleSelector> {
"link" => Some(Link),
"visited" => Some(Visited),
"hover" => Some(Hover),
"disabled" => Some(Disabled),
"enabled" => Some(Enabled),
"first-child" => Some(FirstChild),
"last-child" => Some(LastChild),
"only-child" => Some(OnlyChild),

View file

@ -55,6 +55,15 @@ function should_not_throw(f) {
}
}
function check_selector(elem, selector, matches) {
is(elem.matches(selector), matches);
}
function check_disabled_selector(elem, disabled) {
check_selector(elem, ":disabled", disabled);
check_selector(elem, ":enabled", !disabled);
}
var _test_complete = false;
var _test_timeout = 10000; //10 seconds
function finish() {

View file

@ -0,0 +1,181 @@
<!DOCTYPE html>
<html>
<head>
<title>Tests for :enabled and :disabled selectors</title>
<script src="harness.js"></script>
<script>
{ // Simple initialization checks.
var list, i, elem;
// Examples of elements which are never :enabled or :disabled.
list = ['div', 'p', 'body', 'head', 'span'];
for(i = 0; i < list.length; i++) {
elem = document.createElement(list[i]);
check_selector(elem, ":enabled", false);
check_selector(elem, ":disabled", false);
}
// Anchor, Area and Link are :enabled with an href, but never :disabled.
list = ["a", "area", "link"];
for(i = 0; i < list.length; i++) {
elem = document.createElement(list[i]);
check_selector(elem, ":enabled", false);
check_selector(elem, ":disabled", false);
elem.setAttribute("href", "");
check_selector(elem, ":enabled", true);
check_selector(elem, ":disabled", false);
}
// These are :enabled (and not :disabled) by default.
// XXX: Add 'menuitem' here whenever available.
list = ['button', 'input', 'select', 'textarea', 'optgroup', 'option', 'fieldset'];
for(i = 0; i < list.length; i++) {
elem = document.createElement(list[i]);
check_disabled_selector(elem, false);
}
}
{ // Document elements tests.
var click_count = 0;
var click_event = new Event('click', {bubbles: true, cancelable: true});
var list, elem1, elem2, elem3, elem4, elem5;
function on_click(ev) { click_count++; }
list = ['button', 'input', 'option', 'select', 'textarea'];
for(i = 0; i < list.length; i++) {
click_count = 0;
elem1 = document.getElementById(list[i] + "-1");
is(elem1.disabled, false);
elem1.addEventListener('click', on_click);
elem1.dispatchEvent(click_event);
is(click_count, 1);
elem2 = document.getElementById(list[i] + "-2");
is(elem2.disabled, true);
// Only user-generated click events are prevented.
elem2.addEventListener('click', on_click);
elem2.dispatchEvent(click_event);
is(click_count, 2);
// This should look disabled, though - missing UA's CSS for :disabled?
elem3 = document.getElementById(list[i] + "-3");
is(elem3.disabled, false);
if (list[i] == 'option') { continue; }
elem4 = document.getElementById(list[i] + "-4");
is(elem4.disabled, false);
// This should look disabled, though - missing UA's CSS for :disabled?
elem5 = document.getElementById(list[i] + "-5");
is(elem5.disabled, false);
}
}
{ // JS tests (Button, Input, Select, TextArea).
var list = ['button', 'input', 'select', 'textarea'];
var fieldset = document.createElement("fieldset");
fieldset.disabled = true;
var div = document.createElement("div");
var elem;
for(i = 0; i < list.length; i++) {
elem = document.createElement(list[i]);
check_disabled_selector(elem, false);
div.appendChild(elem);
check_disabled_selector(elem, false);
fieldset.appendChild(div);
check_disabled_selector(elem, true);
document.body.appendChild(fieldset);
check_disabled_selector(elem, true);
document.body.removeChild(fieldset);
check_disabled_selector(elem, true);
fieldset.removeChild(div);
check_disabled_selector(elem, false);
div.removeChild(elem);
check_disabled_selector(elem, false);
}
}
{ // JS tests (Option).
var optgroup = document.createElement("optgroup");
optgroup.disabled = true;
var option = document.createElement("option");
check_disabled_selector(option, false);
optgroup.appendChild(option);
check_disabled_selector(option, true);
document.body.appendChild(optgroup);
check_disabled_selector(option, true);
document.body.removeChild(optgroup);
check_disabled_selector(option, true);
optgroup.removeChild(option);
check_disabled_selector(option, false);
}
finish();
</script>
</head>
<body>
<button id="button-1"></button>
<button id="button-2" disabled></button>
<input id="input-1"></input>
<input id="input-2" disabled></input>
<option id="option-1"></option>
<option id="option-2" disabled></option>
<select id="select-1"></select>
<select id="select-2" disabled></select>
<textarea id="textarea-1"></textarea>
<textarea id="textarea-2" disabled></textarea>
<optgroup disabled>
<option id="option-3"></option>
</optgroup>
<fieldset disabled>
<fieldset>
<button id="button-3"></button>
<input id="input-3"></input>
<select id="select-3"></select>
<textarea id="textarea-3"></textarea>
</fieldset>
</fieldset>
<fieldset disabled>
<legend>
<button id="button-4"></button>
<input id="input-4"></input>
<select id="select-4"></select>
<textarea id="textarea-4"></textarea>
</legend>
</fieldset>
<fieldset disabled>
<legend></legend>
<legend>
<button id="button-5"></button>
<input id="input-5"></input>
<select id="select-5"></select>
<textarea id="textarea-5"></textarea>
</legend>
</fieldset>
</body>
</html>