#![deny(missing_docs)]
use crate::attr::{AttrIdentifier, AttrValue};
use crate::computed_value_flags::ComputedValueFlags;
use crate::dom::{OpaqueNode, TElement, TNode};
use crate::invalidation::element::document_state::InvalidationMatchingData;
use crate::invalidation::element::element_wrapper::ElementSnapshot;
use crate::properties::longhands::display::computed_value::T as Display;
use crate::properties::{ComputedValues, PropertyFlags};
use crate::selector_parser::AttrValue as SelectorAttrValue;
use crate::selector_parser::{PseudoElementCascadeType, SelectorParser};
use crate::values::{AtomIdent, AtomString};
use crate::{Atom, CaseSensitivityExt, LocalName, Namespace, Prefix};
use cssparser::{serialize_identifier, CowRcStr, Parser as CssParser, SourceLocation, ToCss};
use dom::{DocumentState, ElementState};
use fxhash::FxHashMap;
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
use selectors::parser::SelectorParseErrorKind;
use selectors::visitor::SelectorVisitor;
use std::fmt;
use std::mem;
use std::ops::{Deref, DerefMut};
use style_traits::{ParseError, StyleParseErrorKind};
#[derive(
Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize, ToShmem,
)]
#[allow(missing_docs)]
#[repr(usize)]
pub enum PseudoElement {
After = 0,
Before,
Selection,
DetailsSummary,
DetailsContent,
ServoAnonymousBox,
ServoAnonymousTable,
ServoAnonymousTableCell,
ServoAnonymousTableRow,
ServoLegacyText,
ServoLegacyInputText,
ServoLegacyTableWrapper,
ServoLegacyAnonymousTableWrapper,
ServoLegacyAnonymousTable,
ServoLegacyAnonymousBlock,
ServoLegacyInlineBlockWrapper,
ServoLegacyInlineAbsolute,
ServoTableGrid,
ServoTableWrapper,
}
pub const PSEUDO_COUNT: usize = PseudoElement::ServoTableWrapper as usize + 1;
impl ToCss for PseudoElement {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
use self::PseudoElement::*;
dest.write_str(match *self {
After => "::after",
Before => "::before",
Selection => "::selection",
DetailsSummary => "::-servo-details-summary",
DetailsContent => "::-servo-details-content",
ServoAnonymousBox => "::-servo-anonymous-box",
ServoAnonymousTable => "::-servo-anonymous-table",
ServoAnonymousTableCell => "::-servo-anonymous-table-cell",
ServoAnonymousTableRow => "::-servo-anonymous-table-row",
ServoLegacyText => "::-servo-legacy-text",
ServoLegacyInputText => "::-servo-legacy-input-text",
ServoLegacyTableWrapper => "::-servo-legacy-table-wrapper",
ServoLegacyAnonymousTableWrapper => "::-servo-legacy-anonymous-table-wrapper",
ServoLegacyAnonymousTable => "::-servo-legacy-anonymous-table",
ServoLegacyAnonymousBlock => "::-servo-legacy-anonymous-block",
ServoLegacyInlineBlockWrapper => "::-servo-legacy-inline-block-wrapper",
ServoLegacyInlineAbsolute => "::-servo-legacy-inline-absolute",
ServoTableGrid => "::-servo-table-grid",
ServoTableWrapper => "::-servo-table-wrapper",
})
}
}
impl ::selectors::parser::PseudoElement for PseudoElement {
type Impl = SelectorImpl;
}
pub const EAGER_PSEUDO_COUNT: usize = 3;
impl PseudoElement {
#[inline]
pub fn eager_index(&self) -> usize {
debug_assert!(self.is_eager());
self.clone() as usize
}
#[inline]
pub fn index(&self) -> usize {
self.clone() as usize
}
pub fn pseudo_none_array<T>() -> [Option<T>; PSEUDO_COUNT] {
Default::default()
}
#[inline]
pub fn from_eager_index(i: usize) -> Self {
assert!(i < EAGER_PSEUDO_COUNT);
let result: PseudoElement = unsafe { mem::transmute(i) };
debug_assert!(result.is_eager());
result
}
#[inline]
pub fn is_before_or_after(&self) -> bool {
self.is_before() || self.is_after()
}
#[inline]
pub fn is_unknown_webkit_pseudo_element(&self) -> bool {
false
}
#[inline]
pub fn is_marker(&self) -> bool {
false
}
#[inline]
pub fn is_selection(&self) -> bool {
*self == PseudoElement::Selection
}
#[inline]
pub fn is_before(&self) -> bool {
*self == PseudoElement::Before
}
#[inline]
pub fn is_after(&self) -> bool {
*self == PseudoElement::After
}
#[inline]
pub fn is_first_letter(&self) -> bool {
false
}
#[inline]
pub fn is_first_line(&self) -> bool {
false
}
#[inline]
pub fn is_color_swatch(&self) -> bool {
false
}
#[inline]
pub fn is_eager(&self) -> bool {
self.cascade_type() == PseudoElementCascadeType::Eager
}
#[inline]
pub fn is_lazy(&self) -> bool {
self.cascade_type() == PseudoElementCascadeType::Lazy
}
pub fn is_anon_box(&self) -> bool {
self.is_precomputed()
}
#[inline]
pub fn skip_item_display_fixup(&self) -> bool {
!self.is_before_or_after()
}
#[inline]
pub fn is_precomputed(&self) -> bool {
self.cascade_type() == PseudoElementCascadeType::Precomputed
}
#[inline]
pub fn cascade_type(&self) -> PseudoElementCascadeType {
match *self {
PseudoElement::After | PseudoElement::Before | PseudoElement::Selection => {
PseudoElementCascadeType::Eager
},
PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy,
PseudoElement::DetailsContent |
PseudoElement::ServoAnonymousBox |
PseudoElement::ServoAnonymousTable |
PseudoElement::ServoAnonymousTableCell |
PseudoElement::ServoAnonymousTableRow |
PseudoElement::ServoLegacyText |
PseudoElement::ServoLegacyInputText |
PseudoElement::ServoLegacyTableWrapper |
PseudoElement::ServoLegacyAnonymousTableWrapper |
PseudoElement::ServoLegacyAnonymousTable |
PseudoElement::ServoLegacyAnonymousBlock |
PseudoElement::ServoLegacyInlineBlockWrapper |
PseudoElement::ServoLegacyInlineAbsolute |
PseudoElement::ServoTableGrid |
PseudoElement::ServoTableWrapper => PseudoElementCascadeType::Precomputed,
}
}
pub fn canonical(&self) -> PseudoElement {
self.clone()
}
pub fn pseudo_info(&self) {
()
}
#[inline]
pub fn property_restriction(&self) -> Option<PropertyFlags> {
None
}
pub fn should_exist(&self, style: &ComputedValues) -> bool {
let display = style.get_box().clone_display();
if display == Display::None {
return false;
}
if self.is_before_or_after() && style.ineffective_content_property() {
return false;
}
true
}
}
pub type Lang = Box<str>;
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)]
pub struct CustomState(pub AtomIdent);
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)]
#[allow(missing_docs)]
pub enum NonTSPseudoClass {
Active,
AnyLink,
Autofill,
Checked,
CustomState(CustomState),
Default,
Defined,
Disabled,
Enabled,
Focus,
FocusWithin,
FocusVisible,
Fullscreen,
Hover,
InRange,
Indeterminate,
Invalid,
Lang(Lang),
Link,
Modal,
Optional,
OutOfRange,
PlaceholderShown,
PopoverOpen,
ReadOnly,
ReadWrite,
Required,
ServoNonZeroBorder,
Target,
UserInvalid,
UserValid,
Valid,
Visited,
}
impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
type Impl = SelectorImpl;
#[inline]
fn is_active_or_hover(&self) -> bool {
matches!(*self, NonTSPseudoClass::Active | NonTSPseudoClass::Hover)
}
#[inline]
fn is_user_action_state(&self) -> bool {
matches!(
*self,
NonTSPseudoClass::Active | NonTSPseudoClass::Hover | NonTSPseudoClass::Focus
)
}
fn visit<V>(&self, _: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>,
{
true
}
}
impl ToCss for NonTSPseudoClass {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
use self::NonTSPseudoClass::*;
if let Lang(ref lang) = *self {
dest.write_str(":lang(")?;
serialize_identifier(lang, dest)?;
return dest.write_char(')');
}
dest.write_str(match *self {
Self::Active => ":active",
Self::AnyLink => ":any-link",
Self::Autofill => ":autofill",
Self::Checked => ":checked",
Self::Default => ":default",
Self::Defined => ":defined",
Self::Disabled => ":disabled",
Self::Enabled => ":enabled",
Self::Focus => ":focus",
Self::FocusVisible => ":focus-visible",
Self::FocusWithin => ":focus-within",
Self::Fullscreen => ":fullscreen",
Self::Hover => ":hover",
Self::InRange => ":in-range",
Self::Indeterminate => ":indeterminate",
Self::Invalid => ":invalid",
Self::Link => ":link",
Self::Modal => ":modal",
Self::Optional => ":optional",
Self::OutOfRange => ":out-of-range",
Self::PlaceholderShown => ":placeholder-shown",
Self::PopoverOpen => ":popover-open",
Self::ReadOnly => ":read-only",
Self::ReadWrite => ":read-write",
Self::Required => ":required",
Self::ServoNonZeroBorder => ":-servo-nonzero-border",
Self::Target => ":target",
Self::UserInvalid => ":user-invalid",
Self::UserValid => ":user-valid",
Self::Valid => ":valid",
Self::Visited => ":visited",
Self::Lang(_) | Self::CustomState(_) => unreachable!(),
})
}
}
impl NonTSPseudoClass {
pub fn state_flag(&self) -> ElementState {
match *self {
Self::Active => ElementState::ACTIVE,
Self::AnyLink => ElementState::VISITED_OR_UNVISITED,
Self::Autofill => ElementState::AUTOFILL,
Self::Checked => ElementState::CHECKED,
Self::Default => ElementState::DEFAULT,
Self::Defined => ElementState::DEFINED,
Self::Disabled => ElementState::DISABLED,
Self::Enabled => ElementState::ENABLED,
Self::Focus => ElementState::FOCUS,
Self::FocusVisible => ElementState::FOCUSRING,
Self::FocusWithin => ElementState::FOCUS_WITHIN,
Self::Fullscreen => ElementState::FULLSCREEN,
Self::Hover => ElementState::HOVER,
Self::InRange => ElementState::INRANGE,
Self::Indeterminate => ElementState::INDETERMINATE,
Self::Invalid => ElementState::INVALID,
Self::Link => ElementState::UNVISITED,
Self::Modal => ElementState::MODAL,
Self::Optional => ElementState::OPTIONAL_,
Self::OutOfRange => ElementState::OUTOFRANGE,
Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN,
Self::PopoverOpen => ElementState::POPOVER_OPEN,
Self::ReadOnly => ElementState::READONLY,
Self::ReadWrite => ElementState::READWRITE,
Self::Required => ElementState::REQUIRED,
Self::Target => ElementState::URLTARGET,
Self::UserInvalid => ElementState::USER_INVALID,
Self::UserValid => ElementState::USER_VALID,
Self::Valid => ElementState::VALID,
Self::Visited => ElementState::VISITED,
Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => ElementState::empty(),
}
}
pub fn document_state_flag(&self) -> DocumentState {
DocumentState::empty()
}
pub fn needs_cache_revalidation(&self) -> bool {
self.state_flag().is_empty()
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
pub struct SelectorImpl;
#[derive(Debug, Default)]
pub struct ExtraMatchingData<'a> {
pub invalidation_data: InvalidationMatchingData,
pub cascade_input_flags: ComputedValueFlags,
pub originating_element_style: Option<&'a ComputedValues>,
}
impl ::selectors::SelectorImpl for SelectorImpl {
type PseudoElement = PseudoElement;
type NonTSPseudoClass = NonTSPseudoClass;
type ExtraMatchingData<'a> = ExtraMatchingData<'a>;
type AttrValue = AtomString;
type Identifier = AtomIdent;
type LocalName = LocalName;
type NamespacePrefix = Prefix;
type NamespaceUrl = Namespace;
type BorrowedLocalName = markup5ever::LocalName;
type BorrowedNamespaceUrl = markup5ever::Namespace;
}
impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
type Impl = SelectorImpl;
type Error = StyleParseErrorKind<'i>;
#[inline]
fn parse_nth_child_of(&self) -> bool {
false
}
#[inline]
fn parse_is_and_where(&self) -> bool {
true
}
#[inline]
fn parse_has(&self) -> bool {
false
}
#[inline]
fn parse_parent_selector(&self) -> bool {
false
}
#[inline]
fn allow_forgiving_selectors(&self) -> bool {
!self.for_supports_rule
}
fn parse_non_ts_pseudo_class(
&self,
location: SourceLocation,
name: CowRcStr<'i>,
) -> Result<NonTSPseudoClass, ParseError<'i>> {
let pseudo_class = match_ignore_ascii_case! { &name,
"active" => NonTSPseudoClass::Active,
"any-link" => NonTSPseudoClass::AnyLink,
"autofill" => NonTSPseudoClass::Autofill,
"checked" => NonTSPseudoClass::Checked,
"default" => NonTSPseudoClass::Default,
"defined" => NonTSPseudoClass::Defined,
"disabled" => NonTSPseudoClass::Disabled,
"enabled" => NonTSPseudoClass::Enabled,
"focus" => NonTSPseudoClass::Focus,
"focus-visible" => NonTSPseudoClass::FocusVisible,
"focus-within" => NonTSPseudoClass::FocusWithin,
"fullscreen" => NonTSPseudoClass::Fullscreen,
"hover" => NonTSPseudoClass::Hover,
"indeterminate" => NonTSPseudoClass::Indeterminate,
"invalid" => NonTSPseudoClass::Invalid,
"link" => NonTSPseudoClass::Link,
"optional" => NonTSPseudoClass::Optional,
"out-of-range" => NonTSPseudoClass::OutOfRange,
"placeholder-shown" => NonTSPseudoClass::PlaceholderShown,
"popover-open" => NonTSPseudoClass::PopoverOpen,
"read-only" => NonTSPseudoClass::ReadOnly,
"read-write" => NonTSPseudoClass::ReadWrite,
"required" => NonTSPseudoClass::Required,
"target" => NonTSPseudoClass::Target,
"user-invalid" => NonTSPseudoClass::UserInvalid,
"user-valid" => NonTSPseudoClass::UserValid,
"valid" => NonTSPseudoClass::Valid,
"visited" => NonTSPseudoClass::Visited,
"-servo-nonzero-border" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(
SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into())
))
}
NonTSPseudoClass::ServoNonZeroBorder
},
_ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
};
Ok(pseudo_class)
}
fn parse_non_ts_functional_pseudo_class<'t>(
&self,
name: CowRcStr<'i>,
parser: &mut CssParser<'i, 't>,
after_part: bool,
) -> Result<NonTSPseudoClass, ParseError<'i>> {
use self::NonTSPseudoClass::*;
let pseudo_class = match_ignore_ascii_case! { &name,
"lang" if !after_part => {
Lang(parser.expect_ident_or_string()?.as_ref().into())
},
_ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))),
};
Ok(pseudo_class)
}
fn parse_pseudo_element(
&self,
location: SourceLocation,
name: CowRcStr<'i>,
) -> Result<PseudoElement, ParseError<'i>> {
use self::PseudoElement::*;
let pseudo_element = match_ignore_ascii_case! { &name,
"before" => Before,
"after" => After,
"selection" => Selection,
"-servo-details-summary" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
DetailsSummary
},
"-servo-details-content" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
DetailsContent
},
"-servo-anonymous-box" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoAnonymousBox
},
"-servo-legacy-text" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoLegacyText
},
"-servo-legacy-input-text" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoLegacyInputText
},
"-servo-legacy-table-wrapper" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoLegacyTableWrapper
},
"-servo-legacy-anonymous-table-wrapper" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoLegacyAnonymousTableWrapper
},
"-servo-legacy-anonymous-table" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoLegacyAnonymousTable
},
"-servo-anonymous-table" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoAnonymousTable
},
"-servo-anonymous-table-row" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoAnonymousTableRow
},
"-servo-anonymous-table-cell" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoAnonymousTableCell
},
"-servo-legacy-anonymous-block" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoLegacyAnonymousBlock
},
"-servo-legacy-inline-block-wrapper" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoLegacyInlineBlockWrapper
},
"-servo-legacy-inline-absolute" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoLegacyInlineAbsolute
},
"-servo-table-grid" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoTableGrid
},
"-servo-table-wrapper" => {
if !self.in_user_agent_stylesheet() {
return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
}
ServoTableWrapper
},
_ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone())))
};
Ok(pseudo_element)
}
fn default_namespace(&self) -> Option<Namespace> {
self.namespaces.default.as_ref().map(|ns| ns.clone())
}
fn namespace_for_prefix(&self, prefix: &Prefix) -> Option<Namespace> {
self.namespaces.prefixes.get(prefix).cloned()
}
}
impl SelectorImpl {
#[inline]
pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
where
F: FnMut(PseudoElement),
{
for i in 0..EAGER_PSEUDO_COUNT {
fun(PseudoElement::from_eager_index(i));
}
}
}
#[derive(Debug)]
pub struct SnapshotMap(FxHashMap<OpaqueNode, ServoElementSnapshot>);
impl SnapshotMap {
pub fn new() -> Self {
SnapshotMap(FxHashMap::default())
}
pub fn get<T: TElement>(&self, el: &T) -> Option<&ServoElementSnapshot> {
self.0.get(&el.as_node().opaque())
}
}
impl Deref for SnapshotMap {
type Target = FxHashMap<OpaqueNode, ServoElementSnapshot>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SnapshotMap {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Debug, Default, MallocSizeOf)]
pub struct ServoElementSnapshot {
pub state: Option<ElementState>,
pub attrs: Option<Vec<(AttrIdentifier, AttrValue)>>,
pub changed_attrs: Vec<LocalName>,
pub class_changed: bool,
pub id_changed: bool,
pub other_attributes_changed: bool,
}
impl ServoElementSnapshot {
pub fn new() -> Self {
Self::default()
}
pub fn id_changed(&self) -> bool {
self.id_changed
}
pub fn class_changed(&self) -> bool {
self.class_changed
}
pub fn other_attr_changed(&self) -> bool {
self.other_attributes_changed
}
fn get_attr(&self, namespace: &Namespace, name: &LocalName) -> Option<&AttrValue> {
self.attrs
.as_ref()
.unwrap()
.iter()
.find(|&&(ref ident, _)| ident.local_name == *name && ident.namespace == *namespace)
.map(|&(_, ref v)| v)
}
#[inline]
pub fn each_attr_changed<F>(&self, mut callback: F)
where
F: FnMut(&LocalName),
{
for name in &self.changed_attrs {
callback(name)
}
}
fn any_attr_ignore_ns<F>(&self, name: &LocalName, mut f: F) -> bool
where
F: FnMut(&AttrValue) -> bool,
{
self.attrs
.as_ref()
.unwrap()
.iter()
.any(|&(ref ident, ref v)| ident.local_name == *name && f(v))
}
}
impl ElementSnapshot for ServoElementSnapshot {
fn state(&self) -> Option<ElementState> {
self.state.clone()
}
fn has_attrs(&self) -> bool {
self.attrs.is_some()
}
fn id_attr(&self) -> Option<&Atom> {
self.get_attr(&ns!(), &local_name!("id"))
.map(|v| v.as_atom())
}
fn is_part(&self, _name: &AtomIdent) -> bool {
false
}
fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
None
}
fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
self.get_attr(&ns!(), &local_name!("class"))
.map_or(false, |v| {
v.as_tokens()
.iter()
.any(|atom| case_sensitivity.eq_atom(atom, name))
})
}
fn each_class<F>(&self, mut callback: F)
where
F: FnMut(&AtomIdent),
{
if let Some(v) = self.get_attr(&ns!(), &local_name!("class")) {
for class in v.as_tokens() {
callback(AtomIdent::cast(class));
}
}
}
fn lang_attr(&self) -> Option<SelectorAttrValue> {
self.get_attr(&ns!(xml), &local_name!("lang"))
.or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
.map(|v| SelectorAttrValue::from(v as &str))
}
#[inline]
fn has_custom_states(&self) -> bool {
false
}
#[inline]
fn has_custom_state(&self, _state: &AtomIdent) -> bool {
false
}
#[inline]
fn each_custom_state<F>(&self, mut _callback: F)
where
F: FnMut(&AtomIdent),
{
}
}
impl ServoElementSnapshot {
pub fn attr_matches(
&self,
ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName,
operation: &AttrSelectorOperation<&AtomString>,
) -> bool {
match *ns {
NamespaceConstraint::Specific(ref ns) => self
.get_attr(ns, local_name)
.map_or(false, |value| value.eval_selector(operation)),
NamespaceConstraint::Any => {
self.any_attr_ignore_ns(local_name, |value| value.eval_selector(operation))
},
}
}
}
pub fn extended_filtering(tag: &str, range: &str) -> bool {
range.split(',').any(|lang_range| {
let mut range_subtags = lang_range.split('\x2d');
let mut tag_subtags = tag.split('\x2d');
if let (Some(range_subtag), Some(tag_subtag)) = (range_subtags.next(), tag_subtags.next()) {
if !(range_subtag.eq_ignore_ascii_case(tag_subtag) ||
range_subtag.eq_ignore_ascii_case("*"))
{
return false;
}
}
let mut current_tag_subtag = tag_subtags.next();
for range_subtag in range_subtags {
if range_subtag == "*" {
continue;
}
match current_tag_subtag.clone() {
Some(tag_subtag) => {
if range_subtag.eq_ignore_ascii_case(tag_subtag) {
current_tag_subtag = tag_subtags.next();
continue;
}
if tag_subtag.len() == 1 {
return false;
}
current_tag_subtag = tag_subtags.next();
if current_tag_subtag.is_none() {
return false;
}
},
None => {
return false;
},
}
}
true
})
}