diff options
Diffstat (limited to 'components/script/dom/htmlcollection.rs')
-rw-r--r-- | components/script/dom/htmlcollection.rs | 281 |
1 files changed, 163 insertions, 118 deletions
diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs index 4e05de86922..d5198b94986 100644 --- a/components/script/dom/htmlcollection.rs +++ b/components/script/dom/htmlcollection.rs @@ -1,32 +1,31 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use dom::bindings::codegen::Bindings::HTMLCollectionBinding; -use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods; -use dom::bindings::inheritance::Castable; -use dom::bindings::js::{JS, Root, MutNullableJS}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; -use dom::bindings::str::DOMString; -use dom::bindings::trace::JSTraceable; -use dom::bindings::xmlname::namespace_from_domstring; -use dom::element::Element; -use dom::node::Node; -use dom::window::Window; + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; +use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; +use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::trace::JSTraceable; +use crate::dom::bindings::xmlname::namespace_from_domstring; +use crate::dom::element::Element; +use crate::dom::node::{document_from_node, Node}; +use crate::dom::window::Window; use dom_struct::dom_struct; -use html5ever_atoms::{LocalName, QualName}; +use html5ever::{LocalName, QualName}; use servo_atoms::Atom; use std::cell::Cell; use style::str::split_html_space_chars; -pub trait CollectionFilter : JSTraceable { +pub trait CollectionFilter: JSTraceable { fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool; } // An optional u32, using maxint to represent None. // It would be nicer just to use Option<u32> for this, but that would produce word // alignment issues since Option<u32> uses 33 bits. -#[derive(Clone, Copy, JSTraceable, HeapSizeOf)] +#[derive(Clone, Copy, JSTraceable, MallocSizeOf)] struct OptionU32 { bits: u32, } @@ -41,53 +40,80 @@ impl OptionU32 { } fn some(bits: u32) -> OptionU32 { - assert!(bits != u32::max_value()); + assert_ne!(bits, u32::max_value()); OptionU32 { bits: bits } } fn none() -> OptionU32 { - OptionU32 { bits: u32::max_value() } + OptionU32 { + bits: u32::max_value(), + } } } #[dom_struct] pub struct HTMLCollection { reflector_: Reflector, - root: JS<Node>, - #[ignore_heap_size_of = "Contains a trait object; can't measure due to #6870"] - filter: Box<CollectionFilter + 'static>, + root: Dom<Node>, + #[ignore_malloc_size_of = "Contains a trait object; can't measure due to #6870"] + filter: Box<dyn CollectionFilter + 'static>, // We cache the version of the root node and all its decendents, // the length of the collection, and a cursor into the collection. // FIXME: make the cached cursor element a weak pointer cached_version: Cell<u64>, - cached_cursor_element: MutNullableJS<Element>, + cached_cursor_element: MutNullableDom<Element>, cached_cursor_index: Cell<OptionU32>, cached_length: Cell<OptionU32>, } impl HTMLCollection { #[allow(unrooted_must_root)] - pub fn new_inherited(root: &Node, filter: Box<CollectionFilter + 'static>) -> HTMLCollection { + pub fn new_inherited( + root: &Node, + filter: Box<dyn CollectionFilter + 'static>, + ) -> HTMLCollection { HTMLCollection { reflector_: Reflector::new(), - root: JS::from_ref(root), + root: Dom::from_ref(root), filter: filter, // Default values for the cache cached_version: Cell::new(root.inclusive_descendants_version()), - cached_cursor_element: MutNullableJS::new(None), + cached_cursor_element: MutNullableDom::new(None), cached_cursor_index: Cell::new(OptionU32::none()), cached_length: Cell::new(OptionU32::none()), } } + /// Returns a collection which is always empty. + pub fn always_empty(window: &Window, root: &Node) -> DomRoot<Self> { + #[derive(JSTraceable)] + struct NoFilter; + impl CollectionFilter for NoFilter { + fn filter<'a>(&self, _: &'a Element, _: &'a Node) -> bool { + false + } + } + + Self::new(window, root, Box::new(NoFilter)) + } + #[allow(unrooted_must_root)] - pub fn new(window: &Window, root: &Node, filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> { - reflect_dom_object(box HTMLCollection::new_inherited(root, filter), - window, HTMLCollectionBinding::Wrap) + pub fn new( + window: &Window, + root: &Node, + filter: Box<dyn CollectionFilter + 'static>, + ) -> DomRoot<HTMLCollection> { + reflect_dom_object( + Box::new(HTMLCollection::new_inherited(root, filter)), + window, + ) } - pub fn create(window: &Window, root: &Node, - filter: Box<CollectionFilter + 'static>) -> Root<HTMLCollection> { + pub fn create( + window: &Window, + root: &Node, + filter: Box<dyn CollectionFilter + 'static>, + ) -> DomRoot<HTMLCollection> { HTMLCollection::new(window, root, filter) } @@ -104,7 +130,11 @@ impl HTMLCollection { } } - fn set_cached_cursor(&self, index: u32, element: Option<Root<Element>>) -> Option<Root<Element>> { + fn set_cached_cursor( + &self, + index: u32, + element: Option<DomRoot<Element>>, + ) -> Option<DomRoot<Element>> { if let Some(element) = element { self.cached_cursor_index.set(OptionU32::some(index)); self.cached_cursor_element.set(Some(&element)); @@ -115,30 +145,35 @@ impl HTMLCollection { } // https://dom.spec.whatwg.org/#concept-getelementsbytagname - pub fn by_qualified_name(window: &Window, root: &Node, qualified_name: LocalName) - -> Root<HTMLCollection> { + pub fn by_qualified_name( + window: &Window, + root: &Node, + qualified_name: LocalName, + ) -> DomRoot<HTMLCollection> { // case 1 if qualified_name == local_name!("*") { - #[derive(JSTraceable, HeapSizeOf)] + #[derive(JSTraceable, MallocSizeOf)] struct AllFilter; impl CollectionFilter for AllFilter { fn filter(&self, _elem: &Element, _root: &Node) -> bool { true } } - return HTMLCollection::create(window, root, box AllFilter); + return HTMLCollection::create(window, root, Box::new(AllFilter)); } - #[derive(JSTraceable, HeapSizeOf)] + #[derive(JSTraceable, MallocSizeOf)] struct HtmlDocumentFilter { qualified_name: LocalName, ascii_lower_qualified_name: LocalName, } impl CollectionFilter for HtmlDocumentFilter { fn filter(&self, elem: &Element, root: &Node) -> bool { - if root.is_in_html_doc() && elem.namespace() == &ns!(html) { // case 2 + if root.is_in_html_doc() && elem.namespace() == &ns!(html) { + // case 2 HTMLCollection::match_element(elem, &self.ascii_lower_qualified_name) - } else { // case 2 and 3 + } else { + // case 2 and 3 HTMLCollection::match_element(elem, &self.qualified_name) } } @@ -148,122 +183,124 @@ impl HTMLCollection { ascii_lower_qualified_name: qualified_name.to_ascii_lowercase(), qualified_name: qualified_name, }; - HTMLCollection::create(window, root, box filter) + HTMLCollection::create(window, root, Box::new(filter)) } fn match_element(elem: &Element, qualified_name: &LocalName) -> bool { - match elem.prefix() { + match elem.prefix().as_ref() { None => elem.local_name() == qualified_name, - Some(prefix) => qualified_name.starts_with(&**prefix) && - qualified_name.find(":") == Some(prefix.len()) && - qualified_name.ends_with(&**elem.local_name()), + Some(prefix) => { + qualified_name.starts_with(&**prefix) && + qualified_name.find(":") == Some(prefix.len()) && + qualified_name.ends_with(&**elem.local_name()) + }, } } - pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString, - maybe_ns: Option<DOMString>) -> Root<HTMLCollection> { + pub fn by_tag_name_ns( + window: &Window, + root: &Node, + tag: DOMString, + maybe_ns: Option<DOMString>, + ) -> DomRoot<HTMLCollection> { let local = LocalName::from(tag); let ns = namespace_from_domstring(maybe_ns); - let qname = QualName::new(ns, local); + let qname = QualName::new(None, ns, local); HTMLCollection::by_qual_tag_name(window, root, qname) } - pub fn by_qual_tag_name(window: &Window, root: &Node, qname: QualName) -> Root<HTMLCollection> { - #[derive(JSTraceable, HeapSizeOf)] + pub fn by_qual_tag_name( + window: &Window, + root: &Node, + qname: QualName, + ) -> DomRoot<HTMLCollection> { + #[derive(JSTraceable, MallocSizeOf)] struct TagNameNSFilter { - qname: QualName + qname: QualName, } impl CollectionFilter for TagNameNSFilter { fn filter(&self, elem: &Element, _root: &Node) -> bool { - ((self.qname.ns == namespace_url!("*")) || (self.qname.ns == *elem.namespace())) && - ((self.qname.local == local_name!("*")) || (self.qname.local == *elem.local_name())) + ((self.qname.ns == namespace_url!("*")) || (self.qname.ns == *elem.namespace())) && + ((self.qname.local == local_name!("*")) || + (self.qname.local == *elem.local_name())) } } - let filter = TagNameNSFilter { - qname: qname - }; - HTMLCollection::create(window, root, box filter) + let filter = TagNameNSFilter { qname: qname }; + HTMLCollection::create(window, root, Box::new(filter)) } - pub fn by_class_name(window: &Window, root: &Node, classes: DOMString) - -> Root<HTMLCollection> { + pub fn by_class_name( + window: &Window, + root: &Node, + classes: DOMString, + ) -> DomRoot<HTMLCollection> { let class_atoms = split_html_space_chars(&classes).map(Atom::from).collect(); HTMLCollection::by_atomic_class_name(window, root, class_atoms) } - pub fn by_atomic_class_name(window: &Window, root: &Node, classes: Vec<Atom>) - -> Root<HTMLCollection> { - #[derive(JSTraceable, HeapSizeOf)] + pub fn by_atomic_class_name( + window: &Window, + root: &Node, + classes: Vec<Atom>, + ) -> DomRoot<HTMLCollection> { + #[derive(JSTraceable, MallocSizeOf)] struct ClassNameFilter { - classes: Vec<Atom> + classes: Vec<Atom>, } impl CollectionFilter for ClassNameFilter { fn filter(&self, elem: &Element, _root: &Node) -> bool { - self.classes.iter().all(|class| elem.has_class(class)) + let case_sensitivity = document_from_node(elem) + .quirks_mode() + .classes_and_ids_case_sensitivity(); + self.classes + .iter() + .all(|class| elem.has_class(class, case_sensitivity)) } } - let filter = ClassNameFilter { - classes: classes - }; - HTMLCollection::create(window, root, box filter) + let filter = ClassNameFilter { classes: classes }; + HTMLCollection::create(window, root, Box::new(filter)) } - pub fn children(window: &Window, root: &Node) -> Root<HTMLCollection> { - #[derive(JSTraceable, HeapSizeOf)] + pub fn children(window: &Window, root: &Node) -> DomRoot<HTMLCollection> { + #[derive(JSTraceable, MallocSizeOf)] struct ElementChildFilter; impl CollectionFilter for ElementChildFilter { fn filter(&self, elem: &Element, root: &Node) -> bool { root.is_parent_of(elem.upcast()) } } - HTMLCollection::create(window, root, box ElementChildFilter) + HTMLCollection::create(window, root, Box::new(ElementChildFilter)) } - pub fn elements_iter_after<'a>(&'a self, after: &'a Node) -> impl Iterator<Item=Root<Element>> + 'a { + pub fn elements_iter_after<'a>( + &'a self, + after: &'a Node, + ) -> impl Iterator<Item = DomRoot<Element>> + 'a { // Iterate forwards from a node. - HTMLCollectionElementsIter { - node_iter: after.following_nodes(&self.root), - root: Root::from_ref(&self.root), - filter: &self.filter, - } + after + .following_nodes(&self.root) + .filter_map(DomRoot::downcast) + .filter(move |element| self.filter.filter(&element, &self.root)) } - pub fn elements_iter<'a>(&'a self) -> impl Iterator<Item=Root<Element>> + 'a { + pub fn elements_iter<'a>(&'a self) -> impl Iterator<Item = DomRoot<Element>> + 'a { // Iterate forwards from the root. self.elements_iter_after(&*self.root) } - pub fn elements_iter_before<'a>(&'a self, before: &'a Node) -> impl Iterator<Item=Root<Element>> + 'a { + pub fn elements_iter_before<'a>( + &'a self, + before: &'a Node, + ) -> impl Iterator<Item = DomRoot<Element>> + 'a { // Iterate backwards from a node. - HTMLCollectionElementsIter { - node_iter: before.preceding_nodes(&self.root), - root: Root::from_ref(&self.root), - filter: &self.filter, - } - } - - pub fn root_node(&self) -> Root<Node> { - Root::from_ref(&self.root) + before + .preceding_nodes(&self.root) + .filter_map(DomRoot::downcast) + .filter(move |element| self.filter.filter(&element, &self.root)) } -} - -// TODO: Make this generic, and avoid code duplication -struct HTMLCollectionElementsIter<'a, I> { - node_iter: I, - root: Root<Node>, - filter: &'a Box<CollectionFilter>, -} - -impl<'a, I: Iterator<Item=Root<Node>>> Iterator for HTMLCollectionElementsIter<'a, I> { - type Item = Root<Element>; - fn next(&mut self) -> Option<Self::Item> { - let filter = &self.filter; - let root = &self.root; - self.node_iter.by_ref() - .filter_map(Root::downcast) - .filter(|element| filter.filter(&element, root)) - .next() + pub fn root_node(&self) -> DomRoot<Node> { + DomRoot::from_ref(&self.root) } } @@ -284,7 +321,7 @@ impl HTMLCollectionMethods for HTMLCollection { } // https://dom.spec.whatwg.org/#dom-htmlcollection-item - fn Item(&self, index: u32) -> Option<Root<Element>> { + fn Item(&self, index: u32) -> Option<DomRoot<Element>> { self.validate_cache(); if let Some(element) = self.cached_cursor_element.get() { @@ -297,14 +334,14 @@ impl HTMLCollectionMethods for HTMLCollection { // The cursor is before the element we're looking for // Iterate forwards, starting at the cursor. let offset = index - (cached_index + 1); - let node: Root<Node> = Root::upcast(element); + let node: DomRoot<Node> = DomRoot::upcast(element); let mut iter = self.elements_iter_after(&node); self.set_cached_cursor(index, iter.nth(offset as usize)) } else { // The cursor is after the element we're looking for // Iterate backwards, starting at the cursor. let offset = cached_index - (index + 1); - let node: Root<Node> = Root::upcast(element); + let node: DomRoot<Node> = DomRoot::upcast(element); let mut iter = self.elements_iter_before(&node); self.set_cached_cursor(index, iter.nth(offset as usize)) } @@ -321,26 +358,28 @@ impl HTMLCollectionMethods for HTMLCollection { } // https://dom.spec.whatwg.org/#dom-htmlcollection-nameditem - fn NamedItem(&self, key: DOMString) -> Option<Root<Element>> { + fn NamedItem(&self, key: DOMString) -> Option<DomRoot<Element>> { // Step 1. if key.is_empty() { return None; } + let key = Atom::from(key); + // Step 2. self.elements_iter().find(|elem| { - elem.get_string_attribute(&local_name!("id")) == key || - (elem.namespace() == &ns!(html) && elem.get_string_attribute(&local_name!("name")) == key) + elem.get_id().map_or(false, |id| id == key) || + (elem.namespace() == &ns!(html) && elem.get_name().map_or(false, |id| id == key)) }) } // https://dom.spec.whatwg.org/#dom-htmlcollection-item - fn IndexedGetter(&self, index: u32) -> Option<Root<Element>> { + fn IndexedGetter(&self, index: u32) -> Option<DomRoot<Element>> { self.Item(index) } // check-tidy: no specs after this line - fn NamedGetter(&self, name: DOMString) -> Option<Root<Element>> { + fn NamedGetter(&self, name: DOMString) -> Option<DomRoot<Element>> { self.NamedItem(name) } @@ -352,14 +391,20 @@ impl HTMLCollectionMethods for HTMLCollection { // Step 2 for elem in self.elements_iter() { // Step 2.1 - let id_attr = elem.get_string_attribute(&local_name!("id")); - if !id_attr.is_empty() && !result.contains(&id_attr) { - result.push(id_attr) + if let Some(id_atom) = elem.get_id() { + let id_str = DOMString::from(&*id_atom); + if !result.contains(&id_str) { + result.push(id_str); + } } // Step 2.2 - let name_attr = elem.get_string_attribute(&local_name!("name")); - if !name_attr.is_empty() && !result.contains(&name_attr) && *elem.namespace() == ns!(html) { - result.push(name_attr) + if *elem.namespace() == ns!(html) { + if let Some(name_atom) = elem.get_name() { + let name_str = DOMString::from(&*name_atom); + if !result.contains(&name_str) { + result.push(name_str) + } + } } } |