diff options
author | Jack Moffitt <jack@metajack.im> | 2014-08-28 09:34:23 -0600 |
---|---|---|
committer | Jack Moffitt <jack@metajack.im> | 2014-09-08 20:21:42 -0600 |
commit | c6ab60dbfc6da7b4f800c9e40893c8b58413960c (patch) | |
tree | d1d74076cf7fa20e4f77ec7cb82cae98b67362cb /components/script/dom/htmlcollection.rs | |
parent | db2f642c32fc5bed445bb6f2e45b0f6f0b4342cf (diff) | |
download | servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.tar.gz servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.zip |
Cargoify servo
Diffstat (limited to 'components/script/dom/htmlcollection.rs')
-rw-r--r-- | components/script/dom/htmlcollection.rs | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs new file mode 100644 index 00000000000..5bef6c56ff5 --- /dev/null +++ b/components/script/dom/htmlcollection.rs @@ -0,0 +1,257 @@ +/* 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::codegen::InheritTypes::{ElementCast, NodeCast}; +use dom::bindings::global::Window; +use dom::bindings::js::{JS, JSRef, Temporary}; +use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; +use dom::element::{Element, AttributeHandlers, ElementHelpers}; +use dom::node::{Node, NodeHelpers}; +use dom::window::Window; +use servo_util::atom::Atom; +use servo_util::namespace; +use servo_util::namespace::Namespace; +use servo_util::str::{DOMString, split_html_space_chars}; + +use serialize::{Encoder, Encodable}; +use std::ascii::StrAsciiExt; + +pub trait CollectionFilter { + fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool; +} + +impl<S: Encoder<E>, E> Encodable<S, E> for Box<CollectionFilter> { + fn encode(&self, _s: &mut S) -> Result<(), E> { + Ok(()) + } +} + +#[deriving(Encodable)] +pub enum CollectionTypeId { + Static(Vec<JS<Element>>), + Live(JS<Node>, Box<CollectionFilter>) +} + +#[deriving(Encodable)] +pub struct HTMLCollection { + collection: CollectionTypeId, + reflector_: Reflector, +} + +impl HTMLCollection { + pub fn new_inherited(collection: CollectionTypeId) -> HTMLCollection { + HTMLCollection { + collection: collection, + reflector_: Reflector::new(), + } + } + + pub fn new(window: &JSRef<Window>, collection: CollectionTypeId) -> Temporary<HTMLCollection> { + reflect_dom_object(box HTMLCollection::new_inherited(collection), + &Window(*window), HTMLCollectionBinding::Wrap) + } +} + +impl HTMLCollection { + pub fn create(window: &JSRef<Window>, root: &JSRef<Node>, + filter: Box<CollectionFilter>) -> Temporary<HTMLCollection> { + HTMLCollection::new(window, Live(JS::from_rooted(root), filter)) + } + + fn all_elements(window: &JSRef<Window>, root: &JSRef<Node>, + namespace_filter: Option<Namespace>) -> Temporary<HTMLCollection> { + struct AllElementFilter { + namespace_filter: Option<Namespace> + } + impl CollectionFilter for AllElementFilter { + fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool { + match self.namespace_filter { + None => true, + Some(ref namespace) => elem.namespace == *namespace + } + } + } + let filter = AllElementFilter {namespace_filter: namespace_filter}; + HTMLCollection::create(window, root, box filter) + } + + pub fn by_tag_name(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString) + -> Temporary<HTMLCollection> { + if tag.as_slice() == "*" { + return HTMLCollection::all_elements(window, root, None); + } + + struct TagNameFilter { + tag: Atom, + ascii_lower_tag: Atom, + } + impl CollectionFilter for TagNameFilter { + fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool { + if elem.html_element_in_html_document() { + elem.local_name == self.ascii_lower_tag + } else { + elem.local_name == self.tag + } + } + } + let filter = TagNameFilter { + tag: Atom::from_slice(tag.as_slice()), + ascii_lower_tag: Atom::from_slice(tag.as_slice().to_ascii_lower().as_slice()), + }; + HTMLCollection::create(window, root, box filter) + } + + pub fn by_tag_name_ns(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString, + maybe_ns: Option<DOMString>) -> Temporary<HTMLCollection> { + let namespace_filter = match maybe_ns { + Some(namespace) => { + match namespace.as_slice() { + "*" => None, + ns => Some(Namespace::from_str(ns)), + } + }, + None => Some(namespace::Null), + }; + + if tag.as_slice() == "*" { + return HTMLCollection::all_elements(window, root, namespace_filter); + } + struct TagNameNSFilter { + tag: Atom, + namespace_filter: Option<Namespace> + } + impl CollectionFilter for TagNameNSFilter { + fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool { + let ns_match = match self.namespace_filter { + Some(ref namespace) => { + elem.deref().namespace == *namespace + }, + None => true + }; + ns_match && elem.deref().local_name == self.tag + } + } + let filter = TagNameNSFilter { + tag: Atom::from_slice(tag.as_slice()), + namespace_filter: namespace_filter + }; + HTMLCollection::create(window, root, box filter) + } + + pub fn by_class_name(window: &JSRef<Window>, root: &JSRef<Node>, classes: DOMString) + -> Temporary<HTMLCollection> { + struct ClassNameFilter { + classes: Vec<DOMString> + } + impl CollectionFilter for ClassNameFilter { + fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool { + self.classes.iter().all(|class| elem.has_class(class.as_slice())) + } + } + let filter = ClassNameFilter { + classes: split_html_space_chars(classes.as_slice()).map(|class| class.to_string()).collect() + }; + HTMLCollection::create(window, root, box filter) + } + + pub fn children(window: &JSRef<Window>, root: &JSRef<Node>) -> Temporary<HTMLCollection> { + struct ElementChildFilter; + impl CollectionFilter for ElementChildFilter { + fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool { + root.is_parent_of(NodeCast::from_ref(elem)) + } + } + HTMLCollection::create(window, root, box ElementChildFilter) + } +} + +impl<'a> HTMLCollectionMethods for JSRef<'a, HTMLCollection> { + // http://dom.spec.whatwg.org/#dom-htmlcollection-length + fn Length(&self) -> u32 { + match self.collection { + Static(ref elems) => elems.len() as u32, + Live(ref root, ref filter) => { + let root = root.root(); + root.deref().traverse_preorder() + .filter(|&child| { + let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&child); + elem.map_or(false, |elem| filter.filter(elem, &*root)) + }).count() as u32 + } + } + } + + // http://dom.spec.whatwg.org/#dom-htmlcollection-item + fn Item(&self, index: u32) -> Option<Temporary<Element>> { + match self.collection { + Static(ref elems) => elems + .as_slice() + .get(index as uint) + .map(|elem| Temporary::new(elem.clone())), + Live(ref root, ref filter) => { + let root = root.root(); + root.deref().traverse_preorder() + .filter_map(|node| { + let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&node); + elem.filtered(|&elem| filter.filter(elem, &*root)) + .map(|elem| elem.clone()) + }) + .nth(index as uint) + .clone() + .map(|elem| Temporary::from_rooted(&elem)) + } + } + } + + // http://dom.spec.whatwg.org/#dom-htmlcollection-nameditem + fn NamedItem(&self, key: DOMString) -> Option<Temporary<Element>> { + // Step 1. + if key.is_empty() { + return None; + } + + // Step 2. + match self.collection { + Static(ref elems) => elems.iter() + .map(|elem| elem.root()) + .find(|elem| { + elem.get_string_attribute("name") == key || + elem.get_string_attribute("id") == key }) + .map(|maybe_elem| Temporary::from_rooted(&*maybe_elem)), + Live(ref root, ref filter) => { + let root = root.root(); + root.deref().traverse_preorder() + .filter_map(|node| { + let elem: Option<&JSRef<Element>> = ElementCast::to_ref(&node); + elem.filtered(|&elem| filter.filter(elem, &*root)) + .map(|elem| elem.clone()) + }) + .find(|elem| { + elem.get_string_attribute("name") == key || + elem.get_string_attribute("id") == key }) + .map(|maybe_elem| Temporary::from_rooted(&maybe_elem)) + } + } + } + + fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option<Temporary<Element>> { + let maybe_elem = self.Item(index); + *found = maybe_elem.is_some(); + maybe_elem + } + + fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<Temporary<Element>> { + let maybe_elem = self.NamedItem(name); + *found = maybe_elem.is_some(); + maybe_elem + } +} + +impl Reflectable for HTMLCollection { + fn reflector<'a>(&'a self) -> &'a Reflector { + &self.reflector_ + } +} |