From 8187916ee4228fd00a92fdac1f6ee96fea9395f1 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Mon, 2 Jun 2014 14:45:26 -0700 Subject: [PATCH 1/8] Implement TElement for JSRef --- src/components/script/dom/element.rs | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 66027956a65..2e300985eed 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -196,6 +196,8 @@ impl LayoutElementHelpers for JS { pub trait ElementHelpers { fn html_element_in_html_document(&self) -> bool; + fn get_local_name<'a>(&'a self) -> &'a str; + fn get_namespace<'a>(&'a self) -> &'a Namespace; } impl<'a> ElementHelpers for JSRef<'a, Element> { @@ -204,6 +206,14 @@ impl<'a> ElementHelpers for JSRef<'a, Element> { let node: &JSRef = NodeCast::from_ref(self); is_html && node.owner_doc().root().is_html_document } + + fn get_local_name<'a>(&'a self) -> &'a str { + self.deref().local_name.as_slice() + } + + fn get_namespace<'a>(&'a self) -> &'a Namespace { + &self.deref().namespace + } } pub trait AttributeHandlers { @@ -798,3 +808,33 @@ impl<'a> VirtualMethods for JSRef<'a, Element> { } } } + +impl<'a> style::TElement for JSRef<'a, Element> { + fn get_attr(&self, namespace: &Namespace, attr: &str) -> Option<&'static str> { + self.get_attribute(namespace.clone(), attr).root().map(|attr| { + unsafe { mem::transmute(attr.deref().value_ref()) } + }) + } + fn get_link(&self) -> Option<&'static str> { + // FIXME: This is HTML only. + let node: &JSRef = NodeCast::from_ref(self); + match node.type_id() { + // http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html# + // selector-link + ElementNodeTypeId(HTMLAnchorElementTypeId) | + ElementNodeTypeId(HTMLAreaElementTypeId) | + ElementNodeTypeId(HTMLLinkElementTypeId) => self.get_attr(&namespace::Null, "href"), + _ => None, + } + } + fn get_local_name<'a>(&'a self) -> &'a str { + (self as &ElementHelpers).get_local_name() + } + fn get_namespace<'a>(&'a self) -> &'a Namespace { + (self as &ElementHelpers).get_namespace() + } + fn get_hover_state(&self) -> bool { + let node: &JSRef = NodeCast::from_ref(self); + node.get_hover_state() + } +} From f78d04b6209ff3ee1195525313e8d657be1cda47 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Tue, 3 Jun 2014 14:01:30 -0700 Subject: [PATCH 2/8] Implement TNode for JS --- src/components/script/dom/node.rs | 50 +++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index c7cde126e42..af0dbd97979 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -4,7 +4,8 @@ //! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements. -use dom::attr::Attr; +use cssparser::tokenize; +use dom::attr::{Attr, AttrMethods}; use dom::bindings::codegen::InheritTypes::{CommentCast, DocumentCast, DocumentTypeCast}; use dom::bindings::codegen::InheritTypes::{ElementCast, TextCast, NodeCast, ElementDerived}; use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeBase, NodeDerived}; @@ -21,7 +22,8 @@ use dom::comment::Comment; use dom::document::{Document, DocumentMethods, DocumentHelpers, HTMLDocument, NonHTMLDocument}; use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; -use dom::element::{Element, ElementMethods, ElementTypeId, HTMLAnchorElementTypeId}; +use dom::element::{AttributeHandlers, Element, ElementMethods, ElementTypeId}; +use dom::element::{HTMLAnchorElementTypeId, ElementHelpers}; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::nodelist::{NodeList}; use dom::processinginstruction::{ProcessingInstruction, ProcessingInstructionMethods}; @@ -42,6 +44,7 @@ use libc::uintptr_t; use std::cell::{Cell, RefCell, Ref, RefMut}; use std::iter::{Map, Filter}; use std::mem; +use style; use style::ComputedValues; use sync::Arc; @@ -1901,3 +1904,46 @@ impl<'a> VirtualMethods for JSRef<'a, Node> { Some(eventtarget as &mut VirtualMethods:) } } + +impl<'a> style::TNode> for JSRef<'a, Node> { + fn parent_node(&self) -> Option> { + (self as &NodeHelpers).parent_node().map(|node| *node.root()) + } + fn prev_sibling(&self) -> Option> { + (self as &NodeHelpers).prev_sibling().map(|node| *node.root()) + } + fn next_sibling(&self) -> Option> { + (self as &NodeHelpers).next_sibling().map(|node| *node.root()) + } + fn is_document(&self) -> bool { + (self as &NodeHelpers).is_document() + } + fn is_element(&self) -> bool { + (self as &NodeHelpers).is_element() + } + fn as_element(&self) -> JSRef<'a, Element> { + let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self); + assert!(elem.is_some()); + *elem.unwrap() + } + fn match_attr(&self, attr: &style::AttrSelector, test: |&str| -> bool) -> bool { + let name = { + let elem: Option<&JSRef<'a, Element>> = ElementCast::to_ref(self); + assert!(elem.is_some()); + let elem: &ElementHelpers = elem.unwrap() as &ElementHelpers; + if elem.html_element_in_html_document() { + attr.lower_name.as_slice() + } else { + attr.name.as_slice() + } + }; + match attr.namespace { + style::SpecificNamespace(ref ns) => { + self.as_element().get_attribute(ns.clone(), name).root() + .map_or(false, |attr| test(attr.deref().Value().as_slice())) + }, + // FIXME: https://github.com/mozilla/servo/issues/1558 + style::AnyNamespace => false, + } + } +} From f0aadb790da0b0973c4cf182a40f8b8950625de5 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Tue, 3 Jun 2014 14:16:49 -0700 Subject: [PATCH 3/8] Added ParentNode.querySelector skeleton --- src/components/script/dom/document.rs | 5 +++++ src/components/script/dom/documentfragment.rs | 7 +++++++ src/components/script/dom/element.rs | 5 +++++ src/components/script/dom/webidls/ParentNode.webidl | 6 ++++++ 4 files changed, 23 insertions(+) diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index a00a12c26de..497a739e172 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -328,6 +328,7 @@ pub trait DocumentMethods { fn Applets(&self) -> Temporary; fn Location(&self) -> Temporary; fn Children(&self) -> Temporary; + fn QuerySelector(&self, selectors: DOMString) -> Fallible>>; fn GetOnload(&self) -> Option; fn SetOnload(&mut self, listener: Option); } @@ -810,6 +811,10 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { HTMLCollection::children(&*window, NodeCast::from_ref(self)) } + fn QuerySelector(&self, selectors: DOMString) -> Fallible>> { + Ok(None) + } + fn GetOnload(&self) -> Option { let eventtarget: &JSRef = EventTargetCast::from_ref(self); eventtarget.get_event_handler_common("load") diff --git a/src/components/script/dom/documentfragment.rs b/src/components/script/dom/documentfragment.rs index 886b1b894e1..2b3dc5afd80 100644 --- a/src/components/script/dom/documentfragment.rs +++ b/src/components/script/dom/documentfragment.rs @@ -7,10 +7,12 @@ use dom::bindings::codegen::Bindings::DocumentFragmentBinding; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::error::Fallible; use dom::document::Document; +use dom::element::Element; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlcollection::HTMLCollection; use dom::node::{DocumentFragmentNodeTypeId, Node, window_from_node}; use dom::window::{Window, WindowMethods}; +use servo_util::str::DOMString; #[deriving(Encodable)] pub struct DocumentFragment { @@ -46,6 +48,7 @@ impl DocumentFragment { pub trait DocumentFragmentMethods { fn Children(&self) -> Temporary; + fn QuerySelector(&self, selectors: DOMString) -> Fallible>>; } impl<'a> DocumentFragmentMethods for JSRef<'a, DocumentFragment> { @@ -53,4 +56,8 @@ impl<'a> DocumentFragmentMethods for JSRef<'a, DocumentFragment> { let window = window_from_node(self).root(); HTMLCollection::children(&window.root_ref(), NodeCast::from_ref(self)) } + + fn QuerySelector(&self, selectors: DOMString) -> Fallible>> { + Ok(None) + } } diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 2e300985eed..ee2245e8394 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -429,6 +429,7 @@ pub trait ElementMethods { fn GetInnerHTML(&self) -> Fallible; fn GetOuterHTML(&self) -> Fallible; fn Children(&self) -> Temporary; + fn QuerySelector(&self, selectors: DOMString) -> Fallible>>; fn Remove(&self); } @@ -704,6 +705,10 @@ impl<'a> ElementMethods for JSRef<'a, Element> { HTMLCollection::children(&*window, NodeCast::from_ref(self)) } + fn QuerySelector(&self, selectors: DOMString) -> Fallible>> { + Ok(None) + } + // http://dom.spec.whatwg.org/#dom-childnode-remove fn Remove(&self) { let node: &JSRef = NodeCast::from_ref(self); diff --git a/src/components/script/dom/webidls/ParentNode.webidl b/src/components/script/dom/webidls/ParentNode.webidl index 54b05fbbad9..70ab892fea8 100644 --- a/src/components/script/dom/webidls/ParentNode.webidl +++ b/src/components/script/dom/webidls/ParentNode.webidl @@ -22,4 +22,10 @@ interface ParentNode { // Not implemented yet // void prepend((Node or DOMString)... nodes); // void append((Node or DOMString)... nodes); + + //Element? query(DOMString relativeSelectors); + //[NewObject] Elements queryAll(DOMString relativeSelectors); + [Throws] + Element? querySelector(DOMString selectors); + //[NewObject] NodeList querySelectorAll(DOMString selectors); }; From 249c484c243eaf85049003290b8ef424adefc6be Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Wed, 4 Jun 2014 13:28:36 -0700 Subject: [PATCH 4/8] Implement querySelector for Document --- src/components/script/dom/document.rs | 8 +++++-- src/components/script/dom/node.rs | 29 ++++++++++++++++++++++- src/components/script/script.rs | 1 + src/components/style/selector_matching.rs | 2 +- src/components/style/style.rs | 3 ++- 5 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index 497a739e172..9b4d7aaf55d 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -12,7 +12,8 @@ use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable, TemporaryPushabl use dom::bindings::js::OptionalRootable; use dom::bindings::trace::Untraceable; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; -use dom::bindings::error::{ErrorResult, Fallible, NotSupported, InvalidCharacter, HierarchyRequest, NamespaceError}; +use dom::bindings::error::{ErrorResult, Fallible, NotSupported, InvalidCharacter}; +use dom::bindings::error::{HierarchyRequest, NamespaceError}; use dom::bindings::utils::{xml_name_type, InvalidXMLName, Name, QName}; use dom::comment::Comment; use dom::customevent::CustomEvent; @@ -806,13 +807,16 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { window.Location() } + // http://dom.spec.whatwg.org/#dom-parentnode-children fn Children(&self) -> Temporary { let window = self.window.root(); HTMLCollection::children(&*window, NodeCast::from_ref(self)) } + // http://dom.spec.whatwg.org/#dom-parentnode-queryselector fn QuerySelector(&self, selectors: DOMString) -> Fallible>> { - Ok(None) + let root: &JSRef = NodeCast::from_ref(self); + root.query_selector(selectors) } fn GetOnload(&self) -> Option { diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index af0dbd97979..5fa704810b2 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -15,7 +15,7 @@ use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, Root, OptionalUnr use dom::bindings::js::{OptionalSettable, TemporaryPushable, OptionalRootedRootable}; use dom::bindings::js::{ResultRootable, OptionalRootable}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; -use dom::bindings::error::{ErrorResult, Fallible, NotFound, HierarchyRequest}; +use dom::bindings::error::{ErrorResult, Fallible, NotFound, HierarchyRequest, Syntax}; use dom::bindings::utils; use dom::characterdata::{CharacterData, CharacterDataMethods}; use dom::comment::Comment; @@ -36,6 +36,7 @@ use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery, C LayoutChan, ReapLayoutDataMsg, TrustedNodeAddress, UntrustedNodeAddress}; use servo_util::geometry::Au; use servo_util::str::{DOMString, null_str_as_empty}; +use style::{parse_selector_list, matches_compound_selector, NamespaceMap}; use js::jsapi::{JSContext, JSObject, JSRuntime}; use js::jsfriendapi; @@ -396,6 +397,8 @@ pub trait NodeHelpers { fn get_bounding_content_box(&self) -> Rect; fn get_content_boxes(&self) -> Vec>; + fn query_selector(&self, selectors: DOMString) -> Fallible>>; + fn remove_self(&self); } @@ -552,6 +555,30 @@ impl<'a> NodeHelpers for JSRef<'a, Node> { rects } + // http://dom.spec.whatwg.org/#dom-parentnode-queryselector + fn query_selector(&self, selectors: DOMString) -> Fallible>> { + // Step 1. + let namespace = NamespaceMap::new(); + match parse_selector_list(tokenize(selectors.as_slice()).map(|(token, _)| token).collect(), &namespace) { + // Step 2. + None => return Err(Syntax), + // Step 3. + Some(ref selectors) => { + for selector in selectors.iter() { + assert!(selector.pseudo_element.is_none()); + for elem in self.child_elements() { + let node: &JSRef = NodeCast::from_ref(&elem); + let mut _shareable: bool = false; + if matches_compound_selector(selector.compound_selectors.deref(), node, &mut _shareable) { + return Ok(Some(Temporary::from_rooted(&elem))); + } + } + } + } + } + Ok(None) + } + fn ancestors(&self) -> AncestorIterator { AncestorIterator { current: self.parent_node.get().map(|node| (*node.root()).clone()), diff --git a/src/components/script/script.rs b/src/components/script/script.rs index f6dd0ba7025..860187dd5d2 100644 --- a/src/components/script/script.rs +++ b/src/components/script/script.rs @@ -17,6 +17,7 @@ extern crate log; extern crate debug; +extern crate cssparser; extern crate collections; extern crate geom; extern crate hubbub; diff --git a/src/components/style/selector_matching.rs b/src/components/style/selector_matching.rs index 66a874ae798..061a8fc76b5 100644 --- a/src/components/style/selector_matching.rs +++ b/src/components/style/selector_matching.rs @@ -521,7 +521,7 @@ impl Ord for MatchedProperty { /// `shareable` to false unless you are willing to update the style sharing logic. Otherwise things /// will almost certainly break as nodes will start mistakenly sharing styles. (See the code in /// `main/css/matching.rs`.) -fn matches_compound_selector>( selector: &CompoundSelector, element: &N, diff --git a/src/components/style/style.rs b/src/components/style/style.rs index e667fa743b3..0368b71c1f6 100644 --- a/src/components/style/style.rs +++ b/src/components/style/style.rs @@ -33,7 +33,7 @@ extern crate servo_util = "util"; // Public API pub use stylesheets::{Stylesheet, CSSRule, StyleRule}; pub use selector_matching::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOrigin, UserOrigin}; -pub use selector_matching::{MatchedProperty}; +pub use selector_matching::{MatchedProperty, matches_compound_selector}; pub use properties::{cascade, cascade_anonymous}; pub use properties::{PropertyDeclaration, ComputedValues, computed_values, style_structs}; pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes @@ -43,6 +43,7 @@ pub use errors::with_errors_silenced; pub use node::{TElement, TNode}; pub use selectors::{PseudoElement, Before, After, AttrSelector, SpecificNamespace, AnyNamespace}; pub use selectors::{NamespaceConstraint, Selector, CompoundSelector, SimpleSelector, Combinator}; +pub use selectors::{parse_selector_list}; pub use namespaces::NamespaceMap; pub use media_queries::{MediaRule, MediaQueryList, MediaQuery, Device, MediaType, MediaQueryType}; From c874c6470cfb3330c2bb5753b7e9661582ad3087 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Wed, 4 Jun 2014 13:30:38 -0700 Subject: [PATCH 5/8] Implement querySelector for DocumentFragment --- src/components/script/dom/documentfragment.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/script/dom/documentfragment.rs b/src/components/script/dom/documentfragment.rs index 2b3dc5afd80..bcf9c28e7c1 100644 --- a/src/components/script/dom/documentfragment.rs +++ b/src/components/script/dom/documentfragment.rs @@ -10,7 +10,7 @@ use dom::document::Document; use dom::element::Element; use dom::eventtarget::{EventTarget, NodeTargetTypeId}; use dom::htmlcollection::HTMLCollection; -use dom::node::{DocumentFragmentNodeTypeId, Node, window_from_node}; +use dom::node::{DocumentFragmentNodeTypeId, Node, NodeHelpers, window_from_node}; use dom::window::{Window, WindowMethods}; use servo_util::str::DOMString; @@ -52,12 +52,15 @@ pub trait DocumentFragmentMethods { } impl<'a> DocumentFragmentMethods for JSRef<'a, DocumentFragment> { + // http://dom.spec.whatwg.org/#dom-parentnode-children fn Children(&self) -> Temporary { let window = window_from_node(self).root(); HTMLCollection::children(&window.root_ref(), NodeCast::from_ref(self)) } + // http://dom.spec.whatwg.org/#dom-parentnode-queryselector fn QuerySelector(&self, selectors: DOMString) -> Fallible>> { - Ok(None) + let root: &JSRef = NodeCast::from_ref(self); + root.query_selector(selectors) } } From 3601aebaa281740277d6b4cc0868acab2f022dce Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Wed, 4 Jun 2014 13:30:53 -0700 Subject: [PATCH 6/8] Implement querySelector for Element --- src/components/script/dom/element.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index ee2245e8394..e16ef6bf6e4 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -700,13 +700,16 @@ impl<'a> ElementMethods for JSRef<'a, Element> { Ok(serialize(&mut NodeIterator::new(NodeCast::from_ref(self), true, false))) } + // http://dom.spec.whatwg.org/#dom-parentnode-children fn Children(&self) -> Temporary { let window = window_from_node(self).root(); HTMLCollection::children(&*window, NodeCast::from_ref(self)) } + // http://dom.spec.whatwg.org/#dom-parentnode-queryselector fn QuerySelector(&self, selectors: DOMString) -> Fallible>> { - Ok(None) + let root: &JSRef = NodeCast::from_ref(self); + root.query_selector(selectors) } // http://dom.spec.whatwg.org/#dom-childnode-remove From 75ddd2887bc12a4bdf1a60cfce0fc37207edc7f8 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Wed, 4 Jun 2014 13:31:08 -0700 Subject: [PATCH 7/8] Added content tests for querySelector --- .../test_parentNode_querySelector.html | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/test/content/test_parentNode_querySelector.html diff --git a/src/test/content/test_parentNode_querySelector.html b/src/test/content/test_parentNode_querySelector.html new file mode 100644 index 00000000000..556a45ba81a --- /dev/null +++ b/src/test/content/test_parentNode_querySelector.html @@ -0,0 +1,70 @@ + + + + + + + +
+
+
+

+ + From c89112dabf6d8dfbdb4270665bbd59cefcb7fda7 Mon Sep 17 00:00:00 2001 From: Bruno de Oliveira Abinader Date: Fri, 6 Jun 2014 13:08:44 -0700 Subject: [PATCH 8/8] Fixed traversal issues --- src/components/script/dom/node.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 5fa704810b2..0e9d046e957 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -566,11 +566,12 @@ impl<'a> NodeHelpers for JSRef<'a, Node> { Some(ref selectors) => { for selector in selectors.iter() { assert!(selector.pseudo_element.is_none()); - for elem in self.child_elements() { - let node: &JSRef = NodeCast::from_ref(&elem); + let root = self.ancestors().last().unwrap_or(self.clone()); + for node in root.traverse_preorder().filter(|node| node.is_element()) { let mut _shareable: bool = false; - if matches_compound_selector(selector.compound_selectors.deref(), node, &mut _shareable) { - return Ok(Some(Temporary::from_rooted(&elem))); + if matches_compound_selector(selector.compound_selectors.deref(), &node, &mut _shareable) { + let elem: &JSRef = ElementCast::to_ref(&node).unwrap(); + return Ok(Some(Temporary::from_rooted(elem))); } } }