diff options
Diffstat (limited to 'components/script/dom/nodelist.rs')
-rw-r--r-- | components/script/dom/nodelist.rs | 290 |
1 files changed, 214 insertions, 76 deletions
diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 5c0983ccaa3..d7ec1808323 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -1,22 +1,29 @@ /* 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::NodeBinding::NodeMethods; -use dom::bindings::codegen::Bindings::NodeListBinding; -use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; -use dom::bindings::js::{JS, MutNullableJS, Root, RootedReference}; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; -use dom::node::{ChildrenMutation, Node}; -use dom::window::Window; + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; +use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; +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::document::Document; +use crate::dom::htmlelement::HTMLElement; +use crate::dom::htmlformelement::HTMLFormElement; +use crate::dom::node::{ChildrenMutation, Node}; +use crate::dom::window::Window; use dom_struct::dom_struct; +use servo_atoms::Atom; use std::cell::Cell; -#[derive(JSTraceable, HeapSizeOf)] -#[must_root] +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] pub enum NodeListType { - Simple(Vec<JS<Node>>), + Simple(Vec<Dom<Node>>), Children(ChildrenList), + Labels(LabelsList), + Radio(RadioList), + ElementsByName(ElementsByNameList), } // https://dom.spec.whatwg.org/#interface-nodelist @@ -36,22 +43,47 @@ impl NodeList { } #[allow(unrooted_must_root)] - pub fn new(window: &Window, list_type: NodeListType) -> Root<NodeList> { - reflect_dom_object(box NodeList::new_inherited(list_type), - window, - NodeListBinding::Wrap) + pub fn new(window: &Window, list_type: NodeListType) -> DomRoot<NodeList> { + reflect_dom_object(Box::new(NodeList::new_inherited(list_type)), window) + } + + pub fn new_simple_list<T>(window: &Window, iter: T) -> DomRoot<NodeList> + where + T: Iterator<Item = DomRoot<Node>>, + { + NodeList::new( + window, + NodeListType::Simple(iter.map(|r| Dom::from_ref(&*r)).collect()), + ) } - pub fn new_simple_list<T>(window: &Window, iter: T) -> Root<NodeList> - where T: Iterator<Item=Root<Node>> { - NodeList::new(window, NodeListType::Simple(iter.map(|r| JS::from_ref(&*r)).collect())) + pub fn new_simple_list_slice(window: &Window, slice: &[&Node]) -> DomRoot<NodeList> { + NodeList::new( + window, + NodeListType::Simple(slice.iter().map(|r| Dom::from_ref(*r)).collect()), + ) } - pub fn new_child_list(window: &Window, node: &Node) -> Root<NodeList> { + pub fn new_child_list(window: &Window, node: &Node) -> DomRoot<NodeList> { NodeList::new(window, NodeListType::Children(ChildrenList::new(node))) } - pub fn empty(window: &Window) -> Root<NodeList> { + pub fn new_labels_list(window: &Window, element: &HTMLElement) -> DomRoot<NodeList> { + NodeList::new(window, NodeListType::Labels(LabelsList::new(element))) + } + + pub fn new_elements_by_name_list( + window: &Window, + document: &Document, + name: DOMString, + ) -> DomRoot<NodeList> { + NodeList::new( + window, + NodeListType::ElementsByName(ElementsByNameList::new(document, name)), + ) + } + + pub fn empty(window: &Window) -> DomRoot<NodeList> { NodeList::new(window, NodeListType::Simple(vec![])) } } @@ -62,57 +94,62 @@ impl NodeListMethods for NodeList { match self.list_type { NodeListType::Simple(ref elems) => elems.len() as u32, NodeListType::Children(ref list) => list.len(), + NodeListType::Labels(ref list) => list.len(), + NodeListType::Radio(ref list) => list.len(), + NodeListType::ElementsByName(ref list) => list.len(), } } // https://dom.spec.whatwg.org/#dom-nodelist-item - fn Item(&self, index: u32) -> Option<Root<Node>> { + fn Item(&self, index: u32) -> Option<DomRoot<Node>> { match self.list_type { - NodeListType::Simple(ref elems) => { - elems.get(index as usize).map(|node| Root::from_ref(&**node)) - }, + NodeListType::Simple(ref elems) => elems + .get(index as usize) + .map(|node| DomRoot::from_ref(&**node)), NodeListType::Children(ref list) => list.item(index), + NodeListType::Labels(ref list) => list.item(index), + NodeListType::Radio(ref list) => list.item(index), + NodeListType::ElementsByName(ref list) => list.item(index), } } // https://dom.spec.whatwg.org/#dom-nodelist-item - fn IndexedGetter(&self, index: u32) -> Option<Root<Node>> { + fn IndexedGetter(&self, index: u32) -> Option<DomRoot<Node>> { self.Item(index) } } - impl NodeList { pub fn as_children_list(&self) -> &ChildrenList { if let NodeListType::Children(ref list) = self.list_type { list } else { - panic!("called as_children_list() on a simple node list") + panic!("called as_children_list() on a non-children node list") } } - pub fn as_simple_list(&self) -> &Vec<JS<Node>> { - if let NodeListType::Simple(ref list) = self.list_type { + pub fn as_radio_list(&self) -> &RadioList { + if let NodeListType::Radio(ref list) = self.list_type { list } else { - panic!("called as_simple_list() on a children node list") + panic!("called as_radio_list() on a non-radio node list") } } - pub fn iter(&self) -> NodeListIterator { - NodeListIterator { - nodes: self, - offset: 0, - } + pub fn iter<'a>(&'a self) -> impl Iterator<Item = DomRoot<Node>> + 'a { + let len = self.Length(); + // There is room for optimization here in non-simple cases, + // as calling Item repeatedly on a live list can involve redundant work. + (0..len).flat_map(move |i| self.Item(i)) } } -#[derive(JSTraceable, HeapSizeOf)] -#[must_root] +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] pub struct ChildrenList { - node: JS<Node>, - #[ignore_heap_size_of = "Defined in rust-mozjs"] - last_visited: MutNullableJS<Node>, + node: Dom<Node>, + #[ignore_malloc_size_of = "Defined in rust-mozjs"] + last_visited: MutNullableDom<Node>, last_index: Cell<u32>, } @@ -120,8 +157,8 @@ impl ChildrenList { pub fn new(node: &Node) -> ChildrenList { let last_visited = node.GetFirstChild(); ChildrenList { - node: JS::from_ref(node), - last_visited: MutNullableJS::new(last_visited.r()), + node: Dom::from_ref(node), + last_visited: MutNullableDom::new(last_visited.as_deref()), last_index: Cell::new(0u32), } } @@ -130,7 +167,7 @@ impl ChildrenList { self.node.children_count() } - pub fn item(&self, index: u32) -> Option<Root<Node>> { + pub fn item(&self, index: u32) -> Option<DomRoot<Node>> { // This always start traversing the children from the closest element // among parent's first and last children and the last visited one. let len = self.len() as u32; @@ -151,7 +188,11 @@ impl ChildrenList { self.last_visited.get().unwrap().GetNextSibling().unwrap() } else if last_index > 0 && index == last_index - 1u32 { // Item is last visited's previous sibling. - self.last_visited.get().unwrap().GetPreviousSibling().unwrap() + self.last_visited + .get() + .unwrap() + .GetPreviousSibling() + .unwrap() } else if index > last_index { if index == len - 1u32 { // Item is parent's last child, not worth updating last visited. @@ -159,28 +200,39 @@ impl ChildrenList { } if index <= last_index + (len - last_index) / 2u32 { // Item is closer to the last visited child and follows it. - self.last_visited.get().unwrap() - .inclusively_following_siblings() - .nth((index - last_index) as usize).unwrap() + self.last_visited + .get() + .unwrap() + .inclusively_following_siblings() + .nth((index - last_index) as usize) + .unwrap() } else { // Item is closer to parent's last child and obviously // precedes it. - self.node.GetLastChild().unwrap() + self.node + .GetLastChild() + .unwrap() .inclusively_preceding_siblings() - .nth((len - index - 1u32) as usize).unwrap() + .nth((len - index - 1u32) as usize) + .unwrap() } } else if index >= last_index / 2u32 { // Item is closer to the last visited child and precedes it. - self.last_visited.get().unwrap() - .inclusively_preceding_siblings() - .nth((last_index - index) as usize).unwrap() + self.last_visited + .get() + .unwrap() + .inclusively_preceding_siblings() + .nth((last_index - index) as usize) + .unwrap() } else { // Item is closer to parent's first child and obviously follows it. debug_assert!(index < last_index / 2u32); - self.node.GetFirstChild().unwrap() - .inclusively_following_siblings() - .nth(index as usize) - .unwrap() + self.node + .GetFirstChild() + .unwrap() + .inclusively_following_siblings() + .nth(index as usize) + .unwrap() }; self.last_visited.set(Some(&last_visited)); self.last_index.set(index); @@ -209,11 +261,13 @@ impl ChildrenList { } } - fn replace(list: &ChildrenList, - prev: Option<&Node>, - removed: &Node, - added: &[&Node], - next: Option<&Node>) { + fn replace( + list: &ChildrenList, + prev: Option<&Node>, + removed: &Node, + added: &[&Node], + next: Option<&Node>, + ) { let index = list.last_index.get(); if removed == &*list.last_visited.get().unwrap() { let visited = match (prev, added, next) { @@ -223,9 +277,9 @@ impl ChildrenList { // by ChildrenMutation::replace(). unreachable!() }, - (_, &[node, ..], _) => node, - (_, &[], Some(next)) => next, - (Some(prev), &[], None) => { + (_, added, _) if !added.is_empty() => added[0], + (_, _, Some(next)) => next, + (Some(prev), _, None) => { list.last_index.set(index - 1u32); prev }, @@ -257,7 +311,12 @@ impl ChildrenList { ChildrenMutation::Prepend { added, next } => { prepend(self, added, next); }, - ChildrenMutation::Replace { prev, removed, added, next } => { + ChildrenMutation::Replace { + prev, + removed, + added, + next, + } => { replace(self, prev, removed, added, next); }, ChildrenMutation::ReplaceAll { added, .. } => { @@ -277,26 +336,105 @@ impl ChildrenList { self.last_index.set(middle as u32); } }, + ChildrenMutation::ChangeText => {}, } } fn reset(&self) { - self.last_visited.set(self.node.GetFirstChild().r()); + self.last_visited.set(self.node.GetFirstChild().as_deref()); self.last_index.set(0u32); } } -pub struct NodeListIterator<'a> { - nodes: &'a NodeList, - offset: u32, +// Labels lists: There might be room for performance optimization +// analogous to the ChildrenMutation case of a children list, +// in which we can keep information from an older access live +// if we know nothing has happened that would change it. +// However, label relationships can happen from further away +// in the DOM than parent-child relationships, so it's not as simple, +// and it's possible that tracking label moves would end up no faster +// than recalculating labels. +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct LabelsList { + element: Dom<HTMLElement>, } -impl<'a> Iterator for NodeListIterator<'a> { - type Item = Root<Node>; +impl LabelsList { + pub fn new(element: &HTMLElement) -> LabelsList { + LabelsList { + element: Dom::from_ref(element), + } + } + + pub fn len(&self) -> u32 { + self.element.labels_count() + } + + pub fn item(&self, index: u32) -> Option<DomRoot<Node>> { + self.element.label_at(index) + } +} + +// Radio node lists: There is room for performance improvement here; +// a form is already aware of changes to its set of controls, +// so a radio list can cache and cache-invalidate its contents +// just by hooking into what the form already knows without a +// separate mutation observer. FIXME #25482 +#[derive(Clone, Copy, JSTraceable, MallocSizeOf)] +pub enum RadioListMode { + ControlsExceptImageInputs, + Images, +} + +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct RadioList { + form: Dom<HTMLFormElement>, + mode: RadioListMode, + name: Atom, +} + +impl RadioList { + pub fn new(form: &HTMLFormElement, mode: RadioListMode, name: Atom) -> RadioList { + RadioList { + form: Dom::from_ref(form), + mode: mode, + name: name, + } + } + + pub fn len(&self) -> u32 { + self.form.count_for_radio_list(self.mode, &self.name) + } + + pub fn item(&self, index: u32) -> Option<DomRoot<Node>> { + self.form.nth_for_radio_list(index, self.mode, &self.name) + } +} + +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct ElementsByNameList { + document: Dom<Document>, + name: DOMString, +} + +impl ElementsByNameList { + pub fn new(document: &Document, name: DOMString) -> ElementsByNameList { + ElementsByNameList { + document: Dom::from_ref(document), + name: name, + } + } + + pub fn len(&self) -> u32 { + self.document.elements_by_name_count(&self.name) + } - fn next(&mut self) -> Option<Root<Node>> { - let result = self.nodes.Item(self.offset); - self.offset = self.offset + 1; - result + pub fn item(&self, index: u32) -> Option<DomRoot<Node>> { + self.document + .nth_element_by_name(index, &self.name) + .and_then(|n| Some(DomRoot::from_ref(&*n))) } } |