aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/element.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/element.rs')
-rw-r--r--components/script/dom/element.rs159
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) {