Fix getElementsByTagName[NS] support to match the spec.

This commit is contained in:
James Graham 2014-07-27 22:44:13 +01:00
parent 70e70fd1d9
commit 0b802ab018
5 changed files with 65 additions and 22 deletions

View file

@ -351,12 +351,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
// http://dom.spec.whatwg.org/#dom-document-getelementsbytagnamens // http://dom.spec.whatwg.org/#dom-document-getelementsbytagnamens
fn GetElementsByTagNameNS(&self, maybe_ns: Option<DOMString>, tag_name: DOMString) -> Temporary<HTMLCollection> { fn GetElementsByTagNameNS(&self, maybe_ns: Option<DOMString>, tag_name: DOMString) -> Temporary<HTMLCollection> {
let window = self.window.root(); let window = self.window.root();
HTMLCollection::by_tag_name_ns(&*window, NodeCast::from_ref(self), tag_name, maybe_ns)
let namespace = match maybe_ns {
Some(namespace) => Namespace::from_str(namespace.as_slice()),
None => Null
};
HTMLCollection::by_tag_name_ns(&*window, NodeCast::from_ref(self), tag_name, namespace)
} }
// http://dom.spec.whatwg.org/#dom-document-getelementsbyclassname // http://dom.spec.whatwg.org/#dom-document-getelementsbyclassname

View file

@ -224,9 +224,8 @@ pub trait ElementHelpers {
impl<'a> ElementHelpers for JSRef<'a, Element> { impl<'a> ElementHelpers for JSRef<'a, Element> {
fn html_element_in_html_document(&self) -> bool { fn html_element_in_html_document(&self) -> bool {
let is_html = self.namespace == namespace::HTML;
let node: &JSRef<Node> = NodeCast::from_ref(self); let node: &JSRef<Node> = NodeCast::from_ref(self);
is_html && node.owner_doc().root().is_html_document self.namespace == namespace::HTML && node.is_in_html_doc()
} }
fn get_local_name<'a>(&'a self) -> &'a Atom { fn get_local_name<'a>(&'a self) -> &'a Atom {
@ -702,12 +701,8 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
fn GetElementsByTagNameNS(&self, maybe_ns: Option<DOMString>, fn GetElementsByTagNameNS(&self, maybe_ns: Option<DOMString>,
localname: DOMString) -> Temporary<HTMLCollection> { localname: DOMString) -> Temporary<HTMLCollection> {
let namespace = match maybe_ns {
Some(namespace) => Namespace::from_str(namespace.as_slice()),
None => Null
};
let window = window_from_node(self).root(); let window = window_from_node(self).root();
HTMLCollection::by_tag_name_ns(&*window, NodeCast::from_ref(self), localname, namespace) HTMLCollection::by_tag_name_ns(&*window, NodeCast::from_ref(self), localname, maybe_ns)
} }
fn GetElementsByClassName(&self, classes: DOMString) -> Temporary<HTMLCollection> { fn GetElementsByClassName(&self, classes: DOMString) -> Temporary<HTMLCollection> {

View file

@ -8,14 +8,16 @@ use dom::bindings::codegen::InheritTypes::{ElementCast, NodeCast};
use dom::bindings::global::Window; use dom::bindings::global::Window;
use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::js::{JS, JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
use dom::element::{Element, AttributeHandlers}; use dom::element::{Element, AttributeHandlers, ElementHelpers};
use dom::node::{Node, NodeHelpers}; use dom::node::{Node, NodeHelpers};
use dom::window::Window; use dom::window::Window;
use servo_util::atom::Atom; use servo_util::atom::Atom;
use servo_util::namespace;
use servo_util::namespace::Namespace; use servo_util::namespace::Namespace;
use servo_util::str::{DOMString, split_html_space_chars}; use servo_util::str::{DOMString, split_html_space_chars};
use serialize::{Encoder, Encodable}; use serialize::{Encoder, Encodable};
use std::ascii::StrAsciiExt;
pub trait CollectionFilter { pub trait CollectionFilter {
fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool; fn filter(&self, elem: &JSRef<Element>, root: &JSRef<Node>) -> bool;
@ -59,36 +61,82 @@ impl HTMLCollection {
HTMLCollection::new(window, Live(JS::from_rooted(root), filter)) 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) pub fn by_tag_name(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString)
-> Temporary<HTMLCollection> { -> Temporary<HTMLCollection> {
if tag.as_slice() == "*" {
return HTMLCollection::all_elements(window, root, None);
}
struct TagNameFilter { struct TagNameFilter {
tag: Atom tag: Atom,
ascii_lower_tag: Atom,
} }
impl CollectionFilter for TagNameFilter { impl CollectionFilter for TagNameFilter {
fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool { fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
elem.deref().local_name == self.tag if elem.html_element_in_html_document() {
elem.local_name == self.ascii_lower_tag
} else {
elem.local_name == self.tag
}
} }
} }
let filter = TagNameFilter { let filter = TagNameFilter {
tag: Atom::from_slice(tag.as_slice()) 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) HTMLCollection::create(window, root, box filter)
} }
pub fn by_tag_name_ns(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString, pub fn by_tag_name_ns(window: &JSRef<Window>, root: &JSRef<Node>, tag: DOMString,
namespace: Namespace) -> Temporary<HTMLCollection> { 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 { struct TagNameNSFilter {
tag: Atom, tag: Atom,
namespace: Namespace namespace_filter: Option<Namespace>
} }
impl CollectionFilter for TagNameNSFilter { impl CollectionFilter for TagNameNSFilter {
fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool { fn filter(&self, elem: &JSRef<Element>, _root: &JSRef<Node>) -> bool {
elem.deref().namespace == self.namespace && elem.deref().local_name == self.tag 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 { let filter = TagNameNSFilter {
tag: Atom::from_slice(tag.as_slice()), tag: Atom::from_slice(tag.as_slice()),
namespace: namespace namespace_filter: namespace_filter
}; };
HTMLCollection::create(window, root, box filter) HTMLCollection::create(window, root, box filter)
} }

View file

@ -389,6 +389,7 @@ pub trait NodeHelpers {
fn owner_doc(&self) -> Temporary<Document>; fn owner_doc(&self) -> Temporary<Document>;
fn set_owner_doc(&self, document: &JSRef<Document>); fn set_owner_doc(&self, document: &JSRef<Document>);
fn is_in_html_doc(&self) -> bool;
fn wait_until_safe_to_modify_dom(&self); fn wait_until_safe_to_modify_dom(&self);
@ -672,6 +673,10 @@ impl<'a> NodeHelpers for JSRef<'a, Node> {
self.owner_doc.assign(Some(document.clone())); self.owner_doc.assign(Some(document.clone()));
} }
fn is_in_html_doc(&self) -> bool {
self.owner_doc().root().is_html_document
}
fn children(&self) -> AbstractNodeChildrenIterator { fn children(&self) -> AbstractNodeChildrenIterator {
AbstractNodeChildrenIterator { AbstractNodeChildrenIterator {
current_node: self.first_child.get().map(|node| (*node.root()).clone()), current_node: self.first_child.get().map(|node| (*node.root()).clone()),

View file

@ -52,7 +52,7 @@
// test3: getElementsByTagName // test3: getElementsByTagName
{ {
is(document.getElementsByTagName("DIV").length, 0); is(document.getElementsByTagName("DIV").length, 5);
is(document.getElementsByTagName("div").length, is(document.getElementsByTagName("div").length,
document.documentElement.getElementsByTagName("div").length); document.documentElement.getElementsByTagName("div").length);