From 7759358e0978909986216409884b9d1d66afe87f Mon Sep 17 00:00:00 2001 From: Jim Hoskins Date: Mon, 12 Jan 2015 22:34:17 -0800 Subject: [PATCH] Implement Element#closest fixes #4603 - Add definition to the Element.webidl and implementation to element.rs. - Create inclusive_ancestors helper in NodeHelpers - Update test expectations --- components/script/dom/element.rs | 17 +++++ components/script/dom/node.rs | 7 ++ components/script/dom/webidls/Element.webidl | 3 + tests/wpt/metadata/dom/interfaces.html.ini | 9 --- .../dom/nodes/Element-closest.html.ini | 68 ------------------- .../wpt/metadata/html/dom/interfaces.html.ini | 6 -- 6 files changed, 27 insertions(+), 83 deletions(-) diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 129756c1575..14f48f5f3b6 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -1120,6 +1120,23 @@ impl<'a> ElementMethods for JSRef<'a, Element> { } } } + + // https://dom.spec.whatwg.org/#dom-element-closest + fn Closest(self, selectors: DOMString) -> Fallible>> { + let parser_context = ParserContext { + origin: StylesheetOrigin::Author, + }; + match style::parse_selector_list_from_str(&parser_context, selectors.as_slice()) { + Err(()) => Err(Syntax), + Ok(ref selectors) => { + let root: JSRef = NodeCast::from_ref(self); + Ok(root.inclusive_ancestors() + .filter_map(ElementCast::to_ref) + .find(|element| matches(selectors, &NodeCast::from_ref(*element), &mut None)) + .map(Temporary::from_rooted)) + } + } + } } pub fn get_attribute_parts<'a>(name: &'a str) -> (Option<&'a str>, &'a str) { diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index bc44aeda8ba..fb336efd85b 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -397,6 +397,7 @@ impl<'a> Iterator> for QuerySelectorIterator<'a> { pub trait NodeHelpers<'a> { fn ancestors(self) -> AncestorIterator<'a>; + fn inclusive_ancestors(self) -> AncestorIterator<'a>; fn children(self) -> NodeChildrenIterator<'a>; fn rev_children(self) -> ReverseChildrenIterator; fn child_elements(self) -> ChildElementIterator<'a>; @@ -798,6 +799,12 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } } + fn inclusive_ancestors(self) -> AncestorIterator<'a> { + AncestorIterator { + current: Some(self.clone()) + } + } + fn owner_doc(self) -> Temporary { self.owner_doc.get().unwrap() } diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl index fc737bdb9ec..820d8b5b4f2 100644 --- a/components/script/dom/webidls/Element.webidl +++ b/components/script/dom/webidls/Element.webidl @@ -44,6 +44,9 @@ interface Element : Node { boolean hasAttribute(DOMString name); boolean hasAttributeNS(DOMString? namespace, DOMString localName); + [Throws] + Element? closest(DOMString selectors); + [Throws] boolean matches(DOMString selectors); diff --git a/tests/wpt/metadata/dom/interfaces.html.ini b/tests/wpt/metadata/dom/interfaces.html.ini index fbd2b3f48c6..9faa4357cc9 100644 --- a/tests/wpt/metadata/dom/interfaces.html.ini +++ b/tests/wpt/metadata/dom/interfaces.html.ini @@ -285,9 +285,6 @@ [Element interface: operation removeAttributeNode(Attr)] expected: FAIL - [Element interface: operation closest(DOMString)] - expected: FAIL - [Element interface: attribute firstElementChild] expected: FAIL @@ -357,12 +354,6 @@ [Element interface: calling removeAttributeNode(Attr) on element with too few arguments must throw TypeError] expected: FAIL - [Element interface: element must inherit property "closest" with the proper type (22)] - expected: FAIL - - [Element interface: calling closest(DOMString) on element with too few arguments must throw TypeError] - expected: FAIL - [Element interface: element must inherit property "firstElementChild" with the proper type (28)] expected: FAIL diff --git a/tests/wpt/metadata/dom/nodes/Element-closest.html.ini b/tests/wpt/metadata/dom/nodes/Element-closest.html.ini index 5c81c4b29cf..ea5ab2754ae 100644 --- a/tests/wpt/metadata/dom/nodes/Element-closest.html.ini +++ b/tests/wpt/metadata/dom/nodes/Element-closest.html.ini @@ -1,77 +1,9 @@ [Element-closest.html] type: testharness - [Element.closest with context node \'test12\' and selector \'select\'] - expected: FAIL - - [Element.closest with context node \'test13\' and selector \'fieldset\'] - expected: FAIL - - [Element.closest with context node \'test13\' and selector \'div\'] - expected: FAIL - - [Element.closest with context node \'test3\' and selector \'body\'] - expected: FAIL - - [Element.closest with context node \'test4\' and selector \'[default\]\'] - expected: FAIL - - [Element.closest with context node \'test4\' and selector \'[selected\]\'] - expected: FAIL - - [Element.closest with context node \'test11\' and selector \'[selected\]\'] - expected: FAIL - - [Element.closest with context node \'test12\' and selector \'[name="form-a"\]\'] - expected: FAIL - - [Element.closest with context node \'test13\' and selector \'form[name="form-a"\]\'] - expected: FAIL - - [Element.closest with context node \'test9\' and selector \'input[required\]\'] - expected: FAIL - - [Element.closest with context node \'test9\' and selector \'select[required\]\'] - expected: FAIL - - [Element.closest with context node \'test13\' and selector \'div:not(.div1)\'] - expected: FAIL - - [Element.closest with context node \'test6\' and selector \'div.div3\'] - expected: FAIL - - [Element.closest with context node \'test1\' and selector \'div#test7\'] - expected: FAIL - - [Element.closest with context node \'test12\' and selector \'.div3 > .div2\'] - expected: FAIL - - [Element.closest with context node \'test12\' and selector \'.div3 > .div1\'] - expected: FAIL - - [Element.closest with context node \'test9\' and selector \'form > input[required\]\'] - expected: FAIL - - [Element.closest with context node \'test12\' and selector \'fieldset > select[required\]\'] - expected: FAIL - - [Element.closest with context node \'test6\' and selector \'input + fieldset\'] - expected: FAIL - - [Element.closest with context node \'test3\' and selector \'form + form\'] - expected: FAIL - - [Element.closest with context node \'test5\' and selector \'form + form\'] - expected: FAIL [Element.closest with context node \'test10\' and selector \':empty\'] expected: FAIL - [Element.closest with context node \'test11\' and selector \':last-child\'] - expected: FAIL - - [Element.closest with context node \'test12\' and selector \':first-child\'] - expected: FAIL - [Element.closest with context node \'test11\' and selector \':invalid\'] expected: FAIL diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index f8fbde7b085..5df709b991c 100644 --- a/tests/wpt/metadata/html/dom/interfaces.html.ini +++ b/tests/wpt/metadata/html/dom/interfaces.html.ini @@ -1488,12 +1488,6 @@ [Element interface: calling removeAttributeNode(Attr) on document.createElement("noscript") with too few arguments must throw TypeError] expected: FAIL - [Element interface: document.createElement("noscript") must inherit property "closest" with the proper type (22)] - expected: FAIL - - [Element interface: calling closest(DOMString) on document.createElement("noscript") with too few arguments must throw TypeError] - expected: FAIL - [Element interface: document.createElement("noscript") must inherit property "firstElementChild" with the proper type (28)] expected: FAIL