use crate::dom::{Document, Node, NodeId};
use crate::style::errors::RuleParseErrorKind;
use accessibility_scraper::selector::CssLocalName;
use accessibility_scraper::selector::Simple;
use cssparser::ToCss;
use html5ever::Namespace;
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
use selectors::context::{MatchingContext, MatchingMode, QuirksMode};
use selectors::matching::{matches_selector, ElementSelectorFlags};
use std::fmt;
pub type SelectorList = selectors::SelectorList<Simple>;
pub type Selector = selectors::parser::Selector<Simple>;
pub fn matches(selector: &Selector, document: &Document, element: NodeId) -> bool {
matches_selector(
selector,
0,
None,
&NodeRef {
document,
node: element,
},
&mut MatchingContext::new(MatchingMode::Normal, None, None, QuirksMode::NoQuirks),
&mut |_, _| {},
)
}
pub struct Parser;
#[derive(Clone, PartialEq, Eq)]
pub enum PseudoElement {}
#[derive(Clone, PartialEq, Eq)]
pub enum PseudoClass {}
impl selectors::parser::NonTSPseudoClass for PseudoClass {
type Impl = Simple;
fn is_active_or_hover(&self) -> bool {
match *self {}
}
}
impl<'i> selectors::parser::Parser<'i> for Parser {
type Impl = Simple;
type Error = RuleParseErrorKind<'i>;
}
impl selectors::parser::PseudoElement for PseudoElement {
type Impl = Simple;
}
impl ToCss for PseudoElement {
fn to_css<W>(&self, _dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
match *self {}
}
}
impl ToCss for PseudoClass {
fn to_css<W>(&self, _dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
match *self {}
}
}
#[derive(Copy, Clone)]
struct NodeRef<'a> {
document: &'a Document,
node: NodeId,
}
impl<'a> std::fmt::Debug for NodeRef<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.node.fmt(f)
}
}
impl<'a> NodeRef<'a> {
fn node(self) -> &'a Node {
&self.document[self.node]
}
}
fn find_element<'a, F>(
document: &'a Document,
first: Option<NodeId>,
next: F,
) -> Option<NodeRef<'a>>
where
F: Fn(&Node) -> Option<NodeId>,
{
let mut node = first?;
loop {
if document[node].as_element().is_some() {
return Some(NodeRef { document, node });
}
node = next(&document[node])?
}
}
impl<'a> selectors::Element for NodeRef<'a> {
type Impl = Simple;
fn opaque(&self) -> selectors::OpaqueElement {
selectors::OpaqueElement::new::<Node>(self.node())
}
fn parent_element(&self) -> Option<Self> {
let parent = self.node().parent?;
self.document[parent].as_element()?;
Some(NodeRef {
document: self.document,
node: parent,
})
}
fn next_sibling_element(&self) -> Option<Self> {
find_element(self.document, self.node().next_sibling, |node| {
node.next_sibling
})
}
fn prev_sibling_element(&self) -> Option<Self> {
find_element(self.document, self.node().previous_sibling, |node| {
node.previous_sibling
})
}
fn is_html_element_in_html_document(&self) -> bool {
self.node().as_element().unwrap().name.ns == ns!(html) && self.node().in_html_document()
}
fn local_name(&self) -> &accessibility_scraper::selector::CssLocalName {
&self.node().as_element().unwrap().css_local_name
}
fn namespace(&self) -> &Namespace {
&self.node().as_element().unwrap().name.ns
}
fn is_html_slot_element(&self) -> bool {
false
}
fn parent_node_is_shadow_root(&self) -> bool {
false
}
fn containing_shadow_host(&self) -> Option<Self> {
None
}
fn attr_matches(
&self,
ns: &NamespaceConstraint<&Namespace>,
local_name: &CssLocalName,
operation: &AttrSelectorOperation<&accessibility_scraper::selector::CssString>,
) -> bool {
self.node().as_element().unwrap().attrs.iter().any(|attr| {
attr.name.local == *local_name.0
&& match *ns {
NamespaceConstraint::Any => true,
NamespaceConstraint::Specific(ns) => attr.name.ns == *ns,
}
&& operation.eval_str(&attr.value)
})
}
fn match_non_ts_pseudo_class<F>(
&self,
pseudo_class: &accessibility_scraper::selector::NonTSPseudoClass,
_context: &mut MatchingContext<Self::Impl>,
_flags_setter: &mut F,
) -> bool
where
F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class {}
}
fn match_pseudo_element(
&self,
pseudo_element: &accessibility_scraper::selector::PseudoElement,
_context: &mut MatchingContext<Self::Impl>,
) -> bool {
match *pseudo_element {}
}
fn is_link(&self) -> bool {
let element = self.node().as_element().unwrap();
element.name.ns == ns!(html)
&& matches!(
element.name.local,
local_name!("a") | local_name!("area") | local_name!("link")
)
&& element.get_attr(&local_name!("href")).is_some()
}
fn has_id(&self, id: &CssLocalName, case_sensitivity: CaseSensitivity) -> bool {
self.node()
.as_element()
.unwrap()
.get_attr(&local_name!("id"))
.map_or(false, |attr| {
case_sensitivity.eq(id.0.as_bytes(), attr.as_bytes())
})
}
fn has_class(&self, class: &CssLocalName, case_sensitivity: CaseSensitivity) -> bool {
self.node()
.as_element()
.unwrap()
.get_attr(&local_name!("class"))
.map_or(false, |attr| {
case_sensitivity.eq(class.0.as_bytes(), attr.as_bytes())
})
}
fn is_empty(&self) -> bool {
match self.node().first_child {
None => true,
Some(mut node) => loop {
if self.document[node].as_element().is_some() {
return false;
}
if let Some(text) = self.document[node].as_text() {
if !text.is_empty() {
return false;
}
}
match self.document[node].next_sibling {
None => return true,
Some(n) => node = n,
}
},
}
}
fn is_root(&self) -> bool {
self.parent_element().is_none()
}
}