diff --git a/components/style/dom_apis.rs b/components/style/dom_apis.rs index b050c181cfa..64bb8d31a12 100644 --- a/components/style/dom_apis.rs +++ b/components/style/dom_apis.rs @@ -6,8 +6,11 @@ //! and Gecko. use context::QuirksMode; +use dom::{TElement, TNode}; +use invalidation::element::invalidator::{Invalidation, InvalidationProcessor, InvalidationVector}; use selectors::{Element, NthIndexCache, SelectorList}; use selectors::matching::{self, MatchingContext, MatchingMode}; +use smallvec::SmallVec; /// pub fn element_matches( @@ -57,3 +60,128 @@ where return None; } + +/// The result of a querySelector call. +pub type QuerySelectorResult = SmallVec<[E; 128]>; + +/// The query kind we're doing (either only the first descendant that matches or +/// all of them). +pub enum QuerySelectorKind { + /// + All, + /// + First, +} + +struct QuerySelectorProcessor<'a, E: TElement + 'a> { + kind: QuerySelectorKind, + results: &'a mut QuerySelectorResult, + matching_context: MatchingContext<'a, E::Impl>, + selector_list: &'a SelectorList, +} + +impl<'a, E> InvalidationProcessor<'a, E> for QuerySelectorProcessor<'a, E> +where + E: TElement + 'a, +{ + fn collect_invalidations( + &mut self, + _element: E, + self_invalidations: &mut InvalidationVector<'a>, + descendant_invalidations: &mut InvalidationVector<'a>, + _sibling_invalidations: &mut InvalidationVector<'a>, + ) -> bool { + // FIXME(emilio): If the element is not a root element, and + // selector_list has any descendant combinator, we need to do extra work + // in order to handle properly things like: + // + //
+ //
+ //
+ //
+ //
+ // + // b.querySelector('#a div'); // Should return "c". + // + let target_vector = + if self.matching_context.scope_element.is_some() { + descendant_invalidations + } else { + self_invalidations + }; + + for selector in self.selector_list.0.iter() { + target_vector.push(Invalidation::new(selector, 0)) + } + + false + } + + fn matching_context(&mut self) -> &mut MatchingContext<'a, E::Impl> { + &mut self.matching_context + } + + fn should_process_descendants(&mut self, _: E) -> bool { + match self.kind { + QuerySelectorKind::All => true, + QuerySelectorKind::First => self.results.is_empty(), + } + } + + fn invalidated_self(&mut self, e: E) { + self.results.push(e); + } + + fn recursion_limit_exceeded(&mut self, _e: E) {} + fn invalidated_descendants(&mut self, _e: E, _child: E) {} +} + +/// +pub fn query_selector( + root: E::ConcreteNode, + selector_list: &SelectorList, + results: &mut QuerySelectorResult, + kind: QuerySelectorKind, + quirks_mode: QuirksMode, +) { + use invalidation::element::invalidator::TreeStyleInvalidator; + + let mut nth_index_cache = NthIndexCache::default(); + let mut matching_context = MatchingContext::new( + MatchingMode::Normal, + None, + Some(&mut nth_index_cache), + quirks_mode, + ); + + let root_element = root.as_element(); + matching_context.scope_element = root_element.map(|e| e.opaque()); + + let mut processor = QuerySelectorProcessor { + kind, + results, + matching_context, + selector_list, + }; + + match root_element { + Some(e) => { + TreeStyleInvalidator::new( + e, + /* stack_limit_checker = */ None, + &mut processor, + ).invalidate(); + } + None => { + for node in root.dom_children() { + if let Some(e) = node.as_element() { + TreeStyleInvalidator::new( + e, + /* stack_limit_checker = */ None, + &mut processor, + ).invalidate(); + } + } + } + } +}