use cssparser::ToCss;
use pochoir_template_engine::TemplateBlock;
use precomputed_hash::PrecomputedHash;
use selectors::{
matching,
parser::{self, ParseRelative, SelectorList, SelectorParseErrorKind},
Element, OpaqueElement,
};
use std::{
fmt,
hash::{DefaultHasher, Hash, Hasher},
};
use super::{TreeRef, TreeRefId};
use crate::Node;
pub fn build_selector_list(
selector: &str,
) -> Result<SelectorList<InnerSelector>, cssparser::ParseError<'_, SelectorParseErrorKind<'_>>> {
let mut input = cssparser::ParserInput::new(selector);
let mut parser = cssparser::Parser::new(&mut input);
SelectorList::parse(&InnerSelectorParser, &mut parser, ParseRelative::No)
}
impl Element for TreeRef<'_, '_> {
type Impl = InnerSelector;
fn opaque(&self) -> OpaqueElement {
OpaqueElement::new(&self.id)
}
fn parent_element(&self) -> Option<Self> {
let parent = self.parent();
if parent == *self {
None
} else {
Some(parent)
}
}
fn parent_node_is_shadow_root(&self) -> bool {
false
}
fn containing_shadow_host(&self) -> Option<Self> {
None
}
fn is_pseudo_element(&self) -> bool {
false
}
fn prev_sibling_element(&self) -> Option<Self> {
self.prev_sibling()
}
fn next_sibling_element(&self) -> Option<Self> {
self.next_sibling()
}
fn first_element_child(&self) -> Option<Self> {
self.children().next()
}
fn is_html_element_in_html_document(&self) -> bool {
true
}
fn has_local_name(
&self,
local_name: &<Self::Impl as selectors::SelectorImpl>::BorrowedLocalName,
) -> bool {
match self.data() {
Node::Element(ref name, _) => *name == local_name.inner,
_ => false,
}
}
fn has_namespace(
&self,
_ns: &<Self::Impl as selectors::SelectorImpl>::BorrowedNamespaceUrl,
) -> bool {
true
}
fn is_same_type(&self, other: &Self) -> bool {
if let Node::Element(ref name, _) = self.data() {
let name = name.clone();
if let Node::Element(ref other_name, _) = other.data() {
return name == *other_name;
}
}
false
}
fn attr_matches(
&self,
_ns: &selectors::attr::NamespaceConstraint<
&<Self::Impl as selectors::SelectorImpl>::NamespaceUrl,
>,
local_name: &<Self::Impl as selectors::SelectorImpl>::LocalName,
operation: &selectors::attr::AttrSelectorOperation<
&<Self::Impl as selectors::SelectorImpl>::AttrValue,
>,
) -> bool {
match self.data() {
Node::Element(_, ref attrs) => attrs.iter().any(|(key, val)| {
*local_name == LocalName::from(&***key)
&& operation.eval_str(
&val.iter()
.filter_map(|b| {
if let TemplateBlock::RawText(t) = &**b {
Some(t.to_string())
} else {
None
}
})
.collect::<String>(),
)
}),
_ => false,
}
}
fn match_non_ts_pseudo_class(
&self,
_pc: &<Self::Impl as selectors::SelectorImpl>::NonTSPseudoClass,
_context: &mut matching::MatchingContext<Self::Impl>,
) -> bool {
false
}
fn match_pseudo_element(
&self,
_pe: &<Self::Impl as selectors::SelectorImpl>::PseudoElement,
_context: &mut matching::MatchingContext<Self::Impl>,
) -> bool {
false
}
fn apply_selector_flags(&self, _flags: matching::ElementSelectorFlags) {}
fn is_link(&self) -> bool {
matches!(self.data(), Node::Element(ref el_name, _) if el_name == "link")
}
fn is_html_slot_element(&self) -> bool {
true
}
fn has_id(
&self,
id: &<Self::Impl as selectors::SelectorImpl>::Identifier,
case_sensitivity: selectors::attr::CaseSensitivity,
) -> bool {
if let Node::Element(_, _) = self.data() {
self.attr("id")
.unwrap_or_default()
.is_some_and(|v| case_sensitivity.eq(v.as_bytes(), id.inner.as_bytes()))
} else {
false
}
}
fn has_class(
&self,
name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
case_sensitivity: selectors::attr::CaseSensitivity,
) -> bool {
if let Node::Element(_, _) = self.data() {
self.attr("class")
.unwrap_or_default()
.is_some_and(|v| case_sensitivity.eq(v.as_bytes(), name.inner.as_bytes()))
} else {
false
}
}
fn imported_part(
&self,
_name: &<Self::Impl as selectors::SelectorImpl>::Identifier,
) -> Option<<Self::Impl as selectors::SelectorImpl>::Identifier> {
None
}
fn is_part(&self, _name: &<Self::Impl as selectors::SelectorImpl>::Identifier) -> bool {
false
}
fn is_empty(&self) -> bool {
!self
.children()
.any(|c| matches!(c.data(), Node::Element(_, _) | Node::TemplateBlock(_)))
}
fn is_root(&self) -> bool {
self.id == TreeRefId::Root
}
fn assigned_slot(&self) -> Option<Self> {
None
}
fn ignores_nth_child_selectors(&self) -> bool {
false
}
fn has_custom_state(&self, _name: &<Self::Impl as parser::SelectorImpl>::Identifier) -> bool {
false
}
fn add_element_unique_hashes(&self, _filter: &mut selectors::bloom::BloomFilter) -> bool {
false
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct LocalName {
inner: String,
}
impl AsRef<str> for LocalName {
fn as_ref(&self) -> &str {
self.inner.as_str()
}
}
impl From<&str> for LocalName {
fn from(v: &str) -> Self {
Self {
inner: v.to_string(),
}
}
}
impl PrecomputedHash for LocalName {
fn precomputed_hash(&self) -> u32 {
let mut hasher = DefaultHasher::new();
self.inner.hash(&mut hasher);
hasher.finish() as u32
}
}
impl ToCss for LocalName {
fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
dest.write_str(&self.inner)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Namespace;
impl PrecomputedHash for Namespace {
fn precomputed_hash(&self) -> u32 {
0
}
}
pub struct InnerSelectorParser;
impl<'i> parser::Parser<'i> for InnerSelectorParser {
type Impl = InnerSelector;
type Error = SelectorParseErrorKind<'i>;
}
#[derive(Debug, Clone)]
pub struct InnerSelector;
impl parser::SelectorImpl for InnerSelector {
type ExtraMatchingData<'a> = String;
type AttrValue = LocalName;
type Identifier = LocalName;
type LocalName = LocalName;
type NamespaceUrl = Namespace;
type NamespacePrefix = LocalName;
type BorrowedLocalName = LocalName;
type BorrowedNamespaceUrl = Namespace;
type NonTSPseudoClass = NonTSPseudoClass;
type PseudoElement = PseudoElement;
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct NonTSPseudoClass;
impl parser::NonTSPseudoClass for NonTSPseudoClass {
type Impl = InnerSelector;
fn is_active_or_hover(&self) -> bool {
false
}
fn is_user_action_state(&self) -> bool {
false
}
}
impl ToCss for NonTSPseudoClass {
fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
dest.write_str("")
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct PseudoElement;
impl parser::PseudoElement for PseudoElement {
type Impl = InnerSelector;
}
impl ToCss for PseudoElement {
fn to_css<W: fmt::Write>(&self, dest: &mut W) -> fmt::Result {
dest.write_str("")
}
}