From 6a7c48ee8891c30e04ef667fba5bf05b07ff3836 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Fri, 7 Mar 2014 15:43:24 -0500 Subject: [PATCH] servo: Merge #1838 - Updates for HTMLCollection (from brunoabinader:htmlcollection-live); r=jdm Source-Repo: https://github.com/servo/servo Source-Revision: 2238d81b530d40c164df59569fb4d11914ad5c99 --- .../script/dom/bindings/codegen/Bindings.conf | 14 ++ servo/src/components/script/dom/document.rs | 77 ++++++----- servo/src/components/script/dom/element.rs | 33 +++-- .../components/script/dom/htmlcollection.rs | 80 +++++++++-- .../script/dom/htmldatalistelement.rs | 1 + .../script/dom/htmlfieldsetelement.rs | 1 + .../components/script/dom/htmlformelement.rs | 1 + .../components/script/dom/htmlmapelement.rs | 1 + .../script/dom/webidls/Document.webidl | 2 + .../script/dom/webidls/Element.webidl | 1 - .../script/dom/webidls/HTMLCollection.webidl | 3 +- .../html/content/test_htmlcollection.html | 127 ++++++++++++++++++ 12 files changed, 277 insertions(+), 64 deletions(-) create mode 100644 servo/src/test/html/content/test_htmlcollection.html diff --git a/servo/src/components/script/dom/bindings/codegen/Bindings.conf b/servo/src/components/script/dom/bindings/codegen/Bindings.conf index 3420e40a4dde..369d08bfdf03 100644 --- a/servo/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/servo/src/components/script/dom/bindings/codegen/Bindings.conf @@ -26,12 +26,23 @@ DOMInterfaces = { 'Console': {}, 'Document': { 'needsAbstract': [ + 'anchors', + 'applets', 'body', 'createComment', 'createDocumentFragment', 'createElement', 'createProcessingInstruction', 'createTextNode', + 'embeds', + 'forms', + 'getElementsByClassName', + 'getElementsByTagName', + 'getElementsByTagNameNS', + 'images', + 'links', + 'plugins', + 'scripts', 'title', ], }, @@ -43,6 +54,9 @@ DOMInterfaces = { 'attributes', 'getBoundingClientRect', 'getClientRects', + 'getElementsByClassName', + 'getElementsByTagName', + 'getElementsByTagNameNS', 'id', 'innerHTML', 'outerHTML', diff --git a/servo/src/components/script/dom/document.rs b/servo/src/components/script/dom/document.rs index 2a81d08f4a51..07660e30e88b 100644 --- a/servo/src/components/script/dom/document.rs +++ b/servo/src/components/script/dom/document.rs @@ -35,7 +35,7 @@ use dom::window::Window; use html::hubbub_html_parser::build_element_from_tag; use hubbub::hubbub::{QuirksMode, NoQuirks, LimitedQuirks, FullQuirks}; use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage}; -use servo_util::namespace::Null; +use servo_util::namespace::{Namespace, Null}; use servo_util::str::DOMString; use extra::url::{Url, from_str}; @@ -212,8 +212,22 @@ impl Document { } // http://dom.spec.whatwg.org/#dom-document-getelementsbytagname - pub fn GetElementsByTagName(&self, tag: DOMString) -> JS { - self.createHTMLCollection(|elem| elem.get().tag_name == tag) + pub fn GetElementsByTagName(&self, abstract_self: &JS, tag_name: DOMString) -> JS { + HTMLCollection::by_tag_name(&self.window, &NodeCast::from(abstract_self), tag_name) + } + + // http://dom.spec.whatwg.org/#dom-document-getelementsbytagnamens + pub fn GetElementsByTagNameNS(&self, abstract_self: &JS, maybe_ns: Option, tag_name: DOMString) -> JS { + let namespace = match maybe_ns { + Some(namespace) => Namespace::from_str(namespace), + None => Null + }; + HTMLCollection::by_tag_name_ns(&self.window, &NodeCast::from(abstract_self), tag_name, namespace) + } + + // http://dom.spec.whatwg.org/#dom-document-getelementsbyclassname + pub fn GetElementsByClassName(&self, abstract_self: &JS, classes: DOMString) -> JS { + HTMLCollection::by_class_name(&self.window, &NodeCast::from(abstract_self), classes) } // http://dom.spec.whatwg.org/#dom-nonelementparentnode-getelementbyid @@ -417,43 +431,49 @@ impl Document { }) } - pub fn Images(&self) -> JS { - self.createHTMLCollection(|elem| "img" == elem.get().tag_name) + pub fn Images(&self, abstract_self: &JS) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1847 + HTMLCollection::by_tag_name(&self.window, &NodeCast::from(abstract_self), ~"img") } - pub fn Embeds(&self) -> JS { - self.createHTMLCollection(|elem| "embed" == elem.get().tag_name) + pub fn Embeds(&self, abstract_self: &JS) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1847 + HTMLCollection::by_tag_name(&self.window, &NodeCast::from(abstract_self), ~"embed") } - pub fn Plugins(&self) -> JS { - self.Embeds() + pub fn Plugins(&self, abstract_self: &JS) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1847 + self.Embeds(abstract_self) } - pub fn Links(&self) -> JS { - self.createHTMLCollection(|elem| { + pub fn Links(&self, abstract_self: &JS) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1847 + HTMLCollection::create(&self.window, &NodeCast::from(abstract_self), |elem| { ("a" == elem.get().tag_name || "area" == elem.get().tag_name) && elem.get().get_attribute(Null, "href").is_some() }) } - pub fn Forms(&self) -> JS { - self.createHTMLCollection(|elem| "form" == elem.get().tag_name) + pub fn Forms(&self, abstract_self: &JS) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1847 + HTMLCollection::by_tag_name(&self.window, &NodeCast::from(abstract_self), ~"form") } - pub fn Scripts(&self) -> JS { - self.createHTMLCollection(|elem| "script" == elem.get().tag_name) + pub fn Scripts(&self, abstract_self: &JS) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1847 + HTMLCollection::by_tag_name(&self.window, &NodeCast::from(abstract_self), ~"script") } - pub fn Anchors(&self) -> JS { - self.createHTMLCollection(|elem| { - "a" == elem.get().tag_name && - elem.get().get_attribute(Null, "name").is_some() + pub fn Anchors(&self, abstract_self: &JS) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1847 + HTMLCollection::create(&self.window, &NodeCast::from(abstract_self), |elem| { + "a" == elem.get().tag_name && elem.get().get_attribute(Null, "name").is_some() }) } - pub fn Applets(&self) -> JS { + pub fn Applets(&self, abstract_self: &JS) -> JS { // FIXME: This should be return OBJECT elements containing applets. - self.createHTMLCollection(|elem| "applet" == elem.get().tag_name) + HTMLCollection::by_tag_name(&self.window, &NodeCast::from(abstract_self), ~"applet") } pub fn create_collection(&self, callback: |elem: &JS| -> Option>) -> ~[JS] { @@ -473,21 +493,6 @@ impl Document { nodes } - pub fn createHTMLCollection(&self, callback: |elem: &JS| -> bool) -> JS { - HTMLCollection::new(&self.window, self.create_collection(|node| { - if !node.is_element() { - return None; - } - - let element: JS = ElementCast::to(node); - if !callback(&element) { - return None; - } - - Some(element) - })) - } - pub fn createNodeList(&self, callback: |node: &JS| -> bool) -> JS { NodeList::new_simple_list(&self.window, self.create_collection(|node| { if !callback(node) { diff --git a/servo/src/components/script/dom/element.rs b/servo/src/components/script/dom/element.rs index 4ff7872aa807..95e4e7f9af94 100644 --- a/servo/src/components/script/dom/element.rs +++ b/servo/src/components/script/dom/element.rs @@ -370,6 +370,13 @@ impl Element { _ => false } } + + pub fn has_class(&self, name: &str) -> bool { + // FIXME: https://github.com/mozilla/servo/issues/1840 + let class_names = self.get_string_attribute("class"); + let mut classes = class_names.split(' '); + classes.any(|class| name == class) + } } // http://www.whatwg.org/html/#reflecting-content-attributes-in-idl-attributes @@ -503,25 +510,27 @@ impl Element { self.GetAttributeNS(namespace, local_name).is_some() } - // http://dom.spec.whatwg.org/#dom-element-getelementsbytagname - pub fn GetElementsByTagName(&self, _localname: DOMString) -> JS { - // FIXME: stub - https://github.com/mozilla/servo/issues/1660 + pub fn GetElementsByTagName(&self, abstract_self: &JS, localname: DOMString) -> JS { let doc = self.node.owner_doc(); - HTMLCollection::new(&doc.get().window, ~[]) + let doc = doc.get(); + HTMLCollection::by_tag_name(&doc.window, &NodeCast::from(abstract_self), localname) } - // http://dom.spec.whatwg.org/#dom-element-getelementsbytagnamens - pub fn GetElementsByTagNameNS(&self, _namespace: Option, _localname: DOMString) -> Fallible> { - // FIXME: stub - https://github.com/mozilla/servo/issues/1660 + pub fn GetElementsByTagNameNS(&self, abstract_self: &JS, maybe_ns: Option, + localname: DOMString) -> JS { let doc = self.node.owner_doc(); - Ok(HTMLCollection::new(&doc.get().window, ~[])) + let doc = doc.get(); + let namespace = match maybe_ns { + Some(namespace) => Namespace::from_str(namespace), + None => Null + }; + HTMLCollection::by_tag_name_ns(&doc.window, &NodeCast::from(abstract_self), localname, namespace) } - // http://dom.spec.whatwg.org/#dom-element-getelementsbyclassname - pub fn GetElementsByClassName(&self, _names: DOMString) -> JS { - // FIXME: stub - https://github.com/mozilla/servo/issues/1660 + pub fn GetElementsByClassName(&self, abstract_self: &JS, classes: DOMString) -> JS { let doc = self.node.owner_doc(); - HTMLCollection::new(&doc.get().window, ~[]) + let doc = doc.get(); + HTMLCollection::by_class_name(&doc.window, &NodeCast::from(abstract_self), classes) } // http://dev.w3.org/csswg/cssom-view/#dom-element-getclientrects diff --git a/servo/src/components/script/dom/htmlcollection.rs b/servo/src/components/script/dom/htmlcollection.rs index ff2cfa7b5a14..96e04d46d3bd 100644 --- a/servo/src/components/script/dom/htmlcollection.rs +++ b/servo/src/components/script/dom/htmlcollection.rs @@ -2,18 +2,16 @@ * 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::InheritTypes::{ElementCast}; use dom::bindings::codegen::HTMLCollectionBinding; use dom::bindings::js::JS; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; -use dom::bindings::error::Fallible; use dom::element::Element; +use dom::node::{Node, NodeHelpers}; use dom::window::Window; +use servo_util::namespace::Namespace; use servo_util::str::DOMString; -use js::jsapi::{JSObject, JSContext}; - -use std::ptr; - #[deriving(Encodable)] pub struct HTMLCollection { elements: ~[JS], @@ -34,11 +32,44 @@ impl HTMLCollection { reflect_dom_object(~HTMLCollection::new_inherited(window.clone(), elements), window, HTMLCollectionBinding::Wrap) } - +} + +impl HTMLCollection { + pub fn create(window: &JS, root: &JS, predicate: |elem: &JS| -> bool) -> JS { + let mut elements = ~[]; + for child in root.traverse_preorder() { + if child.is_element() { + let elem: JS = ElementCast::to(&child); + if predicate(&elem) { + elements.push(elem); + } + } + } + HTMLCollection::new(window, elements) + } + + pub fn by_tag_name(window: &JS, root: &JS, tag_name: DOMString) -> JS { + HTMLCollection::create(window, root, |elem| elem.get().tag_name == tag_name) + } + + pub fn by_tag_name_ns(window: &JS, root: &JS, tag_name: DOMString, namespace: Namespace) -> JS { + HTMLCollection::create(window, root, |elem| elem.get().namespace == namespace && elem.get().tag_name == tag_name) + } + + pub fn by_class_name(window: &JS, root: &JS, classes: DOMString) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1840 + let classes: ~[&str] = classes.split(' ').collect(); + HTMLCollection::create(window, root, |elem| classes.iter().all(|class| elem.get().has_class(*class))) + } +} + +impl HTMLCollection { + // http://dom.spec.whatwg.org/#dom-htmlcollection-length pub fn Length(&self) -> u32 { self.elements.len() as u32 } + // http://dom.spec.whatwg.org/#dom-htmlcollection-item pub fn Item(&self, index: u32) -> Option> { if index < self.Length() { Some(self.elements[index].clone()) @@ -47,17 +78,40 @@ impl HTMLCollection { } } - pub fn NamedItem(&self, _cx: *JSContext, _name: DOMString) -> Fallible<*JSObject> { - Ok(ptr::null()) - } + // http://dom.spec.whatwg.org/#dom-htmlcollection-nameditem + pub fn NamedItem(&self, key: DOMString) -> Option> { + // Step 1. + if key.is_empty() { + return None; + } + // Step 2. + self.elements.iter().find(|elem| { + let elem = elem.get(); + elem.get_string_attribute("name") == key || elem.get_string_attribute("id") == key + }).map(|maybe_elem| maybe_elem.clone()) + } +} + +impl HTMLCollection { pub fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option> { - *found = true; - self.Item(index) + let maybe_elem = self.Item(index); + *found = maybe_elem.is_some(); + maybe_elem } - pub fn NamedGetter(&self, _cx: *JSContext, _name: Option, _found: &mut bool) -> Fallible<*JSObject> { - Ok(ptr::null()) + pub fn NamedGetter(&self, maybe_name: Option, found: &mut bool) -> Option> { + match maybe_name { + Some(name) => { + let maybe_elem = self.NamedItem(name); + *found = maybe_elem.is_some(); + maybe_elem + }, + None => { + *found = false; + None + } + } } } diff --git a/servo/src/components/script/dom/htmldatalistelement.rs b/servo/src/components/script/dom/htmldatalistelement.rs index 6cd888029c2c..ef3f91f1702a 100644 --- a/servo/src/components/script/dom/htmldatalistelement.rs +++ b/servo/src/components/script/dom/htmldatalistelement.rs @@ -42,6 +42,7 @@ impl HTMLDataListElement { impl HTMLDataListElement { pub fn Options(&self) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1842 let doc = self.htmlelement.element.node.owner_doc(); let doc = doc.get(); HTMLCollection::new(&doc.window, ~[]) diff --git a/servo/src/components/script/dom/htmlfieldsetelement.rs b/servo/src/components/script/dom/htmlfieldsetelement.rs index cdfc6eab4029..4dec639d041d 100644 --- a/servo/src/components/script/dom/htmlfieldsetelement.rs +++ b/servo/src/components/script/dom/htmlfieldsetelement.rs @@ -69,6 +69,7 @@ impl HTMLFieldSetElement { } pub fn Elements(&self) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1843 let doc = self.htmlelement.element.node.owner_doc(); let doc = doc.get(); HTMLCollection::new(&doc.window, ~[]) diff --git a/servo/src/components/script/dom/htmlformelement.rs b/servo/src/components/script/dom/htmlformelement.rs index 03d2ee8eeb16..a1ca7d5dc2f4 100644 --- a/servo/src/components/script/dom/htmlformelement.rs +++ b/servo/src/components/script/dom/htmlformelement.rs @@ -115,6 +115,7 @@ impl HTMLFormElement { } pub fn Elements(&self) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1844 let doc = self.htmlelement.element.node.owner_doc(); let doc = doc.get(); HTMLCollection::new(&doc.window, ~[]) diff --git a/servo/src/components/script/dom/htmlmapelement.rs b/servo/src/components/script/dom/htmlmapelement.rs index 41516e9a5d29..d343073a2785 100644 --- a/servo/src/components/script/dom/htmlmapelement.rs +++ b/servo/src/components/script/dom/htmlmapelement.rs @@ -51,6 +51,7 @@ impl HTMLMapElement { } pub fn Areas(&self) -> JS { + // FIXME: https://github.com/mozilla/servo/issues/1845 let doc = self.htmlelement.element.node.owner_doc(); let doc = doc.get(); HTMLCollection::new(&doc.window, ~[]) diff --git a/servo/src/components/script/dom/webidls/Document.webidl b/servo/src/components/script/dom/webidls/Document.webidl index 11e92a0930e1..98df0b2aef9c 100644 --- a/servo/src/components/script/dom/webidls/Document.webidl +++ b/servo/src/components/script/dom/webidls/Document.webidl @@ -21,6 +21,8 @@ interface Document : Node { readonly attribute DocumentType? doctype; readonly attribute Element? documentElement; HTMLCollection getElementsByTagName(DOMString localName); + HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); + HTMLCollection getElementsByClassName(DOMString classNames); Element? getElementById(DOMString elementId); [Creator, Throws] diff --git a/servo/src/components/script/dom/webidls/Element.webidl b/servo/src/components/script/dom/webidls/Element.webidl index 6c393f00067a..e26c9415d4a7 100644 --- a/servo/src/components/script/dom/webidls/Element.webidl +++ b/servo/src/components/script/dom/webidls/Element.webidl @@ -51,7 +51,6 @@ interface Element : Node { boolean hasAttributeNS(DOMString? namespace, DOMString localName); HTMLCollection getElementsByTagName(DOMString localName); - [Throws] HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); HTMLCollection getElementsByClassName(DOMString classNames); }; diff --git a/servo/src/components/script/dom/webidls/HTMLCollection.webidl b/servo/src/components/script/dom/webidls/HTMLCollection.webidl index 2c1fc60e04a6..7389e7768345 100644 --- a/servo/src/components/script/dom/webidls/HTMLCollection.webidl +++ b/servo/src/components/script/dom/webidls/HTMLCollection.webidl @@ -10,6 +10,5 @@ interface HTMLCollection { readonly attribute unsigned long length; getter Element? item(unsigned long index); - [Throws] - getter object? namedItem(DOMString name); // only returns Element + getter Element? namedItem(DOMString name); }; diff --git a/servo/src/test/html/content/test_htmlcollection.html b/servo/src/test/html/content/test_htmlcollection.html new file mode 100644 index 000000000000..e024a331e802 --- /dev/null +++ b/servo/src/test/html/content/test_htmlcollection.html @@ -0,0 +1,127 @@ + + + + + + + +
+
+
+
+
+

+

+

+

+ +