use crate::dom::TElement;
use crate::selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl};
use crate::selector_parser::{Snapshot, SnapshotMap};
use crate::values::AtomIdent;
use crate::{CaseSensitivityExt, LocalName, Namespace, WeakAtom};
use dom::ElementState;
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
use selectors::bloom::BloomFilter;
use selectors::matching::{ElementSelectorFlags, MatchingContext};
use selectors::{Element, OpaqueElement};
use std::cell::Cell;
use std::fmt;
pub trait ElementSnapshot: Sized {
fn state(&self) -> Option<ElementState>;
fn has_attrs(&self) -> bool;
fn debug_list_attributes(&self) -> String {
String::new()
}
fn id_attr(&self) -> Option<&WeakAtom>;
fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool;
fn is_part(&self, name: &AtomIdent) -> bool;
fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent>;
fn each_class<F>(&self, _: F)
where
F: FnMut(&AtomIdent);
fn has_custom_states(&self) -> bool;
fn has_custom_state(&self, state: &AtomIdent) -> bool;
fn each_custom_state<F>(&self, callback: F)
where
F: FnMut(&AtomIdent);
fn lang_attr(&self) -> Option<AttrValue>;
}
#[derive(Clone)]
pub struct ElementWrapper<'a, E>
where
E: TElement,
{
element: E,
cached_snapshot: Cell<Option<&'a Snapshot>>,
snapshot_map: &'a SnapshotMap,
}
impl<'a, E> ElementWrapper<'a, E>
where
E: TElement,
{
pub fn new(el: E, snapshot_map: &'a SnapshotMap) -> Self {
ElementWrapper {
element: el,
cached_snapshot: Cell::new(None),
snapshot_map: snapshot_map,
}
}
pub fn snapshot(&self) -> Option<&'a Snapshot> {
if !self.element.has_snapshot() {
return None;
}
if let Some(s) = self.cached_snapshot.get() {
return Some(s);
}
let snapshot = self.snapshot_map.get(&self.element);
debug_assert!(snapshot.is_some(), "has_snapshot lied!");
self.cached_snapshot.set(snapshot);
snapshot
}
pub fn state_changes(&self) -> ElementState {
let snapshot = match self.snapshot() {
Some(s) => s,
None => return ElementState::empty(),
};
match snapshot.state() {
Some(state) => state ^ self.element.state(),
None => ElementState::empty(),
}
}
fn get_lang(&self) -> Option<AttrValue> {
let mut current = self.clone();
loop {
let lang = match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.lang_attr(),
_ => current.element.lang_attr(),
};
if lang.is_some() {
return lang;
}
current = current.parent_element()?;
}
}
}
impl<'a, E> fmt::Debug for ElementWrapper<'a, E>
where
E: TElement,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.element.fmt(f)
}
}
impl<'a, E> Element for ElementWrapper<'a, E>
where
E: TElement,
{
type Impl = SelectorImpl;
fn match_non_ts_pseudo_class(
&self,
pseudo_class: &NonTSPseudoClass,
context: &mut MatchingContext<Self::Impl>,
) -> bool {
match *pseudo_class {
NonTSPseudoClass::Link => {
return self.is_link() && context.visited_handling().matches_unvisited();
},
NonTSPseudoClass::Visited => {
return self.is_link() && context.visited_handling().matches_visited();
},
#[cfg(feature = "gecko")]
NonTSPseudoClass::MozTableBorderNonzero => {
if let Some(snapshot) = self.snapshot() {
if snapshot.has_other_pseudo_class_state() {
return snapshot.mIsTableBorderNonzero();
}
}
},
#[cfg(feature = "gecko")]
NonTSPseudoClass::MozSelectListBox => {
if let Some(snapshot) = self.snapshot() {
if snapshot.has_other_pseudo_class_state() {
return snapshot.mIsSelectListBox();
}
}
},
NonTSPseudoClass::Lang(ref lang_arg) => {
return self
.element
.match_element_lang(Some(self.get_lang()), lang_arg);
},
NonTSPseudoClass::CustomState(ref state) => return self.has_custom_state(&state.0),
_ => {},
}
let flag = pseudo_class.state_flag();
if flag.is_empty() {
return self
.element
.match_non_ts_pseudo_class(pseudo_class, context);
}
match self.snapshot().and_then(|s| s.state()) {
Some(snapshot_state) => snapshot_state.intersects(flag),
None => self
.element
.match_non_ts_pseudo_class(pseudo_class, context),
}
}
fn apply_selector_flags(&self, _flags: ElementSelectorFlags) {
debug_assert!(false, "Shouldn't need selector flags for invalidation");
}
fn match_pseudo_element(
&self,
pseudo_element: &PseudoElement,
context: &mut MatchingContext<Self::Impl>,
) -> bool {
self.element.match_pseudo_element(pseudo_element, context)
}
fn is_link(&self) -> bool {
match self.snapshot().and_then(|s| s.state()) {
Some(state) => state.intersects(ElementState::VISITED_OR_UNVISITED),
None => self.element.is_link(),
}
}
fn opaque(&self) -> OpaqueElement {
self.element.opaque()
}
fn parent_element(&self) -> Option<Self> {
let parent = self.element.parent_element()?;
Some(Self::new(parent, self.snapshot_map))
}
fn parent_node_is_shadow_root(&self) -> bool {
self.element.parent_node_is_shadow_root()
}
fn containing_shadow_host(&self) -> Option<Self> {
let host = self.element.containing_shadow_host()?;
Some(Self::new(host, self.snapshot_map))
}
fn prev_sibling_element(&self) -> Option<Self> {
let sibling = self.element.prev_sibling_element()?;
Some(Self::new(sibling, self.snapshot_map))
}
fn next_sibling_element(&self) -> Option<Self> {
let sibling = self.element.next_sibling_element()?;
Some(Self::new(sibling, self.snapshot_map))
}
fn first_element_child(&self) -> Option<Self> {
let child = self.element.first_element_child()?;
Some(Self::new(child, self.snapshot_map))
}
#[inline]
fn is_html_element_in_html_document(&self) -> bool {
self.element.is_html_element_in_html_document()
}
#[inline]
fn is_html_slot_element(&self) -> bool {
self.element.is_html_slot_element()
}
#[inline]
fn has_local_name(
&self,
local_name: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedLocalName,
) -> bool {
self.element.has_local_name(local_name)
}
#[inline]
fn has_namespace(
&self,
ns: &<Self::Impl as ::selectors::SelectorImpl>::BorrowedNamespaceUrl,
) -> bool {
self.element.has_namespace(ns)
}
#[inline]
fn is_same_type(&self, other: &Self) -> bool {
self.element.is_same_type(&other.element)
}
fn attr_matches(
&self,
ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName,
operation: &AttrSelectorOperation<&AttrValue>,
) -> bool {
match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => {
snapshot.attr_matches(ns, local_name, operation)
},
_ => self.element.attr_matches(ns, local_name, operation),
}
}
fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot
.id_attr()
.map_or(false, |atom| case_sensitivity.eq_atom(&atom, id)),
_ => self.element.has_id(id, case_sensitivity),
}
}
fn is_part(&self, name: &AtomIdent) -> bool {
match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name),
_ => self.element.is_part(name),
}
}
fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),
_ => self.element.imported_part(name),
}
}
fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),
_ => self.element.has_class(name, case_sensitivity),
}
}
fn has_custom_state(&self, state: &AtomIdent) -> bool {
match self.snapshot() {
Some(snapshot) if snapshot.has_custom_states() => snapshot.has_custom_state(state),
_ => self.element.has_custom_state(state),
}
}
fn is_empty(&self) -> bool {
self.element.is_empty()
}
fn is_root(&self) -> bool {
self.element.is_root()
}
fn is_pseudo_element(&self) -> bool {
self.element.is_pseudo_element()
}
fn pseudo_element_originating_element(&self) -> Option<Self> {
self.element
.pseudo_element_originating_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
}
fn assigned_slot(&self) -> Option<Self> {
self.element
.assigned_slot()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
}
fn add_element_unique_hashes(&self, _filter: &mut BloomFilter) -> bool {
false
}
}