mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +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> {
|
||||
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 {
|
||||
|
|
|
@ -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,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::{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};
|
||||
|
@ -123,8 +125,10 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,6 +387,9 @@ 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 dump(&self);
|
||||
fn dump_indent(&self, indent: uint);
|
||||
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.
|
||||
fn traverse_preorder<'a>(&'a self) -> TreeIterator<'a> {
|
||||
let mut nodes = vec!();
|
||||
|
@ -728,12 +747,16 @@ 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;
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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::{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();
|
||||
|
|
|
@ -27,5 +27,6 @@ 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -778,6 +778,12 @@ 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()
|
||||
},
|
||||
FirstChild => {
|
||||
*shareable = false;
|
||||
matches_first_child(element)
|
||||
|
|
|
@ -77,6 +77,7 @@ pub enum SimpleSelector {
|
|||
Link,
|
||||
Visited,
|
||||
Hover,
|
||||
Disabled,
|
||||
FirstChild, LastChild, OnlyChild,
|
||||
// Empty,
|
||||
Root,
|
||||
|
@ -218,7 +219,7 @@ fn compute_specificity(mut selector: &CompoundSelector,
|
|||
&ClassSelector(..)
|
||||
| &AttrExists(..) | &AttrEqual(..) | &AttrIncludes(..) | &AttrDashMatch(..)
|
||||
| &AttrPrefixMatch(..) | &AttrSubstringMatch(..) | &AttrSuffixMatch(..)
|
||||
| &AnyLink | &Link | &Visited | &Hover
|
||||
| &AnyLink | &Link | &Visited | &Hover | &Disabled
|
||||
| &FirstChild | &LastChild | &OnlyChild | &Root
|
||||
// | &Empty | &Lang(*)
|
||||
| &NthChild(..) | &NthLastChild(..)
|
||||
|
@ -479,6 +480,7 @@ fn parse_simple_pseudo_class(name: &str) -> Option<SimpleSelector> {
|
|||
"link" => Some(Link),
|
||||
"visited" => Some(Visited),
|
||||
"hover" => Some(Hover),
|
||||
"disabled" => Some(Disabled),
|
||||
"first-child" => Some(FirstChild),
|
||||
"last-child" => Some(LastChild),
|
||||
"only-child" => Some(OnlyChild),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue