diff options
Diffstat (limited to 'components/script/dom/element.rs')
-rw-r--r-- | components/script/dom/element.rs | 159 |
1 files changed, 68 insertions, 91 deletions
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 67844b9231b..779b802fc26 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -19,12 +19,14 @@ use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementM use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods; use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use dom::bindings::codegen::InheritTypes::{ElementTypeId, HTMLElementTypeId, NodeTypeId}; use dom::bindings::codegen::UnionTypes::NodeOrString; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; use dom::bindings::js::{JS, LayoutJS, MutNullableHeap}; use dom::bindings::js::{Root, RootedReference}; +use dom::bindings::trace::JSTraceable; use dom::bindings::xmlname::XMLName::InvalidXMLName; use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type}; use dom::characterdata::CharacterData; @@ -40,7 +42,8 @@ use dom::htmlcollection::HTMLCollection; use dom::htmlfieldsetelement::HTMLFieldSetElement; use dom::htmlfontelement::HTMLFontElement; use dom::htmliframeelement::HTMLIFrameElement; -use dom::htmlinputelement::{HTMLInputElement, RawLayoutHTMLInputElementHelpers}; +use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers}; +use dom::htmllabelelement::HTMLLabelElement; use dom::htmllegendelement::HTMLLegendElement; use dom::htmloptgroupelement::HTMLOptGroupElement; use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementLayoutHelpers}; @@ -62,8 +65,8 @@ use html5ever::serialize::TraversalScope; use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode}; use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks}; use selectors::matching::{DeclarationBlock, matches}; -use selectors::parser::parse_author_origin_selector_list_from_str; -use selectors::parser::{AttrSelector, NamespaceConstraint}; +use selectors::parser::{AttrSelector, NamespaceConstraint, parse_author_origin_selector_list_from_str}; +use selectors::states::*; use smallvec::VecLike; use std::ascii::AsciiExt; use std::borrow::{Cow, ToOwned}; @@ -77,28 +80,14 @@ use style::properties::DeclaredValue; use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size}; use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute}; use style::values::CSSFloat; -use style::values::specified::{self, CSSColor, CSSRGBA}; +use style::values::specified::{self, CSSColor, CSSRGBA, LengthOrPercentage}; use url::UrlParser; +use util::mem::HeapSizeOf; use util::str::{DOMString, LengthOrPercentageOrAuto}; -bitflags! { - #[doc = "Element Event States."] - #[derive(JSTraceable, HeapSizeOf)] - flags EventState: u8 { - #[doc = "The mouse is down on this element. \ - (https://html.spec.whatwg.org/multipage/#selector-active). \ - FIXME(#7333): set/unset this when appropriate"] - const IN_ACTIVE_STATE = 0x01, - #[doc = "This element has focus."] - const IN_FOCUS_STATE = 0x02, - #[doc = "The mouse is hovering over this element."] - const IN_HOVER_STATE = 0x04, - #[doc = "Content is enabled (and can be disabled)."] - const IN_ENABLED_STATE = 0x08, - #[doc = "Content is disabled."] - const IN_DISABLED_STATE = 0x10, - } -} +// TODO: Update focus state when the top-level browsing context gains or loses system focus, +// and when the element enters or leaves a browsing context container. +// https://html.spec.whatwg.org/multipage/#selector-focus #[dom_struct] pub struct Element { @@ -111,7 +100,7 @@ pub struct Element { style_attribute: DOMRefCell<Option<PropertyDeclarationBlock>>, attr_list: MutNullableHeap<JS<NamedNodeMap>>, class_list: MutNullableHeap<JS<DOMTokenList>>, - event_state: Cell<EventState>, + state: Cell<ElementState>, } impl PartialEq for Element { @@ -140,11 +129,11 @@ impl Element { pub fn new_inherited(local_name: DOMString, namespace: Namespace, prefix: Option<DOMString>, document: &Document) -> Element { - Element::new_inherited_with_state(EventState::empty(), local_name, + Element::new_inherited_with_state(ElementState::empty(), local_name, namespace, prefix, document) } - pub fn new_inherited_with_state(state: EventState, local_name: DOMString, + pub fn new_inherited_with_state(state: ElementState, local_name: DOMString, namespace: Namespace, prefix: Option<DOMString>, document: &Document) -> Element { @@ -154,11 +143,11 @@ impl Element { namespace: namespace, prefix: prefix, attrs: DOMRefCell::new(vec!()), - attr_list: Default::default(), - class_list: Default::default(), id_attribute: DOMRefCell::new(None), style_attribute: DOMRefCell::new(None), - event_state: Cell::new(state), + attr_list: Default::default(), + class_list: Default::default(), + state: Cell::new(state), } } @@ -188,7 +177,7 @@ pub unsafe fn get_attr_for_layout<'a>(elem: &'a Element, namespace: &Namespace, -> Option<LayoutJS<Attr>> { // cast to point to T in RefCell<T> directly let attrs = elem.attrs.borrow_for_layout(); - attrs.iter().find(|attr: & &JS<Attr>| { + attrs.iter().find(|attr| { let attr = attr.to_layout(); *name == attr.local_name_atom_forever() && (*attr.unsafe_get()).namespace() == namespace @@ -215,7 +204,7 @@ impl RawLayoutElementHelpers for Element { #[inline] unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &Atom) -> Vec<&'a str> { let attrs = self.attrs.borrow_for_layout(); - (*attrs).iter().filter_map(|attr: &JS<Attr>| { + attrs.iter().filter_map(|attr| { let attr = attr.to_layout(); if *name == attr.local_name_atom_forever() { Some(attr.value_ref_forever()) @@ -251,7 +240,7 @@ pub trait LayoutElementHelpers { fn get_checked_state_for_layout(&self) -> bool; fn get_indeterminate_state_for_layout(&self) -> bool; - fn get_event_state_for_layout(&self) -> EventState; + fn get_state_for_layout(&self) -> ElementState; } impl LayoutElementHelpers for LayoutJS<Element> { @@ -358,7 +347,8 @@ impl LayoutElementHelpers for LayoutJS<Element> { hints.push(from_declaration( PropertyDeclaration::FontSize( DeclaredValue::Value( - font_size::SpecifiedValue(font_size))))) + font_size::SpecifiedValue( + LengthOrPercentage::Length(font_size)))))) } let cellspacing = if let Some(this) = self.downcast::<HTMLTableElement>() { @@ -384,7 +374,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { // a text field match (*self.unsafe_get()).get_attr_val_for_layout(&ns!(""), &atom!("type")) { Some("text") | Some("password") => { - match (*this.unsafe_get()).get_size_for_layout() { + match this.get_size_for_layout() { 0 => None, s => Some(s as i32), } @@ -584,7 +574,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { // TODO option and menuitem can also have a checked state. match self.downcast::<HTMLInputElement>() { Some(input) => unsafe { - (*input.unsafe_get()).get_checked_state_for_layout() + input.get_checked_state_for_layout() }, None => false, } @@ -596,7 +586,7 @@ impl LayoutElementHelpers for LayoutJS<Element> { // TODO progress elements can also be matched with :indeterminate match self.downcast::<HTMLInputElement>() { Some(input) => unsafe { - (*input.unsafe_get()).get_indeterminate_state_for_layout() + input.get_indeterminate_state_for_layout() }, None => false, } @@ -604,9 +594,9 @@ impl LayoutElementHelpers for LayoutJS<Element> { #[inline] #[allow(unsafe_code)] - fn get_event_state_for_layout(&self) -> EventState { + fn get_state_for_layout(&self) -> ElementState { unsafe { - (*self.unsafe_get()).event_state.get() + (*self.unsafe_get()).state.get() } } } @@ -886,16 +876,15 @@ impl Element { } pub fn get_attribute(&self, namespace: &Namespace, local_name: &Atom) -> Option<Root<Attr>> { - self.attrs.borrow().iter().map(JS::root).find(|attr| { + self.attrs.borrow().iter().find(|attr| { attr.local_name() == local_name && attr.namespace() == namespace - }) + }).map(|js| Root::from_ref(&**js)) } // https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name pub fn get_attribute_by_name(&self, name: DOMString) -> Option<Root<Attr>> { let name = &self.parsed_name(name); - self.attrs.borrow().iter().map(JS::root) - .find(|a| a.r().name() == name) + self.attrs.borrow().iter().find(|a| a.name() == name).map(|js| Root::from_ref(&**js)) } pub fn set_attribute_from_parser(&self, @@ -903,8 +892,8 @@ impl Element { value: DOMString, prefix: Option<Atom>) { // Don't set if the attribute already exists, so we can handle add_attrs_if_missing - if self.attrs.borrow().iter().map(JS::root) - .any(|a| *a.r().local_name() == qname.local && *a.r().namespace() == qname.ns) { + if self.attrs.borrow().iter() + .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns) { return; } @@ -954,7 +943,8 @@ impl Element { find: F) where F: Fn(&Attr) -> bool { - let attr = self.attrs.borrow().iter().map(JS::root).find(|attr| find(&attr)); + let attr = self.attrs.borrow().iter() + .find(|attr| find(&attr)).map(|js| Root::from_ref(&**js)); if let Some(attr) = attr { attr.set_value(value, self); } else { @@ -985,10 +975,10 @@ impl Element { fn remove_first_matching_attribute<F>(&self, find: F) -> Option<Root<Attr>> where F: Fn(&Attr) -> bool { - let idx = self.attrs.borrow().iter().map(JS::root).position(|attr| find(&attr)); + let idx = self.attrs.borrow().iter().position(|attr| find(&attr)); idx.map(|idx| { - let attr = (*self.attrs.borrow())[idx].root(); + let attr = Root::from_ref(&*(*self.attrs.borrow())[idx]); self.attrs.borrow_mut().remove(idx); attr.set_owner(None); if attr.namespace() == &ns!("") { @@ -1017,8 +1007,8 @@ impl Element { pub fn has_attribute(&self, local_name: &Atom) -> bool { assert!(local_name.bytes().all(|b| b.to_ascii_lowercase() == b)); - self.attrs.borrow().iter().map(JS::root).any(|attr| { - attr.r().local_name() == local_name && attr.r().namespace() == &ns!("") + self.attrs.borrow().iter().any(|attr| { + attr.local_name() == local_name && attr.namespace() == &ns!("") }) } @@ -1582,6 +1572,16 @@ impl VirtualMethods for Element { } } +macro_rules! state_getter { + ($( + $(#[$Flag_attr: meta])* + state $css: expr => $variant: ident / $method: ident / + $flag: ident = $value: expr, + )+) => { + $( fn $method(&self) -> bool { Element::get_state(self).contains($flag) } )+ + } +} + impl<'a> ::selectors::Element for Root<Element> { fn parent_element(&self) -> Option<Root<Element>> { self.upcast::<Node>().GetParentElement() @@ -1648,42 +1648,11 @@ impl<'a> ::selectors::Element for Root<Element> { self.namespace() } - fn get_hover_state(&self) -> bool { - Element::get_hover_state(self) - } - - fn get_active_state(&self) -> bool { - Element::get_active_state(self) - } - - fn get_focus_state(&self) -> bool { - // TODO: Also check whether the top-level browsing context has the system focus, - // and whether this element is a browsing context container. - // https://html.spec.whatwg.org/multipage/#selector-focus - Element::get_focus_state(self) - } + state_pseudo_classes!(state_getter); fn get_id(&self) -> Option<Atom> { self.id_attribute.borrow().clone() } - fn get_disabled_state(&self) -> bool { - Element::get_disabled_state(self) - } - fn get_enabled_state(&self) -> bool { - Element::get_enabled_state(self) - } - fn get_checked_state(&self) -> bool { - match self.downcast::<HTMLInputElement>() { - Some(input) => input.Checked(), - None => false, - } - } - fn get_indeterminate_state(&self) -> bool { - match self.downcast::<HTMLInputElement>() { - Some(input) => input.get_indeterminate_state(), - None => false, - } - } fn has_class(&self, name: &Atom) -> bool { Element::has_class(&**self, name) } @@ -1728,8 +1697,8 @@ impl<'a> ::selectors::Element for Root<Element> { }) }, NamespaceConstraint::Any => { - self.attrs.borrow().iter().map(JS::root).any(|attr| { - attr.local_name() == local_name && test(&attr.value()) + self.attrs.borrow().iter().any(|attr| { + attr.local_name() == local_name && test(&attr.value()) }) } } @@ -1752,6 +1721,10 @@ impl Element { let element = self.downcast::<HTMLAnchorElement>().unwrap(); Some(element as &Activatable) }, + NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLabelElement)) => { + let element = self.downcast::<HTMLLabelElement>().unwrap(); + Some(element as &Activatable) + }, _ => { None } @@ -1833,8 +1806,12 @@ impl Element { self.set_click_in_progress(false); } - fn set_state(&self, which: EventState, value: bool) { - let mut state = self.event_state.get(); + pub fn get_state(&self) -> ElementState { + self.state.get() + } + + pub fn set_state(&self, which: ElementState, value: bool) { + let mut state = self.state.get(); if state.contains(which) == value { return } @@ -1842,14 +1819,14 @@ impl Element { true => state.insert(which), false => state.remove(which), }; - self.event_state.set(state); + self.state.set(state); let node = self.upcast::<Node>(); - node.owner_doc().record_event_state_change(self, which); + node.owner_doc().record_element_state_change(self, which); } pub fn get_active_state(&self) -> bool { - self.event_state.get().contains(IN_ACTIVE_STATE) + self.state.get().contains(IN_ACTIVE_STATE) } pub fn set_active_state(&self, value: bool) { @@ -1857,7 +1834,7 @@ impl Element { } pub fn get_focus_state(&self) -> bool { - self.event_state.get().contains(IN_FOCUS_STATE) + self.state.get().contains(IN_FOCUS_STATE) } pub fn set_focus_state(&self, value: bool) { @@ -1865,7 +1842,7 @@ impl Element { } pub fn get_hover_state(&self) -> bool { - self.event_state.get().contains(IN_HOVER_STATE) + self.state.get().contains(IN_HOVER_STATE) } pub fn set_hover_state(&self, value: bool) { @@ -1873,7 +1850,7 @@ impl Element { } pub fn get_enabled_state(&self) -> bool { - self.event_state.get().contains(IN_ENABLED_STATE) + self.state.get().contains(IN_ENABLED_STATE) } pub fn set_enabled_state(&self, value: bool) { @@ -1881,7 +1858,7 @@ impl Element { } pub fn get_disabled_state(&self) -> bool { - self.event_state.get().contains(IN_DISABLED_STATE) + self.state.get().contains(IN_DISABLED_STATE) } pub fn set_disabled_state(&self, value: bool) { |