Skip to content

Commit

Permalink
make Document.getElementsByName a live collection
Browse files Browse the repository at this point in the history
  • Loading branch information
pshaughn committed Feb 15, 2020
1 parent 3475790 commit 007a6cd
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 18 deletions.
61 changes: 47 additions & 14 deletions components/script/dom/document.rs
Expand Up @@ -3056,14 +3056,56 @@ impl Document {
self.redirect_count.set(count)
}

fn create_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> DomRoot<NodeList> {
pub fn elements_by_name_count(&self, name: &DOMString) -> u32 {
if name.is_empty() {
return 0;
}
self.count_node_list(|n| Document::is_element_in_get_by_name(n, name))
}

pub fn nth_element_by_name(&self, index: u32, name: &DOMString) -> Option<DomRoot<Node>> {
if name.is_empty() {
return None;
}
self.nth_in_node_list(index, |n| Document::is_element_in_get_by_name(n, name))
}

// Note that document.getByName does not match on the same conditions
// as the document named getter.
fn is_element_in_get_by_name(node: &Node, name: &DOMString) -> bool {
let element = match node.downcast::<Element>() {
Some(element) => element,
None => return false,
};
if element.namespace() != &ns!(html) {
return false;
}
element.get_name().map_or(false, |n| *n == **name)
}

fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 {
let doc = self.GetDocumentElement();
let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
let iter = maybe_node
maybe_node
.iter()
.flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
.filter(|node| callback(&node));
NodeList::new_simple_list(&self.window, iter)
.filter(|node| callback(&node))
.count() as u32
}

fn nth_in_node_list<F: Fn(&Node) -> bool>(
&self,
index: u32,
callback: F,
) -> Option<DomRoot<Node>> {
let doc = self.GetDocumentElement();
let maybe_node = doc.as_deref().map(Castable::upcast::<Node>);
maybe_node
.iter()
.flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
.filter(|node| callback(&node))
.nth(index as usize)
.map(|n| DomRoot::from_ref(&*n))
}

fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> {
Expand Down Expand Up @@ -4244,16 +4286,7 @@ impl DocumentMethods for Document {

// https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname
fn GetElementsByName(&self, name: DOMString) -> DomRoot<NodeList> {
self.create_node_list(|node| {
let element = match node.downcast::<Element>() {
Some(element) => element,
None => return false,
};
if element.namespace() != &ns!(html) {
return false;
}
element.get_name().map_or(false, |atom| *atom == *name)
})
NodeList::new_elements_by_name_list(self.window(), self, name)
}

// https://html.spec.whatwg.org/multipage/#dom-document-images
Expand Down
42 changes: 42 additions & 0 deletions components/script/dom/nodelist.rs
Expand Up @@ -7,6 +7,8 @@ use crate::dom::bindings::codegen::Bindings::NodeListBinding;
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};
Expand All @@ -22,6 +24,7 @@ pub enum NodeListType {
Children(ChildrenList),
Labels(LabelsList),
Radio(RadioList),
ElementsByName(ElementsByNameList),
}

// https://dom.spec.whatwg.org/#interface-nodelist
Expand Down Expand Up @@ -74,6 +77,17 @@ impl 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![]))
}
Expand All @@ -87,6 +101,7 @@ impl NodeListMethods for NodeList {
NodeListType::Children(ref list) => list.len(),
NodeListType::Labels(ref list) => list.len(),
NodeListType::Radio(ref list) => list.len(),
NodeListType::ElementsByName(ref list) => list.len(),
}
}

Expand All @@ -99,6 +114,7 @@ impl NodeListMethods for NodeList {
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),
}
}

Expand Down Expand Up @@ -401,3 +417,29 @@ impl RadioList {
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)
}

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)))
}
}

This file was deleted.

0 comments on commit 007a6cd

Please sign in to comment.