style: Add a very simple invalidation-based querySelector.

MozReview-Commit-ID: Fimc8tz4OWX
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
Emilio Cobos Álvarez 2017-10-21 14:26:30 +02:00
parent 7f5536d5bc
commit 6d78e9ba54
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C

View file

@ -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;
/// <https://dom.spec.whatwg.org/#dom-element-matches>
pub fn element_matches<E>(
@ -57,3 +60,128 @@ where
return None;
}
/// The result of a querySelector call.
pub type QuerySelectorResult<E> = SmallVec<[E; 128]>;
/// The query kind we're doing (either only the first descendant that matches or
/// all of them).
pub enum QuerySelectorKind {
/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall>
All,
/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
First,
}
struct QuerySelectorProcessor<'a, E: TElement + 'a> {
kind: QuerySelectorKind,
results: &'a mut QuerySelectorResult<E>,
matching_context: MatchingContext<'a, E::Impl>,
selector_list: &'a SelectorList<E::Impl>,
}
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:
//
// <div id="a">
// <div id="b">
// <div id="c"></div>
// </div>
// </div>
//
// 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) {}
}
/// <https://dom.spec.whatwg.org/#dom-parentnode-queryselector>
pub fn query_selector<E: TElement>(
root: E::ConcreteNode,
selector_list: &SelectorList<E::Impl>,
results: &mut QuerySelectorResult<E>,
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();
}
}
}
}
}