diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/script/dom/document.rs | 11 | ||||
-rw-r--r-- | src/components/script/dom/documentfragment.rs | 12 | ||||
-rw-r--r-- | src/components/script/dom/element.rs | 48 | ||||
-rw-r--r-- | src/components/script/dom/node.rs | 80 | ||||
-rw-r--r-- | src/components/script/dom/webidls/ParentNode.webidl | 6 | ||||
-rw-r--r-- | src/components/script/script.rs | 1 | ||||
-rw-r--r-- | src/components/style/selector_matching.rs | 2 | ||||
-rw-r--r-- | src/components/style/style.rs | 3 | ||||
-rw-r--r-- | src/test/content/test_parentNode_querySelector.html | 70 |
9 files changed, 226 insertions, 7 deletions
diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index a00a12c26de..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; @@ -328,6 +329,7 @@ pub trait DocumentMethods { fn Applets(&self) -> Temporary<HTMLCollection>; fn Location(&self) -> Temporary<Location>; fn Children(&self) -> Temporary<HTMLCollection>; + fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>>; fn GetOnload(&self) -> Option<EventHandlerNonNull>; fn SetOnload(&mut self, listener: Option<EventHandlerNonNull>); } @@ -805,11 +807,18 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { window.Location() } + // http://dom.spec.whatwg.org/#dom-parentnode-children fn Children(&self) -> Temporary<HTMLCollection> { 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<Option<Temporary<Element>>> { + let root: &JSRef<Node> = NodeCast::from_ref(self); + root.query_selector(selectors) + } + fn GetOnload(&self) -> Option<EventHandlerNonNull> { let eventtarget: &JSRef<EventTarget> = 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..bcf9c28e7c1 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::node::{DocumentFragmentNodeTypeId, Node, NodeHelpers, window_from_node}; use dom::window::{Window, WindowMethods}; +use servo_util::str::DOMString; #[deriving(Encodable)] pub struct DocumentFragment { @@ -46,11 +48,19 @@ impl DocumentFragment { pub trait DocumentFragmentMethods { fn Children(&self) -> Temporary<HTMLCollection>; + fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>>; } impl<'a> DocumentFragmentMethods for JSRef<'a, DocumentFragment> { + // http://dom.spec.whatwg.org/#dom-parentnode-children fn Children(&self) -> Temporary<HTMLCollection> { 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<Option<Temporary<Element>>> { + let root: &JSRef<Node> = NodeCast::from_ref(self); + root.query_selector(selectors) + } } diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs index 5fce6ba6903..3189360ccf5 100644 --- a/src/components/script/dom/element.rs +++ b/src/components/script/dom/element.rs @@ -196,6 +196,8 @@ impl LayoutElementHelpers for JS<Element> { 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<Node> = 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 { @@ -418,6 +428,7 @@ pub trait ElementMethods { fn GetInnerHTML(&self) -> Fallible<DOMString>; fn GetOuterHTML(&self) -> Fallible<DOMString>; fn Children(&self) -> Temporary<HTMLCollection>; + fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>>; fn Remove(&self); } @@ -688,11 +699,18 @@ 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<HTMLCollection> { 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<Option<Temporary<Element>>> { + let root: &JSRef<Node> = NodeCast::from_ref(self); + root.query_selector(selectors) + } + // http://dom.spec.whatwg.org/#dom-childnode-remove fn Remove(&self) { let node: &JSRef<Node> = NodeCast::from_ref(self); @@ -799,3 +817,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<Node> = 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<Node> = NodeCast::from_ref(self); + node.get_hover_state() + } +} diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index 45c85ea793e..261518752cb 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}; @@ -14,14 +15,15 @@ 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; 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}; @@ -34,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; @@ -42,6 +45,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; @@ -384,6 +388,8 @@ pub trait NodeHelpers { fn get_bounding_content_box(&self) -> Rect<Au>; fn get_content_boxes(&self) -> Vec<Rect<Au>>; + fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>>; + fn remove_self(&self); } @@ -544,6 +550,31 @@ impl<'a> NodeHelpers for JSRef<'a, Node> { rects } + // http://dom.spec.whatwg.org/#dom-parentnode-queryselector + fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Temporary<Element>>> { + // 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()); + 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) { + let elem: &JSRef<Element> = ElementCast::to_ref(&node).unwrap(); + return Ok(Some(Temporary::from_rooted(elem))); + } + } + } + } + } + Ok(None) + } + fn ancestors(&self) -> AncestorIterator { AncestorIterator { current: self.parent_node.get().map(|node| (*node.root()).clone()), @@ -1900,3 +1931,46 @@ impl<'a> VirtualMethods for JSRef<'a, Node> { Some(eventtarget as &VirtualMethods:) } } + +impl<'a> style::TNode<JSRef<'a, Element>> for JSRef<'a, Node> { + fn parent_node(&self) -> Option<JSRef<'a, Node>> { + (self as &NodeHelpers).parent_node().map(|node| *node.root()) + } + fn prev_sibling(&self) -> Option<JSRef<'a, Node>> { + (self as &NodeHelpers).prev_sibling().map(|node| *node.root()) + } + fn next_sibling(&self) -> Option<JSRef<'a, Node>> { + (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, + } + } +} 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); }; 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<E:TElement, +pub fn matches_compound_selector<E:TElement, N:TNode<E>>( 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}; 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 @@ +<!DOCTYPE html> +<html> + <head> + <script src="harness.js"></script> + <script> + { // document.querySelector + let div = document.getElementById("foo"); + is(document.querySelector("#foo"), div); + + div = document.getElementById("foo\\bar"); + is(document.querySelector("#foo\\\\bar"), div); + + div = document.getElementById("foo:bar"); + is(document.querySelector("#foo\\:bar"), div); + + div = document.getElementById("bar"); + is(document.querySelector("div.myClass"), div); + is(document.querySelector("div:nth-of-type(4)"), div); + } + { // element.querySelector + let body = document.body; + let div = document.getElementById("foo"); + is(body.querySelector("#foo"), div); + + div = document.getElementById("foo\\bar"); + is(body.querySelector("#foo\\\\bar"), div); + + div = document.getElementById("foo:bar"); + is(body.querySelector("#foo\\:bar"), div); + + div = document.getElementById("bar"); + is(body.querySelector("div.myClass"), div); + is(body.querySelector("div:nth-of-type(4)"), div); + } + + { // docfrag.querySelector + let docfrag = document.createDocumentFragment(); + + let div = document.createElement("div"); + div.id = "foo"; + div.className = "myClass"; + + let child = document.createElement("div"); + div.appendChild(child); + docfrag.appendChild(div); + + let p = document.createElement("p"); + p.id = "bar"; + p.className = "myClass"; + docfrag.appendChild(p); + + is(docfrag.querySelector("#foo"), div); + is(docfrag.querySelector("div.myClass"), div); + + is(docfrag.querySelector("#bar"), p); + is(docfrag.querySelector("p.myClass"), p); + + is(docfrag.querySelector(".myClass"), div); + is(docfrag.querySelector("div > div"), child); + } + finish(); + </script> + </head> + <body> + <div id="foo"></div> + <div id="foo\bar"></div> + <div id="foo:bar"></div> + <div id="bar" class="myClass"></p> + </body> +</html> |