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