mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Implement support for :disabled CSS selector
This commit is contained in:
parent
40048b791c
commit
7771350898
7 changed files with 134 additions and 6 deletions
|
@ -398,6 +398,12 @@ impl<'le> TElement for LayoutElement<'le> {
|
||||||
fn get_id(&self) -> Option<Atom> {
|
fn get_id(&self) -> Option<Atom> {
|
||||||
unsafe { self.element.get_attr_atom_for_layout(&namespace::Null, "id") }
|
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_content(content_list: &content::T) -> String {
|
fn get_content(content_list: &content::T) -> String {
|
||||||
|
|
|
@ -257,6 +257,8 @@ pub trait AttributeHandlers {
|
||||||
fn set_atomic_attribute(&self, name: &str, value: DOMString);
|
fn set_atomic_attribute(&self, name: &str, value: DOMString);
|
||||||
|
|
||||||
// http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes
|
// 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 get_url_attribute(&self, name: &str) -> DOMString;
|
||||||
fn set_url_attribute(&self, name: &str, value: DOMString);
|
fn set_url_attribute(&self, name: &str, value: DOMString);
|
||||||
fn get_string_attribute(&self, name: &str) -> 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);
|
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 {
|
fn get_url_attribute(&self, name: &str) -> DOMString {
|
||||||
// XXX Resolve URL.
|
// XXX Resolve URL.
|
||||||
self.get_string_attribute(name)
|
self.get_string_attribute(name)
|
||||||
|
@ -664,7 +685,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
|
||||||
// http://dom.spec.whatwg.org/#dom-element-hasattribute
|
// http://dom.spec.whatwg.org/#dom-element-hasattribute
|
||||||
fn HasAttribute(&self,
|
fn HasAttribute(&self,
|
||||||
name: DOMString) -> bool {
|
name: DOMString) -> bool {
|
||||||
self.GetAttribute(name).is_some()
|
self.has_attribute(name.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://dom.spec.whatwg.org/#dom-element-hasattributens
|
// http://dom.spec.whatwg.org/#dom-element-hasattributens
|
||||||
|
@ -925,4 +946,8 @@ 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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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::{ElementCast, TextCast, NodeCast, ElementDerived};
|
||||||
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived};
|
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived};
|
||||||
use dom::bindings::codegen::InheritTypes::{ProcessingInstructionCast, EventTargetCast};
|
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::error::{ErrorResult, Fallible, NotFound, HierarchyRequest, Syntax};
|
||||||
use dom::bindings::global::{GlobalRef, Window};
|
use dom::bindings::global::{GlobalRef, Window};
|
||||||
use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, Root, OptionalUnrootable};
|
use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, Root, OptionalUnrootable};
|
||||||
|
@ -123,8 +125,10 @@ bitflags! {
|
||||||
flags NodeFlags: u8 {
|
flags NodeFlags: u8 {
|
||||||
#[doc = "Specifies whether this node is in a document."]
|
#[doc = "Specifies whether this node is in a document."]
|
||||||
static IsInDoc = 0x01,
|
static IsInDoc = 0x01,
|
||||||
#[doc = "Specifies whether this node is hover state for this node"]
|
#[doc = "Specifies whether this node is in hover state."]
|
||||||
static InHoverState = 0x02
|
static InHoverState = 0x02,
|
||||||
|
#[doc = "Specifies whether this node is in disabled state."]
|
||||||
|
static InDisabledState = 0x04
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,6 +387,9 @@ pub trait NodeHelpers {
|
||||||
fn get_hover_state(&self) -> bool;
|
fn get_hover_state(&self) -> bool;
|
||||||
fn set_hover_state(&self, state: bool);
|
fn set_hover_state(&self, state: bool);
|
||||||
|
|
||||||
|
fn get_disabled_state(&self) -> bool;
|
||||||
|
fn set_disabled_state(&self, state: bool);
|
||||||
|
|
||||||
fn dump(&self);
|
fn dump(&self);
|
||||||
fn dump_indent(&self, indent: uint);
|
fn dump_indent(&self, indent: uint);
|
||||||
fn debug_str(&self) -> String;
|
fn debug_str(&self) -> String;
|
||||||
|
@ -500,6 +507,18 @@ 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterates over this node and all its descendants, in preorder.
|
/// Iterates over this node and all its descendants, in preorder.
|
||||||
fn traverse_preorder<'a>(&'a self) -> TreeIterator<'a> {
|
fn traverse_preorder<'a>(&'a self) -> TreeIterator<'a> {
|
||||||
let mut nodes = vec!();
|
let mut nodes = vec!();
|
||||||
|
@ -728,12 +747,16 @@ impl LayoutNodeHelpers for JS<Node> {
|
||||||
|
|
||||||
pub trait RawLayoutNodeHelpers {
|
pub trait RawLayoutNodeHelpers {
|
||||||
unsafe fn get_hover_state_for_layout(&self) -> bool;
|
unsafe fn get_hover_state_for_layout(&self) -> bool;
|
||||||
|
unsafe fn get_disabled_state_for_layout(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawLayoutNodeHelpers for Node {
|
impl RawLayoutNodeHelpers for Node {
|
||||||
unsafe fn get_hover_state_for_layout(&self) -> bool {
|
unsafe fn get_hover_state_for_layout(&self) -> bool {
|
||||||
self.flags.deref().borrow().contains(InHoverState)
|
self.flags.deref().borrow().contains(InHoverState)
|
||||||
}
|
}
|
||||||
|
unsafe fn get_disabled_state_for_layout(&self) -> bool {
|
||||||
|
self.flags.deref().borrow().contains(InDisabledState)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1966,3 +1989,47 @@ 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);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
},
|
||||||
|
_ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,13 +12,14 @@ use dom::bindings::js::OptionalRootable;
|
||||||
use dom::bindings::utils::Reflectable;
|
use dom::bindings::utils::Reflectable;
|
||||||
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
|
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
|
||||||
use dom::document::{Document, HTMLDocument, DocumentHelpers};
|
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_, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
||||||
use dom::event::Event;
|
use dom::event::Event;
|
||||||
use dom::uievent::UIEvent;
|
use dom::uievent::UIEvent;
|
||||||
use dom::eventtarget::{EventTarget, EventTargetHelpers};
|
use dom::eventtarget::{EventTarget, EventTargetHelpers};
|
||||||
use dom::node;
|
use dom::node;
|
||||||
use dom::node::{Node, NodeHelpers};
|
use dom::node::{ElementNodeTypeId, Node, NodeHelpers};
|
||||||
use dom::window::{TimerId, Window, WindowHelpers};
|
use dom::window::{TimerId, Window, WindowHelpers};
|
||||||
use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress};
|
use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress};
|
||||||
use html::hubbub_html_parser::HtmlParserResult;
|
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 {
|
impl ScriptTask {
|
||||||
/// Creates a new script task.
|
/// Creates a new script task.
|
||||||
pub fn new(id: PipelineId,
|
pub fn new(id: PipelineId,
|
||||||
|
@ -691,6 +710,8 @@ impl ScriptTask {
|
||||||
match maybe_node {
|
match maybe_node {
|
||||||
Some(node) => {
|
Some(node) => {
|
||||||
debug!("clicked on {:s}", node.debug_str());
|
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() {
|
match *page.frame() {
|
||||||
Some(ref frame) => {
|
Some(ref frame) => {
|
||||||
let window = frame.window.root();
|
let window = frame.window.root();
|
||||||
|
|
|
@ -27,5 +27,6 @@ pub trait TElement {
|
||||||
fn get_namespace<'a>(&'a self) -> &'a Namespace;
|
fn get_namespace<'a>(&'a self) -> &'a Namespace;
|
||||||
fn get_hover_state(&self) -> bool;
|
fn get_hover_state(&self) -> bool;
|
||||||
fn get_id(&self) -> Option<Atom>;
|
fn get_id(&self) -> Option<Atom>;
|
||||||
|
fn get_disabled_state(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -778,6 +778,12 @@ fn matches_simple_selector<E:TElement,
|
||||||
let elem = element.as_element();
|
let elem = element.as_element();
|
||||||
elem.get_hover_state()
|
elem.get_hover_state()
|
||||||
},
|
},
|
||||||
|
// http://www.whatwg.org/html/#selector-disabled
|
||||||
|
Disabled => {
|
||||||
|
*shareable = false;
|
||||||
|
let elem = element.as_element();
|
||||||
|
elem.get_disabled_state()
|
||||||
|
},
|
||||||
FirstChild => {
|
FirstChild => {
|
||||||
*shareable = false;
|
*shareable = false;
|
||||||
matches_first_child(element)
|
matches_first_child(element)
|
||||||
|
|
|
@ -77,6 +77,7 @@ pub enum SimpleSelector {
|
||||||
Link,
|
Link,
|
||||||
Visited,
|
Visited,
|
||||||
Hover,
|
Hover,
|
||||||
|
Disabled,
|
||||||
FirstChild, LastChild, OnlyChild,
|
FirstChild, LastChild, OnlyChild,
|
||||||
// Empty,
|
// Empty,
|
||||||
Root,
|
Root,
|
||||||
|
@ -218,7 +219,7 @@ fn compute_specificity(mut selector: &CompoundSelector,
|
||||||
&ClassSelector(..)
|
&ClassSelector(..)
|
||||||
| &AttrExists(..) | &AttrEqual(..) | &AttrIncludes(..) | &AttrDashMatch(..)
|
| &AttrExists(..) | &AttrEqual(..) | &AttrIncludes(..) | &AttrDashMatch(..)
|
||||||
| &AttrPrefixMatch(..) | &AttrSubstringMatch(..) | &AttrSuffixMatch(..)
|
| &AttrPrefixMatch(..) | &AttrSubstringMatch(..) | &AttrSuffixMatch(..)
|
||||||
| &AnyLink | &Link | &Visited | &Hover
|
| &AnyLink | &Link | &Visited | &Hover | &Disabled
|
||||||
| &FirstChild | &LastChild | &OnlyChild | &Root
|
| &FirstChild | &LastChild | &OnlyChild | &Root
|
||||||
// | &Empty | &Lang(*)
|
// | &Empty | &Lang(*)
|
||||||
| &NthChild(..) | &NthLastChild(..)
|
| &NthChild(..) | &NthLastChild(..)
|
||||||
|
@ -479,6 +480,7 @@ fn parse_simple_pseudo_class(name: &str) -> Option<SimpleSelector> {
|
||||||
"link" => Some(Link),
|
"link" => Some(Link),
|
||||||
"visited" => Some(Visited),
|
"visited" => Some(Visited),
|
||||||
"hover" => Some(Hover),
|
"hover" => Some(Hover),
|
||||||
|
"disabled" => Some(Disabled),
|
||||||
"first-child" => Some(FirstChild),
|
"first-child" => Some(FirstChild),
|
||||||
"last-child" => Some(LastChild),
|
"last-child" => Some(LastChild),
|
||||||
"only-child" => Some(OnlyChild),
|
"only-child" => Some(OnlyChild),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue